Custom Widget SDK
Troubleshooting
Start with the last step that worked. A successful token request does not prove that the bundle loaded or that the user has a loyalty profile.
Check without printing credentials
Run this in the browser console on your widget page. It reports whether each value is present, without revealing its contents.
const widget = document.querySelector('rai-custom-widget')
console.table({
sdkRegistered: Boolean(customElements.get('rai-custom-widget')),
widgetPresent: Boolean(widget),
hasWidgetId: Boolean(widget?.getAttribute('widget-id')),
hasBundleUrl: Boolean(widget?.getAttribute('bundle-url')),
hasEmbedToken: Boolean(widget?.getAttribute('embed-token')),
portalOrigin: window.location.origin,
})The widget stays blank
If sdkRegistered is false after the SDK script finishes loading, the browser has not registered the custom element. In the Network panel, check that the SDK request succeeded and returned JavaScript, rather than an HTML error or login page.
Install the package and import it in browser code, or load the pinned browser script once. In Next.js, load it after the client component mounts; a package install alone does not register the element. Check the console for blocked scripts, including your site's Content Security Policy, then use the matching framework example.
Authentication fails on this domain
Compare window.location.origin with the allowed domains on the SDK key under Community Settings > Integrations > SDK Access. Match the scheme, hostname, and port exactly; do not add a page path.
http://localhost:3000, http://127.0.0.1:3000, and https://portal.example.com are different origins. Add the one you are using, request a fresh token, and retry. Do not disable browser security to work around a rejected origin.
Your server cannot return a token
Inspect your application's /api/widget-token request first. A 401 from your application usually means the host session is missing or expired; sign in again. A server error needs a check of the upstream token request in your server logs.
Check the server environment values, the supplied token endpoint, and the configured userIdentifiers keys. The upstream request uses POST; its successful response carries data.embedToken. Your browser-facing route should return only the short-lived token and expiry, never the access key. See the authentication guide for the request and response formats.
The widget stops working after a while
Embed tokens last 15 minutes. Your application must request a fresh token before expiry or on rai-session-expired, then update embed-token on the existing element. The SDK does not mint Access Key tokens by calling your server automatically.
Check that the refresh request succeeds and that your code reads the response field your own route returns. If the host session has ended, stop retrying and ask the user to sign in. Cancel pending refreshes and clear the widget when the host user signs out; the runnable starters include this lifecycle.
Authentication succeeds, but the experience does not load
Check the full bundle-url in the Network panel. It must return JavaScript from the host supplied for your environment. A 200 response alone is not enough if it contains HTML.
Use the widget ID and bundle URL supplied together by Returning.AI. A widget ID selects a configured experience; it is not a bundle name. Different IDs can share one bundle, and a dedicated deployment can use a different host. Compare against your handover and the widget catalog; do not guess an ID from a filename.
The user's loyalty profile cannot be found
Confirm that the user is registered in the same environment. The identifier key and value signed into the token must match the ones sent during registration. For example, a customer ID must not be replaced with an email just because both identify the same person in your own system.
Read identifiers from the authenticated server session, not a browser form or query string. Ask your Returning.AI contact which fields this widget requires if your handover does not specify them. Follow user identifier guidance before changing registration data.
Collect details for support
Attach these listeners before the first token is set, then reproduce the failure. They distinguish authentication from bundle mounting without printing the token.
const widget = document.querySelector('rai-custom-widget')
if (!widget) throw new Error('Add the widget element before attaching listeners')
// Attach these before setting the first embed-token.
widget.addEventListener('rai-authenticated', () => {
console.info('Widget authentication completed')
})
widget.addEventListener('rai-mounted', () => {
console.info('Widget bundle mounted')
})
widget.addEventListener('rai-session-expired', () => {
console.warn('Request a new token from your server')
})
widget.addEventListener('rai-error', (event) => {
// Do not log tokens, full request bodies, or user identifiers.
console.warn('Widget failed', { status: event.detail?.status })
})Send your Returning.AI contact the SDK version, browser and framework versions, target environment, portal origin, failed request path and status, and which lifecycle events fired. Redact user details from screenshots and logs; avoid sending full network exports.
Never share access keys or embed tokens
Do not paste credentials, authorization headers, token responses, or full user identifiers into tickets, chat, or screenshots. Review SDK error messages locally and remove private details before sharing them.