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:
| Surface | Without identity | With identity |
|---|---|---|
#relay-changelog | Full | Full |
#relay-campaign-banner | Product-wide banners; dismissals remembered in this browser | Segment targeting; dismissals remembered per contact |
#relay-feature-requests | Read-only board | Create, vote, reply |
#relay-helpdesk | Locked ("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>| Option | Values | Default |
|---|---|---|
mode / data-theme | light, dark, auto (follows the OS) | light |
primary | any CSS color — buttons, links, active states | #4f46e5 |
font | CSS font-family stack | system font |
radius | CSS length — card and control corners | 14px |
spacing | CSS length — base unit, so it doubles as a density control | 8px |
surface | card (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 identity —
window.RelayEmbed.identitywas 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".