Widget developer guide
Guides

Widget developer guide

Embed script attributes, the JavaScript API, identity verification for logged-in users, SPA and framework installs, and CSP for the Asks widget.

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

The embed script

The Deploy tab of the Chat widget (app.asks.app/channels/widget/deploy) 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 the widget 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. One snippet serves whichever website channel is live: the Chat widget's floating launcher, or the Spotlight pill. 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-user-id / data-user-hashstring

A logged-in user's stable id and its signature from your server. Together they give the user verified, cross-device conversation history — see Logged-in users.

data-customer-email / data-customer-name / data-shopify-customer-idstring

Optional details about the visitor, forwarded when a conversation starts so the customer record matches. On their own they are personalization hints only — the backend never treats them as authentication.

Open the widget from your page

Add data-asks-open to any button or link and a click opens the widget — the Chat widget panel or Spotlight, whichever is live. It works for elements added after page load too:

HTML
<button data-asks-open>Chat with us</button>

Or open and close it from your own code:

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.

Logged-in users

Without identity, visitors chat anonymously and their conversations live in a first-party cookie in that browser. If your site has logins, sign each user's id on your server and the widget gives them the same conversation history on every device and browser — and nobody can impersonate them.

Get the identity secret

On the Chat widget's Deploy tab, find Identity verification (for users with logins) and select Reveal identity secret. Store it as a server-side secret.

Chat widget Deploy tab: the server-side userHash code with Node.js, Ruby, Python, PHP, Go and Java tabs, the Installation status card showing the widget detected, and the Identity verification (for users with logins) card with the Reveal identity secret button
The identity secret stays hidden until you reveal it.
Compute userHash on your server

userHash is the hex HMAC-SHA256 of the user id, keyed with the identity secret. The Deploy tab shows the same code for Ruby, Python, PHP, Go, and Java.

JavaScript
// Node.js, on your server
const crypto = require("crypto");
const userHash = crypto
  .createHmac("sha256", process.env.ASKS_IDENTITY_SECRET)
  .update(String(user.id))
  .digest("hex");
Pass the identity to the widget

Render data-user-id, data-user-hash, data-customer-email and data-customer-name on the script tag — or, in a single-page app where login happens without a page load, call identify after login and identify(null) on logout:

JavaScript
window.AsksWidget.identify({
  userId: user.id,
  userHash: asksUserHash, // fetched from your server
  email: user.email,
  name: user.name,
});

window.AsksWidget.identify(null); // on logout

Verification is always on: when a user id is present, a missing or wrong userHash is rejected rather than treated as anonymous. Use Check a userHash on the Deploy tab to test a hash your server produced — it runs in your browser and sends nothing.

Never put the identity secret in browser code. Rotate secret on the Deploy tab invalidates every issued userHash at once — logged-in users fall back to anonymous chat until your server signs with the new secret.

React, Vue, and Angular

For module-bundled apps, install the @asksdotapp/widget-js package. It loads the hosted embed.js once, so you always run the latest widget:

Terminal
npm install @asksdotapp/widget-js
JavaScript
import Asks from "@asksdotapp/widget-js";

// In a shared layout or root component — repeated calls are ignored.
Asks({
  widgetKey: "YOUR_WIDGET_KEY",
  userId: user.id,          // optional: logged-in users
  userHash: user.asksHash,  // minted on your server
  email: user.email,
  name: user.name,
});

The package also exports show(), hide(), identify(identity), and shutdown() (the same as identify(null), for logout). Every call is safe before the widget finishes loading, and a no-op during server-side rendering.

Single-page apps

Load the widget once — the script in your root HTML or root layout, or the package's Asks() in a shared layout — not per route. The widget survives client-side navigation on its own, and window.AsksWidget.init is idempotent: re-running it clears and remounts rather than duplicating.

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

The global also exposes init for cases where you can't set attributes — it takes widgetKey, containerId or targetSelector (a CSS selector) for inline mode, and an identity object with the fields above.

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 email and name hints stay untrusted unless the user is verified with userHash.

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://cdn.asks.app https://storage.googleapis.comUploaded media: your workspace logo and launcher icon, agent avatars, and image attachments in conversations. Media serves from our CDN (cdn.asks.app), with storage.googleapis.com as the fallback origin — allow both.
media-srchttps://cdn.asks.app https://storage.googleapis.comAudio/video attachments in conversations.
frame-srchttps://storage.googleapis.comThe inline PDF preview's browser-viewer fallback.
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 not live — the config request returns 400 Widget is not active. Turn on Live for the Chat widget or Spotlight in the dashboard.

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.

A logged-in user gets an error or loses their history

The userHash doesn't match. Paste the user id and hash into Check a userHash on the Deploy tab, and make sure your server hashes the exact user id string with the current identity secret.

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.

Chat on WhatsApp