Skip to main content

Widget API Reference

The Appilot Widget exposes a JavaScript API for programmatic control.

Two entry points

bootAppilotWidget() from the appilot package is the integration API: it loads the bundle and keeps it authenticated. window.Appilot is the runtime API the loaded bundle exposes for panel, view, theme and identity control. This page documents both, and everything on it exists today.

Initialization​

bootAppilotWidget(options)​

Load the widget bundle and keep it authenticated for the life of the page. Idempotent while the widget is healthy: calls made while it is booting or ready are ignored. After a failed boot the next call retries from scratch.

import { bootAppilotWidget } from 'appilot';

bootAppilotWidget({
widgetScriptUrl: 'https://cdn.appilot.space/widget/v1/appilot.esm.js',
widgetKey: 'wk_live_a1b2c3d4...',
tokenEndpoint: '/api/widget/token',
theme: 'dark',
});

Options (AppilotWidgetBootOptions):

FieldTypeRequiredDescription
widgetScriptUrlstringYesWidget bundle URL. When absent, the assistant is disabled.
widgetKeystringNoPublishable widget key. Optional on a registered domain; required for localhost/dev and server-to-server.
appilotApiUrlstringNoAppilot backend base URL. Omit it and the widget bundle uses the host it was built for, which for the CDN build is the Appilot cloud.
tokenEndpointstringNoIdentity relay on your own backend that mints an identified widget token (default /api/widget/token).
tokenBodyRecord<string, unknown>NoJSON body sent to the relay (omitted = relay default).
getBearer() => string | null | undefinedNoSupplies the logged-in user's session bearer for the relay call, so the relay federates that user. Return null when there is no session.
position'bottom-right' | 'bottom-left'NoButton position.
themestringNoColor theme (light, dark, auto, host). Omitted, the bundle's own default (auto) applies. See Theming & dark mode.
languagestringNoUI language (en, es, de). Omitted, the bundle detects it from <html lang>.
brandNamestringNoHeader brand text and the name signing assistant turns (data-brand-name). Defaults to your organization name. See Brand name.
surfaceLabelstringNoHow the app names its assistant in console diagnostics.
timeoutMsnumberNoMaximum time for token relay + bundle readiness (default 10000).
autoOpen, collapsed, zIndex, piiMasking, userAuth, docsBaseUrl, accent, accentDark, width, height, layout, containersee ConfigurationNoThe same options as the data-* attributes, in camelCase. Each is stamped on the loader tag only when you pass it.

Returns: Promise<void>

getWidgetAvailability() / subscribeWidgetAvailability(listener)​

Observe boot state so your page can render an honest "assistant unavailable" fallback. getWidgetAvailability() returns the current state ('unknown' | 'booting' | 'ready' | 'unavailable'); subscribeWidgetAvailability(listener) calls listener on every change and returns an unsubscribe function. They pair directly with React's useSyncExternalStore(subscribeWidgetAvailability, getWidgetAvailability).

import { getWidgetAvailability, subscribeWidgetAvailability } from 'appilot';

const stop = subscribeWidgetAvailability((state) => {
console.log('assistant is now', state);
});
// call stop() to unsubscribe

What the loaded widget exposes​

Once the bundle has loaded, the whole runtime surface is on window.Appilot. Everything in this table exists today.

MethodSignatureWhat it does
initAppilot(config) => voidMount the widget with an explicit configuration. Calling it again replaces the current instance.
destroyAppilot() => voidUnmount the widget, drop its listeners, and remove the presence attribute so a co-installed extension restores its own launcher.
open({ prefill? }) => voidOpen the panel, optionally with text waiting in the composer.
close() => voidClose the panel.
setView(view) => voidDeclare the current view. See View sync.
getView() => objectThe last detected view, its id and the method that detected it.
setTheme('light' | 'dark' | 'auto' | 'host') => voidChange the theme policy without remounting, so the open conversation survives.
setLanguage('en' | 'es' | 'de') => voidChange the panel language at runtime, the same way.
setUserToken(token | null) => voidHand the live instance a fresh federated token. bootAppilotWidget calls this on its own refresh; call it yourself when your app authenticates after the widget booted. null clears the identity.
on(event, callback) => () => voidSubscribe to a lifecycle event. Returns an unsubscribe function.

There is no toggle, isOpen, sendMessage, setContext or updateAppilot. open() and close() cover the panel, and nothing sends a message on the user's behalf by design: open({ prefill }) puts text in the composer for the user to review and send.

