Skip to content
Joe BI Platform
Esc
navigateopen⌘Jpreview
On this page

Merchant-facing embeds and links

How to embed shop-locked dashboards and create stable links without exposing Metabase filters.

Merchant-facing embeds and links

Merchants never need a Metabase account. The data plane locks each dashboard to a shop UUID, mints a short-lived Metabase token, and sends the browser to that shop-specific view. Shop names never belong in embed URLs or query parameters.

Use case URL Authentication Lifetime
Product iframe GET /v1/embed/reports/shop_sales?shopId=<uuid> Data-plane bearer token with shop access Returned Metabase URL lasts 10 minutes; refetch it
Internal share or bookmark GET /e/shop_sales/<uuid> Valid session/bearer with shop access at click time The Joe URL never expires
Merchant email, Slack, or iframe without a session GET /e/shop_sales/<uuid>?exp=<unix>&sig=<hmac> Signature in the URL 7, 30, or 90 days; 90 days by default

Both /e/ forms mint a fresh 10-minute Metabase token when clicked and respond with a 302 redirect. A bookmarked /e/ URL therefore does not inherit the Metabase token’s expiry.

Copy-paste examples

Use canonical shop UUIDs only:

https://data-plane.joe.coffee/e/shop_sales/4a2729c3-5be1-4d1e-9b0d-985ac242f0e3

The equivalent query form is supported for clients that cannot put the shop in the path:

https://data-plane.joe.coffee/e/shop_sales?shop=4a2729c3-5be1-4d1e-9b0d-985ac242f0e3

Do not hand-write sig values. Joe staff should open the reports admin, search for the shop by name, choose the report and 7/30/90-day expiry, then copy the generated no-login link. The page also shows the plain authenticated link for internal sharing.

For an application iframe, request a fresh URL from the authenticated API:

GET /v1/embed/reports/shop_sales?shopId=4a2729c3-5be1-4d1e-9b0d-985ac242f0e3
Authorization: Bearer <short-lived-shop-scoped-token>
{
  "ok": true,
  "data": {
    "iframeUrl": "https://joe-metabase.fly.dev/embed/dashboard/<short-lived-token>#bordered=false&titled=false",
    "expiresAt": "2026-07-20T21:10:00.000Z"
  },
  "meta": {}
}

Set iframe.src to data.iframeUrl and refetch before expiresAt. Never append a shop name, shop_id, or a replacement filter to the Metabase URL.

Security model

  • The authenticated path calls the same shop-access assertion as the embed API. A control-plane token can only open a UUID listed in its shopIds claim.
  • No-login links use HMAC-SHA256 with the server-only Metabase embed secret and a domain-separated link:v1: payload. The signature covers report key, shop UUID, and optional Unix expiry.
  • The Worker checks the report registry and the shops table before minting. Unknown reports, unknown shops, invalid signatures, and expired signatures receive a generic branded 404 or 403 page without configuration details.
  • The URL signature authorizes the stable Joe link, not Metabase directly. The Metabase JWT remains short-lived and keeps shop_id locked.
  • Treat a no-login URL like a password until it expires. Forwarding it forwards its access. Generate a shorter replacement when that is the safer choice.

Scheduled merchant-report emails use 90-day signed /e/ links. Internal report emails continue to link directly to authenticated Metabase dashboards.

Adding an embeddable report

  1. Build the dashboard in Metabase with a mandatory shop_id filter and merchant-safe labels.
  2. Enable static embedding and mark shop_id as a locked parameter.
  3. Add the report key, human label, dashboard binding, and locked-param function to src/embed/report-registry.ts.
  4. Add the optional dashboard ID binding to src/env.ts and document deployment configuration.
  5. Verify the authenticated API, both /e/ forms, the admin dropdown, and generic failure pages.

See Merchant Manager integration for an application migration recipe.

Was this page helpful?