Widget
One <script> tag, a floating button, a modal or an inline panel (§61).
Not published yet
@callagent/web-sdk is private: true, so neither the npm package nor the cdn.callagent.dev bundle referenced below is published. The script tag is the shape the widget will ship with; today it is built from a checkout (pnpm --filter @callagent/web-sdk build produces dist/widget.global.js) and served yourself. The widget also depends on the same unimplemented POST /api/v1/client/calls route as the Web SDK, so it cannot start a call against a real control plane yet.
<script
src="https://cdn.callagent.dev/widget.js"
data-public-key="ca_pk_…"
data-assistant-id="01J…"
data-mode="voice"
data-theme="auto"
data-accent="#4f46e5"
data-position="bottom-right"
data-locale="en"
data-greeting="Hi! Ask me anything about your order."></script>17.38 KB gzipped (15.39 KB brotli), no framework runtime, no jQuery, no global CSS.
Attributes
| Attribute | Values | Default |
|---|---|---|
data-public-key | ca_pk_… | required (or data-token) |
data-token | a JWT your backend minted | — |
data-assistant-id | assistant ULID | — |
data-api-url | control-plane origin | https://api.callagent.dev |
data-mode | voice, chat, voice+chat | voice |
data-theme | light, dark, auto | auto |
data-accent | any CSS colour | #4f46e5 |
data-position | bottom-right, bottom-left, top-right, top-left | bottom-right |
data-variant | floating, inline | floating |
data-target | selector, required for inline | — |
data-locale | en, ro, fr, de | browser language |
data-greeting | text shown before the call starts | — |
data-variables / data-metadata | JSON objects | — |
data-auto-open | true / false | false |
data-branding | false hides the attribution | true |
data-z-index | integer | 2147483000 |
data-auto | false to mount from JavaScript instead | true |
A malformed JSON attribute is ignored rather than preventing the widget from mounting.
From JavaScript
<script src="https://cdn.callagent.dev/widget.js" data-auto="false"></script>
<script>
const widget = CallAgentWidget.mount({
publicKey: 'ca_pk_…',
assistantId: '01J…',
variant: 'inline',
target: '#assistant',
strings: { launcherLabel: 'Call the vet' }, // override any built-in string
});
widget.open();
widget.close();
widget.destroy();
</script>widget.client is the underlying CallAgent, so client-side tools are registered on it exactly as in the SDK.
Isolation
Everything renders in a Shadow DOM with all: initial on :host. Nothing the widget defines can reach the host page, and nothing on the host page — a Tailwind preflight, an aggressive reset — can reach in. The widget adds exactly one element to document.body and zero stylesheets to the document.
Accessibility
- The launcher and every control are keyboard reachable, with a visible focus ring.
- The panel is
role="dialog" aria-modal="true"with an accessible name, and traps Tab; Escape closes it and returns focus to the launcher. - The transcript is
role="log" aria-live="polite". Interim results reuse one element rather than appending each revision, so a screen reader is not read the same sentence five times. prefers-reduced-motion: reducedisables the entrance animation, the pulse and smooth scrolling.- Host-supplied strings are escaped, not injected as markup.
Localization
en, ro, fr and de ship in the bundle; a regional tag falls back to its base language and then to English. Any string can be overridden through strings, so a customer can ship their own language without waiting for a release.
Demo
pnpm --filter @callagent/web-sdk build
pnpm --filter @callagent/web-sdk demoapps/web-sdk/demo/index.html mounts both variants, exposes every option as a control and registers a demo client-side tool.