Appilot.open(options?)​

// Just open the panel:
window.Appilot.open();

// Open with a suggested prompt in the composer:
document.querySelector('#generate-scenarios').addEventListener('click', () => {
window.Appilot.open({ prefill: 'Generate three scenarios for this field' });
});

Use it to make "ask the assistant" affordances first-class: a button next to your feature opens the panel with the right prompt ready, instead of telling the user what to type.

Appilot.setTheme(mode) and Appilot.setLanguage(locale)​

Both take effect immediately without remounting, so the conversation on screen is preserved. Wire them to your own controls:

myThemeToggle.addEventListener('change', e => {
window.Appilot.setTheme(e.target.checked ? 'dark' : 'light');
});

myLanguageSelect.addEventListener('change', e => {
window.Appilot.setLanguage(e.target.value);
});

See Theming and dark mode for the no-code data-theme="host" option, which usually replaces the first of these.

Events​

Appilot.on(event, callback) returns an unsubscribe function.

const unsubscribe = window.Appilot.on('viewchange', ({ view, method }) => {
console.log('assistant is now on', view, 'detected by', method);
});

unsubscribe();
EventPayloadWhen
ready{ view, viewId }The widget mounted and the page context resolved.
viewchange{ view, viewId, method }The detected view changed. method is sdk, url_pattern, dom_selector, page_title or none.
contentloaded{ count }Knowledge for the current view finished loading. count is how many items are in scope.
opennoneThe panel opened, from any cause: the launcher, open(), or autoOpen.
closenoneThe panel closed, from any cause.
layoutchange{ x, y, width, height } or nullThe panel's bounds in viewport CSS pixels changed.

open and close are notifications, distinct from the commands of the same name. A page that traps focus inside its own modal subscribes to them to release the trap while the assistant holds the user's attention.

Keeping host results visible​

Subscribe to layoutchange to place your app's result cards beside the panel. Late subscribers receive the last reported value immediately, and moving, resizing or changing the layout updates it.

const unsubscribe = window.Appilot.on('layoutchange', bounds => {
positionResultCard(bounds); // Your app chooses a free rectangle.
});

Only the full panel is measured. While its code is loading, keep a compact fallback. Bounds exclude overflowing menus and other overlays. On a small screen, offer an explicit switch between the result and the conversation rather than covering either with an unreadable card. Do not inspect the closed Shadow DOM or assume a fixed width or side.

DOM events​

Independently of Appilot.on, the widget dispatches a DOM CustomEvent the host page can listen to:

EventTargetdetailWhen
appilot:turn-completedocument{ conversationId: number | null, tools: string[] }After each assistant turn completes. tools is the deduped list of tool names invoked during the turn. It never carries message content.

Use it to refresh your app's data when the assistant writes through your tools:

document.addEventListener('appilot:turn-complete', e => {
if (e.detail.tools.includes('create_record')) refreshRecordList();
});

The page can also dispatch two events the widget listens for: appilot:viewchange to declare the current view, and appilot:identity-refresh-requested to ask the boot loader for a fresh user token. There is no appilot:ready window event; use Appilot.on('ready', ...).

Client actions​

registerTool from the appilot package makes one of your own functions callable by the agent. It has its own page: Client actions.

Federated authentication​

Your backend mints a short-lived Appilot token for the signed-in user and the widget presents it on every turn. The exchange, its error codes and examples in several languages are on The identity relay.

From the page, all you do is point the boot call at your own endpoint:

import { bootAppilotWidget } from 'appilot';

bootAppilotWidget({
widgetScriptUrl: 'https://cdn.appilot.space/widget/v1/appilot.esm.js',
tokenEndpoint: '/api/widget/token',
getBearer: () => getMyAppSessionToken(), // only when your session is a bearer
});

The loader re-relays through that endpoint before the token expires and hands the fresh token to the live instance, so a long-lived tab stays authenticated. When your app authenticates after the widget booted, call window.Appilot.setUserToken(token) yourself.

caution

POST /widget/token uses your server secret and runs on your backend, never in the browser. The public widget key alone cannot mint a user token.

TypeScript Types​

The appilot package includes full TypeScript declarations:

import type { AppilotWidgetBootOptions } from 'appilot';

const options: AppilotWidgetBootOptions = {
widgetScriptUrl: 'https://cdn.appilot.space/widget/v1/appilot.esm.js',
widgetKey: 'wk_live_...',
theme: 'dark',
};