Relay docs
Implementation

Embed

Render the helpdesk, feature requests, changelog, and campaign banners inside your own product with one script.

The embed puts Relay's customer-facing surfaces on your own pages — a "Support" or "What's new" page inside your app, for example — without an iframe. One script, four optional mount points, styles isolated in Shadow DOM and themeable with a handful of variables. It works on any host page; no framework required.

Building a Shopify app? Use the Shopify embed instead — same mount points and identity, Polaris look inside the Shopify admin: Shopify → Embed. The product page shows the right snippet for each product.

Minimum requirements

  • A product in Settings → Products with Enable Embed clicked (this generates the embed token).
  • For the helpdesk and for creating/voting/replying on feature requests: a Relay Secret and a server-signed identity token. Changelog and campaign banners work without one.

Install

Copy the snippet from the product page, or:

<script src="https://app.superrelay.ai/embed/relay-embed.js" data-token="YOUR_EMBED_TOKEN"></script>

<div id="relay-helpdesk"></div>
<div id="relay-feature-requests"></div>
<div id="relay-changelog"></div>
<div id="relay-campaign-banner"></div>

Keep only the containers you want. Containers added later (client-side routing) are picked up automatically. To mount a surface more than once, use data-relay-embed="helpdesk" (or feature-requests, changelog, campaign-banner) instead of an id.

Optional: custom API host

<script
  src="https://app.superrelay.ai/embed/relay-embed.js"
  data-token="YOUR_EMBED_TOKEN"
  data-api="https://YOUR_API_HOST"
></script>

Signed identity

The helpdesk needs to know who is signed in; feature-request writes do too. Your server mints a short-lived token with the product's Relay Secret and the page exposes it before the embed script:

<script>
  window.RelayEmbed = { identity: { token: "eyJhbGciOiJIUzI1NiIs…" } }
</script>
<script src="https://app.superrelay.ai/embed/relay-embed.js" data-token="YOUR_EMBED_TOKEN"></script>

Claims, examples for Node/Next.js/PHP, and how organizations work: Identity verification. window.SuperRelayEmbed is accepted as an alias of window.RelayEmbed.

What each surface needs:

SurfaceWithout identityWith identity
#relay-changelogFullFull
#relay-campaign-bannerProduct-wide banners; dismissals remembered in this browserSegment targeting; dismissals remembered per contact
#relay-feature-requestsRead-only boardCreate, vote, reply
#relay-helpdeskLocked ("needs a signed identity")The customer's tickets — and their organization's, if the product shares tickets across an organization

Campaign banner buttons must point at a URL (http, https, mailto, tel, or a path on your site). The Run code action type is ignored by this embed — it would run workspace-authored JavaScript in your page's origin — and changelog content is sanitized server-side before it reaches the browser.

Theming

The embed renders inside Shadow DOM, so your CSS never bleeds in and ours never bleeds out.

The quickest way to theme it is the customizer in Settings → Products → your product → Embed → Appearance: pick a color mode, accent, corners, density, typeface and surface, watch the real embed update in the live preview, and copy the generated snippet. Everything it produces is just the attributes below, so you can also write them by hand.

Tune it with a few variables — on the script tag:

<script
  src="https://app.superrelay.ai/embed/relay-embed.js"
  data-token="YOUR_EMBED_TOKEN"
  data-theme="auto"
  data-theme-primary="#7c3aed"
  data-theme-font="Inter, system-ui, sans-serif"
  data-theme-radius="8px"
  data-theme-surface="plain"
></script>

or from the page (wins over the attributes):

<script>
  window.RelayEmbed = {
    identity: { token: "…" },
    theme: {
      mode: "dark",
      primary: "#7c3aed",
      font: "Inter, sans-serif",
      radius: "8px",
      spacing: "8px",
      surface: "card",
    },
  }
</script>
OptionValuesDefault
mode / data-themelight, dark, auto (follows the OS)light
primaryany CSS color — buttons, links, active states#4f46e5
fontCSS font-family stacksystem font
radiusCSS length — card and control corners14px
spacingCSS length — base unit, so it doubles as a density control8px
surfacecard (Relay draws its own cards) or plain (flush, for a page that already provides the card)card

Changed the theme after load? Call SuperRelayEmbedSDK.applyTheme().

font: "inherit" (the customizer's Page font) makes the embed use whatever font your page already sets.

For finer control, set the CSS variables directly on a mount element: --sr-primary, --sr-bg, --sr-bg-subtle, --sr-fg, --sr-fg-muted, --sr-fg-subtle, --sr-radius, --sr-space. Borders, rings and shadows are derived from the foreground and accent with color-mix(), so overriding those few tokens re-themes everything consistently in both light and dark.

Accessibility

Native buttons and form controls, visible focus rings, aria-pressed on vote buttons, role="dialog" modals that trap Tab, close on Escape and return focus to the opener, role="status"/role="alert" on loading and error notices. Motion is disabled under prefers-reduced-motion. Everything is keyboard-operable.

Troubleshooting

  • Nothing renders — the script needs data-token, and at least one mount container must exist (or appear later). Open the console: the embed logs [Relay Embed] … with the reason.
  • Helpdesk says it needs a signed identitywindow.RelayEmbed.identity was missing or set after the script ran; set it before the script tag. Identity verification failed: … carries the server's reason (expired token, wrong secret).
  • Feature requests are read-only — same cause; the board loads without an identity, writes need one.
  • Wrong look in a dark app — set data-theme="dark" or "auto".

On this page