Skip to content
Returning.AIDocs

Banners

A separate, lightweight runtime for embedding Returning.AI banners on any page - no auth flow, no iframe. Available since SDK 1.6.0 as its own bundle, rai-banner.iife.js (~13 KB gzipped).

Widgets render banners automatically. Since SDK 1.6.0, mounting any widget (<rai-store-widget>, <rai-channel-widget>, ...) also renders eligible banners targeted at that widget type, and the widget element emits the same rai-banner-* events. Disable per widget with banner="off" (or the no-banner attribute). The standalone <rai-banner> element below is for pages with no widget.

Embed

Custom element:

<script src="https://unpkg.com/@returningai/widget-sdk@1.8.6/dist/rai-banner.iife.js"></script>

<rai-banner community-id="YOUR_COMMUNITY_ID"></rai-banner>

Or script-tag bootstrap (auto-creates the element):

<script
  src="https://unpkg.com/@returningai/widget-sdk@1.8.6/dist/rai-banner.iife.js"
  data-rai-banner
  data-community-id="YOUR_COMMUNITY_ID"
></script>

No mount <div> is required for any placement. Overlay and popup banners float over the page as a centered card; inline banners render into the banner's configured target selector, or in place where the element sits when that selector is not on the page.

Attributes

Attributes work with or without the data- prefix, on the element or the bootstrap script tag.

AttributeRequiredDescription
community-idYesYour community ID from Community Settings.
api-urlNoExplicit API base URL (overrides domain-key).
domain-keyNoEnvironment key resolving the API host: LOCAL | SGTR | STG | GEN. Defaults to LOCAL, so production embeds should set the key Returning.AI gives them (or an explicit api-url).
surfaceNoEligibility surface. Defaults to sdk; leave it unless Returning.AI tells you otherwise.
page-path / page-hostname / page-urlNoOverride the page context used for banner page rules. Defaults to window.location.
storage-prefixNolocalStorage key prefix for the anonymous visitor ID.
embed-tokenNo*SDK embed token minted from your access ID/key, exactly like Access Key Embed for widgets. Required for banners whose Access setting is domain or SDK auth - see Access types below.
<!-- Domain / SDK-auth banners: mint the embed token on your
     server from your access id/key, exactly like widgets -->
<rai-banner
  community-id="YOUR_COMMUNITY_ID"
  embed-token="EMBED_TOKEN_FROM_YOUR_SERVER"
></rai-banner>

How it works

  1. Mints or loads an anonymous visitor UUID in localStorage (anonymous_session), enabling frequency caps and dismiss-memory without any PII.
  2. Calls the public eligibility endpoint POST /v2/api/banner-sdk/eligible with page and viewport context.
  3. Renders the returned banner: overlay/popup placements as a centered card, inline into the configured target (or in place).
  4. Fires one-time, HMAC-signed impression / click / dismiss tracking URLs (replay-protected server-side).
  5. Banner HTML is sanitized client-side with DOMPurify before injection.

Access types

Each banner has an Access setting (chosen in the dashboard) that decides what the embed must provide:

Banner AccessWhat the embed needs
Open to everyoneNothing - community-id only.
Domain gateThe page is served from one of the banner's allowed domains (enforced on the request Origin), or an embed-token whose access key authorizes the origin.
SDK authA valid embed-token whose access key authorizes the origin.

Widget-mounted banners pass the widget's embed-token automatically, so domain and SDK-auth banners work on widgets with no extra setup. A live banner whose surfaces include sdk is required.

Events

The element emits bubbling CustomEvents. Widget-mounted banners emit the same events from the widget element.

EventDetail
rai-banner-impression{ bannerId }
rai-banner-click{ bannerId }
rai-banner-dismiss{ bannerId }
rai-banner-empty{ reason } - no eligible banner
rai-banner-error{ error }
document.addEventListener("rai-banner-impression", (e) =>
  console.log("banner shown", e.detail.bannerId),
);
document.addEventListener("rai-banner-click", (e) =>
  console.log("banner clicked", e.detail.bannerId),
);
document.addEventListener("rai-banner-dismiss", (e) =>
  console.log("banner dismissed", e.detail.bannerId),
);
document.addEventListener("rai-banner-empty", (e) =>
  console.log("no eligible banner", e.detail.reason),
);
document.addEventListener("rai-banner-error", (e) =>
  console.error("banner error", e.detail.error),
);

Note: since 1.6.0 the main widget bundle also makes one eligibility request per widget mount and bundles DOMPurify, so rai-widget.iife.js is slightly larger. Use banner="off" on a widget to skip that request.