← All posts

From n8n workflow to a hosted dashboard in under a minute

How to take any n8n workflow output — sales data, form submissions, scraped pages — and turn it into a shareable dashboard link using a single HTTP Request node.

By Eduard Maghakyan5 min read.md

n8n is great at moving data around. You hook up a Schedule Trigger, pull from an API, filter, transform, and push the result somewhere — Slack, Google Sheets, Airtable, a webhook.

But there's a gap at the end. The data has been shaped into something useful, and now you need to show it to a human. Usually that's where the workflow gets ugly:

  • Email a CSV attachment. (Nobody opens CSVs.)
  • Post a giant text table into Slack. (Unreadable on mobile.)
  • Write to a Google Sheet and share the link. (You've just asked the recipient to care about a spreadsheet.)
  • Build a Retool/Appsmith page, then paste its URL in. (Two tools and an extra subscription.)

None of these feel great, and the reason is that none of them actually render the data — they just relocate it.

The missing option: POST to a rendering API

Here's a different shape. Take whatever n8n produced, POST it to genui.sh, get back a URL that hosts a rendered dashboard, and send that link to Slack.

The whole change is one HTTP Request node.

Example: a weekly sales report

Imagine you've got an n8n workflow that runs every Monday:

  1. Schedule Trigger — weekly, Monday 9am
  2. Airtable node — fetch last week's orders
  3. Code node — aggregate into totals by product, with a trend vs. the previous week
  4. ??? — make it look like something

Today the ??? is the hard part. Here's what it looks like with a rendering step added:

  1. HTTP Request — POST the aggregated data to https://api.genui.sh/v1/artifacts/share
  2. Slack — post the returned URL to #sales

The HTTP Request node config:

  • Method: POST
  • URL: https://api.genui.sh/v1/artifacts/share
  • Authentication: Header Auth → Authorization: Bearer {{$env.GENUI_API_KEY}}
  • Body Content Type: JSON
  • Body:
{
  "template": "@std/dynamic",
  "title": "Weekly Sales — {{$now.format('MMM D')}}",
  "content": {
    "root": "page",
    "elements": {
      "page": {
        "type": "Stack",
        "props": { "direction": "vertical", "gap": "md" },
        "children": ["metrics", "chartCard", "tableCard"]
      },
      "metrics": {
        "type": "Grid",
        "props": { "columns": 2, "gap": "md" },
        "children": ["revenue", "orders"]
      },
      "revenue": {
        "type": "Metric",
        "props": {
          "label": "Revenue",
          "value": "${{$('Code').item.json.revenue}}",
          "trend": "up",
          "trendValue": "{{$('Code').item.json.trend}}",
          "description": "This week",
          "icon": "DollarSign"
        }
      },
      "orders": {
        "type": "Metric",
        "props": {
          "label": "Orders",
          "value": "{{$('Code').item.json.orderCount}}",
          "trend": "up",
          "trendValue": "+8%",
          "description": "This week",
          "icon": "ShoppingCart"
        }
      },
      "chartCard": {
        "type": "Card",
        "props": { "title": "Daily revenue" },
        "children": ["chart"]
      },
      "chart": {
        "type": "Chart",
        "props": {
          "type": "bar",
          "data": "={{ $('Code').item.json.daily }}",
          "config": {
            "categoryKey": "day",
            "series": [{ "key": "revenue", "label": "Revenue" }]
          }
        }
      },
      "tableCard": {
        "type": "Card",
        "props": { "title": "Top products" },
        "children": ["table"]
      },
      "table": {
        "type": "Table",
        "props": {
          "columns": [
            { "key": "product", "header": "Product" },
            { "key": "sales", "header": "Sales", "align": "right" },
            { "key": "revenue", "header": "Revenue", "align": "right" }
          ],
          "rows": "={{ $('Code').item.json.topProducts }}"
        }
      }
    }
  }
}

The response is a single URL:

{
  "id": "art_xyz789",
  "url": "https://genui.sh/a/art_xyz789?token=..."
}

Pass that to the Slack node as the message, and your Monday-morning message to #sales is a clean link. Anyone who clicks it lands on a rendered dashboard with metric cards, a bar chart, and a table — no spreadsheet, no login wall.

Why this beats the alternatives

  • One node, no new tool. You don't add a dashboarding product to your stack. You don't train anyone on a new UI. You don't pay for another seat.
  • It works the same from Make.com, Zapier, cron, or a Bash script. The endpoint is the same HTTP POST. Nothing about it is n8n-specific.
  • The dashboard is a hosted page, not an attachment. Slack previews it nicely, mobile renders cleanly, and anyone you share the link with can open it — no login.
  • Expiry is controllable. On the Free tier links expire after 7 days (good for weekly reports). Starter goes to 30 days. Pro links never expire.

Other patterns this unlocks

Once you've wired an HTTP Request node to genui.sh, a bunch of workflows that used to require a real frontend collapse into a single step:

  • Form submission → summary card in Slack. n8n form trigger → POST a Card with the submission details → post link to the team channel.
  • Scraped price comparison → weekly PDF digest. HTTP Request → transform → POST with template: "pdf" → email.
  • CRM status change → customer-facing update. Webhook from HubSpot → POST a @std/dynamic tree with status timeline → send the URL to the customer.
  • AI agent tool result → shareable dashboard. Let your agent return genui.sh URLs to users instead of pasting JSON back into the chat.

Pricing, for the curious

50 artifacts per month is free (no card). If your workflow runs daily that's enough to cover it plus ad-hoc testing. If it runs hourly, Starter at $7/mo gets you 500. Pro at $19 covers 3,000 artifacts per month — a workflow firing every 15 minutes all day.

There's no per-seat pricing, no per-environment fee, and no "contact sales" for a single-custom-domain setup. One endpoint, one price.

Try it from n8n

Grab a free key at genui.sh, add an HTTP Request node to your next workflow, and ping me at support@genui.sh if you want help shaping the @std/dynamic body for your specific data. I'll happily write it with you.