Widget developer guide
Guides

Widget developer guide

Embed script attributes, SPA installs, inline mode, sessions, domain allowlisting, and CSP for the Asks widget.

This page covers the widget from the developer's side — the embed script, its attributes, and how it behaves inside your page. For setup and appearance, see Website widget and Widget customization.

The embed script

The dashboard's Deploy tab generates your snippet:

HTML
<script
  src="https://cdn.asks.app/embed.js"
  data-widget-key="YOUR_WIDGET_KEY"
  async
></script>

The script auto-initializes when it finds a data-widget-key, mounts a floating launcher in a Shadow DOM (your page's CSS can't leak in, and the widget's can't leak out), and talks to api.asks.app. The widget key is public by design — it identifies your widget, it doesn't authenticate anyone.

Supported attributes

data-widget-keystringrequired

Your widget key. Without it the script does nothing.

data-container-idstring

Mounts the widget inline into the element with this id instead of a floating launcher. You control the container's size:

HTML
<div id="asks-widget-embedded" style="width:100%;height:600px;"></div>
<script src="https://cdn.asks.app/embed.js" data-widget-key="YOUR_WIDGET_KEY" data-container-id="asks-widget-embedded" async></script>
data-customer-email / data-customer-name / data-shopify-customer-idstring

Optional identity hints for logged-in visitors, forwarded when a conversation starts so the customer record matches. These are personalization hints only — the backend never treats them as authentication.

Manual initialization

The script also exposes a global for cases where you can't set attributes:

JavaScript
window.AsksWidget.init({
  widgetKey: "YOUR_WIDGET_KEY",
  containerId: "asks-widget-embedded", // or targetSelector: ".chat-slot"
  identity: { email: "sam@example.com", name: "Sam" },
});

Programmatic control

The global also lets you open and close the floating panel from your own UI — a "Chat with us" link, a help menu item:

JavaScript
window.AsksWidget.open();
window.AsksWidget.close();

Both are no-ops until the widget has mounted. window.AsksWidget.version returns the build identifier of the loaded script — include it when reporting a widget issue.

Single-page apps

Load the script once — in your root HTML or root layout — not per route. The widget survives client-side navigation on its own, and init is idempotent (re-running it clears and remounts rather than duplicating). In React or Next.js, do not mount and unmount the script with a component that re-renders per page.

Don't render the snippet inside a component that unmounts on navigation. Each remount tears down the widget and the visitor's open conversation UI state with it.

Sessions

There is nothing to implement. When a visitor starts or resumes a conversation, the backend issues a signed session token bound to that workspace, conversation, and customer; the widget presents it on subsequent calls. Tampering with conversation or customer ids breaks the signature and the request is rejected. You never handle credentials, and the identity attributes above stay untrusted hints.

Allowed domains

Your widget configuration can carry a domain allowlist. When it's set, the backend checks the browser's Origin/Referer on widget traffic and rejects other domains with 403. An entry matches its exact host and any subdomain (example.com covers shop.example.com). An empty list allows all domains.

This deters embedding your widget key on someone else's site; it is a browser-origin check, not an authentication boundary — scripted abuse is separately contained by per-visitor rate limits.

Content-Security-Policy

If your site enforces a strict CSP, allow:

DirectiveValueWhy
script-srchttps://cdn.asks.appThe embed.js bundle.
connect-srchttps://api.asks.app wss://api.asks.appAll widget API traffic, plus the live-messaging socket (wss:). Without the socket origin, replies still arrive but only on refresh — no live updates or typing indicators.
img-srchttps://storage.googleapis.comUploaded media: your workspace logo and launcher icon, agent avatars, and image attachments in conversations.
media-srchttps://storage.googleapis.comAudio/video attachments in conversations.
font-srcdata:The widget's font is inlined as a data: URI.

Styles live inside the widget's Shadow DOM, so no external stylesheet origin is needed. The font is registered through a small inline <style> tag in your document head — if your style-src bans inline styles, the widget still works and falls back to system fonts. If your agent sends images in replies from other origins (e.g. your own product pages), those origins also need to pass your img-src.

Troubleshooting

Open the browser console — the widget logs under an [asks-widget] prefix.

The widget doesn't show

  • Wrong key — the config request returns 404 Widget not found. Re-copy the snippet from the Deploy tab.
  • Widget turned off — the config request returns 400 Widget is not active. Enable the widget in the dashboard.
  • Domain not allowed — requests return 403. Add the site's domain to the widget's allowed domains, or clear the list.

Inline mode renders nothing

The container element must exist before the script runs. If it doesn't, the console shows Inline container not found and the widget skips mounting — move the script after the div, or keep async and ensure the div is in the initial HTML.

The launcher is hidden behind my UI

The floating host uses the maximum z-index (2147483647) inside its own fixed-position host element. If it's still obscured, check for ancestor stacking contexts created by your own transform or filter styles on body.