Reference
Configuration
Every <hive-chat> attribute and HiveChat.init option, theming CSS variables, language and RTL.
Everything below is verified against the widget source and the platform’s Zod schemas by the portal’s drift tests: if an option is listed here it exists, and if one is added to the SDK the build fails until it is documented.
Where settings come from
A setting can be supplied in up to four places. Later entries are defaults for earlier ones:
// 1. <hive-chat> attributes (highest — markup wins)
// 2. HiveChat.init() / configure() (programmatic)
// 3. key's server-side theme + config (console → Embed → Configure)
// 4. built-in defaults (lowest)The server-side theme and config live on the key and are returned by GET /v1/embed/config on every page load (and again with each session), so the console is the single place to restyle an already-deployed widget. Local overrides are for things the page knows and the console does not (the page language, a signed-in user, a container).
Options and attributes
One row per option of HiveChat.init(options). The same option is available as an attribute on <hive-chat> (column Attribute) and, for the script-tag loader, as a data-* attribute on the <script> tag (column Script data-*).
| Option | Attribute | Script data-* | Type | Default | Description |
|---|---|---|---|---|---|
keyrequired | key | data-key | string | — | Publishable embed key (hive_pk_ + 43 url-safe chars). Bound to one agent and an origin allowlist; safe to ship in HTML. Required on the public embed API; optional only in bearer mode (authToken set) — with neither, init fails. |
apiUrl | api-url | data-api-url | string (origin) | origin of the loader <script> | Platform origin the widget talks to (apiUrl + basePath, so /v1/embed/* by default). Inferred from the script’s own src; required when you bundle the ESM build yourself or proxy the bundle. |
basePath | base-path | — | string (absolute path) | '/v1/embed' | API path under apiUrl where the embed endpoints (/config, /session, /messages, /threads/:id/messages, /runs/:id/events, /runs/:id/cancel) are mounted. Leave it unset for the public embed API; set it only when a platform surface mounts the same wire protocol elsewhere (today: the prototype agent bridge, /v1/prototypes/<id>/agent, together with authToken). Also the knob when a proxy re-mounts the whole API under a prefix (e.g. /api/v1/embed); a proxy that keeps /v1/embed needs only apiUrl. Trailing slashes are stripped; no query, fragment or .. segments. Attribute/programmatic only. |
authToken | auth-token | — | string | unset (session minted from key) | Pre-issued bearer credential — bearer mode. The widget mints no session: every request, GET <basePath>/config included, carries Authorization: Bearer <authToken>, and hive:ready reports key_id: null. A 401 is never retried; the widget dispatches hive:auth-expired, shows its error state and pauses sends until the host calls setAuthToken() (or rewrites the attribute), then fires hive:auth-resumed. Used with a basePath that accepts such tokens (the prototype bridge); the public /v1/embed API does not — for signed-in users on your own site use user: { token }, not authToken. Attribute/programmatic only. |
binding | binding | — | string ([a-z][a-z0-9_]*) | unset | Bearer mode only: names which of the host’s declared agents the conversation is with. Sent as ?binding= on GET <basePath>/config and as binding in every POST <basePath>/messages; the server resolves it to an agent (the prototype bridge requires it and rejects a binding not declared for the token’s role). Ignored by the public /v1/embed API, where the key already names the agent. Attribute/programmatic only. |
locale | locale | data-locale | 'en' | 'ar' | 'auto' | key’s server config, else 'auto' | UI language. ar switches the whole panel to RTL. auto reads <html lang> then navigator.language (anything starting with ar is Arabic). |
theme | theme (JSON) | data-theme (JSON) | EmbedTheme | key’s server theme | Local theme overrides — see the theme table below. Invalid values are dropped, never thrown. Wins over the key’s server-side theme. |
title | title | data-title | string | server config title, else the agent name | Panel header title. |
subtitle | subtitle | data-subtitle | string | server config subtitle | Small line under the title. Hidden when empty. |
welcome | welcome | data-welcome | string | server config welcome | First agent bubble shown before any message is sent (rendered as Markdown, dir="auto"). |
placeholder | placeholder | data-placeholder | string | server config placeholder, else a localized default | Composer placeholder + accessible label. |
user | user-token | data-user-token | { token: string } | anonymous visitor | Identified end user: an HS256 JWT minted by YOUR backend with the key’s identity secret. The attribute form takes the raw token. |
mode | mode | data-mode | 'launcher' | 'inline' | 'launcher' | launcher = floating bubble + panel appended to document.body; inline = the panel fills its container element (use data-container with the script tag). |
open | open (presence) | data-open | boolean | false | Start with the panel open (launcher mode). Any data-open value except "false" counts as true. |
storage | storage | data-storage | 'local' | 'session' | 'none' | 'local' | Where the visitor id and current thread id persist (localStorage, sessionStorage, or in-memory only). Keys are namespaced hive-embed:<key prefix>:visitor|thread — in bearer mode without a key, hive-embed:<basePath>[:<binding>]:…, so two mount points or bindings never share a visitor. |
visitorId | visitor-id | — | string (8–128 url-safe chars) | generated uuid, persisted per storage | Override the visitor id — e.g. to share one conversation across subdomains or devices you already identify. Attribute/programmatic only. |
blocks | — | — | { languages: string[], render: 'host' | 'inline' | 'both', placeholder?: string } | unset (every fence renders as code) | Fenced rich output. inline renders schema-validated chart or chart-shaped json results directly in the conversation; host replaces listed fences with a chip and emits hive:block; both renders inline and emits the event so a host dashboard stays synchronized. Native charts support bounded bar, line, pie and KPI specs; labels are escaped and no model HTML or script executes. Invalid inline specs and unlisted languages remain escaped code. Streaming raw sources are suppressed; restored charts render inline but host events are not replayed. Programmatic only (init() / update() / React blocks prop). |
strings | — | — | { en?: Partial<Strings>, ar?: Partial<Strings> } | unset (built-in EN/AR copy) | Per-locale overrides of the widget’s visitor-facing copy (0.2.1): error ("Something went wrong. Please try again."), retry, offline ("Chat is unavailable right now."), waiting ("Waiting for a human to approve the next step…"), sending, working, and the labels open, close, title, placeholder, send, newChat, you, agent, transcript, blockChip, plus the voice keys voiceStart, voiceEnd, voiceMute, voiceUnmute, voiceHandsfree, voiceListening, voiceThinking, voiceSpeaking, voiceMuted, voiceError, voiceMicBlocked, voiceUnsupported. The override for the *resolved* locale applies; unset keys keep the built-in text, so a host that passes nothing sees exactly the 0.2 copy. Values are plain text (rendered escaped), trimmed to 200 characters; unknown keys and non-strings are ignored. The "Powered by" line is not overridable. Programmatic only (init() / update() / React strings prop). |
voice | — | — | { handsfree?: boolean, ptt?: boolean } | unset (the key’s effective server flags apply) | In-app voice. The mic button appears in the composer when GET /v1/embed/config reports voice.handsfree or voice.ptt as true (deployment ∧ tenant ∧ manifest ∧ key) AND this option does not switch that mode off — it can only tighten, never enable. Tapping the mic lazy-loads /embed/v1/hive-embed-voice.js once, mints a single-use ticket (POST /v1/embed/voice/ticket) and opens wss://…/v1/embed/voice?ticket=…; the orb + captions row replaces the composer until End. Spoken turns land in the transcript like typed ones (hive:message), hive:status gains listening | thinking | speaking, and hive:voice reports every state. Non-boolean values are ignored. Programmatic only (init() / update() / React voice prop). |
Script-tag-only attributes
| Script data-* | Type | Default | Description |
|---|---|---|---|
data-container | CSS selector | document.body | Element to mount into (pair with data-mode="inline"). |
data-manual | presence | auto-init when data-key is set | Skip auto-init; call window.HiveChat.init({...}) yourself (a queued window.HiveChat = { q: [[opts]] } stub is drained on load). |
Theme
theme is an object with the fields below. The same shape is stored on the key (console → Configure → Appearance) and validated server-side with embedThemeSchema; local values are merged over server values field by field.
| Field | Type | Default | CSS variable | Description |
|---|---|---|---|---|
primary | hex color | #4f46e5 | --hive-primary | Brand color: launcher, user bubbles, send button. |
on_primary | hex color | #ffffff | --hive-on-primary | Text/icon color drawn on top of primary. |
background | hex color | #ffffff | --hive-bg | Panel background. |
foreground | hex color | #111827 | --hive-fg | Panel text. Muted/border/surface tones are derived from it. |
radius | integer 0–32 | 16px | --hive-radius | Corner radius (px) for the panel and bubbles. |
font | string (≤ 160 chars) | system-ui, …, "Noto Sans Arabic", … | --hive-font | CSS font-family stack. Characters ; { } < > are rejected. |
position | 'start' | 'end' | 'end' | — | Launcher/panel side, logical: end is right in LTR and left in RTL; start is the opposite. |
launcher_icon | 'chat' | 'sparkles' | 'help' | 'chat' | — | Icon drawn in the floating launcher button. |
z_index | integer ≥ 0 | 2147483000 | --hive-z | Stacking order of the launcher and panel. |
avatar | URL string (≤ 512 chars) | unset (first letter of the title, or the agent emoji) | — | Static header image (widget 0.2.2): an https:// URL or a same-origin path such as /brand/agent.svg. data: and protocol-relative URLs are dropped. The agent’s talking face is separate — Talking avatars. At config.avatar.size: "compact" the live face occupies this spot. |
HiveChat.init({
key: 'hive_pk_REPLACE_WITH_YOUR_PUBLISHABLE_KEY',
theme: {
primary: '#0f766e',
on_primary: '#ffffff',
background: '#ffffff',
foreground: '#0b1324',
radius: 14,
font: 'Inter, system-ui, sans-serif',
position: 'start',
launcher_icon: 'sparkles',
z_index: 1000,
},
});Colors must be 3- or 6-digit hex (^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$). The panel derives muted text, borders and surfaces from foreground/background, so a dark theme is just a dark background with a light foreground.
CSS variables and parts
The theme is applied as CSS custom properties on the hive-chat host element, which the shadow stylesheet reads. You can therefore also theme from your own stylesheet — useful for dark-mode toggles — and style the exposed ::part()s:
/* Host-page CSS: variables are read inside the shadow root */
hive-chat {
--hive-primary: #0f766e;
--hive-on-primary: #fff;
--hive-radius: 14px;
--hive-z: 1000;
}
/* Parts: style exposed pieces without touching internals */
hive-chat::part(launcher) {
box-shadow: 0 8px 24px rgba(15, 118, 110, 0.35);
}
hive-chat::part(panel) {
border: 1px solid #d0d7de;
}| Part | What it is |
|---|---|
launcher | The floating bubble button (launcher mode). |
panel | The chat panel (role="dialog"). |
header | Title bar with avatar, title, subtitle, reset + close. |
avatar-stage | The panel above the transcript where a talking avatar with config.avatar.size: "stage" is drawn. Hidden until the embed key enables a face. See Talking avatars. |
messages | Scrollable transcript (role="log"). |
chart | A schema-validated native bar, line, pie or KPI result rendered inside an agent message (since 0.4.0). |
composer | The textarea + send form. |
attachments | Selected-document tray above the composer. |
mic | The voice button in the composer (voice option). Present only when the key’s effective voice flags and the host option allow a mode. |
voice | The orb + captions + mute/end row that replaces the composer while a voice session is live. |
status | The in-flight row under the transcript (since 0.2.1): data-status is sending, working or waiting. |
error | The failed-turn row (and the offline notice) with its Retry button (since 0.2.1): data-code carries the problem code (RATE_LIMITED, BUDGET_EXCEEDED, EMBED_ORIGIN_DENIED, …) for CSS/tests — the visitor sees only strings.error. Hide it when your page renders its own copy: hive-chat[data-my-state]::part(error) { display: none }. |
branding | The "Powered by" footer (hidden via show_branding: false; links to branding_url when set). |
handle | The grab handle at the top of the phone sheet (since 0.2.2; ≤ 480 px, launcher mode). Swipe down on the header closes the sheet. Hidden on desktop. |
Slots (since widget 0.2.2)
Light-DOM children of hive-chat with a matching slot attribute render inside the panel between the transcript and the composer, styled by your own stylesheet. Nothing renders when a slot is empty — existing pages are unchanged. On a phone (≤ 480 px) the panel is a full-screen sheet: it follows the visual viewport while the keyboard is up, locks the page behind it, and closes on a swipe down from the header (::part(handle)).
| Slot | What it is |
|---|---|
notice | A host-owned notice row (e.g. your own error/state copy) above the composer. |
suggestions | A row of suggested prompts/chips above the composer — a tap can call send(). |
Text and language (server-side display config)
Texts are stored on the key as config (embedDisplayConfigSchema) and can be overridden locally with the options of the same name (title, subtitle, welcome, placeholder, locale). launcher_label, show_branding and branding_url are server-side only.
| Field | Type | Description |
|---|---|---|
title | string ≤ 80 | Panel header title (defaults to the agent name). |
subtitle | string ≤ 120 | Line under the title. |
welcome | string ≤ 1000 | First agent bubble before any message. |
placeholder | string ≤ 120 | Composer placeholder. |
locale | 'en' | 'ar' | 'auto' | Default UI language when the host sets none. |
launcher_label | string ≤ 60 | Accessible label + tooltip for the launcher. |
show_branding | boolean | Show the "Powered by" footer (default true). |
branding_url | https URL ≤ 500 | Where the "Powered by" footer links. Unset (or not https) renders the footer as plain text. |
voice | { handsfree?: boolean; ptt?: boolean } | In-app voice on the widget. Both default false — a public widget must be explicitly allowed to open microphones. Effective only when the deployment, tenant and agent manifest allow it too; GET /v1/embed/config returns the effective flags. |
avatar | { enabled?: boolean; size?: 'compact' | 'stage' } | Talking avatar on the widget (default off). compact draws the agent’s face in the header; stage draws it in ::part(avatar-stage) above the transcript. Effective only when the agent has a face; GET /v1/embed/config returns agent.avatar as null until enabled is true. See Talking avatars. Distinct from the static theme.avatar image. |
Language and RTL
The widget ships English and Arabic UI strings. locale resolves in this order: the local option/attribute, the key’s server config, 'auto'. With auto, <html lang> is checked first, then navigator.language; any value starting with ar selects Arabic, everything else English.
- Arabic sets
dir="rtl"andlang="ar"on the panel: layout, icons and the launcher side flip.positionis logical —endstays “the trailing side”. - Every message bubble and the welcome text render with
dir="auto", so mixed-language conversations read correctly in either UI language. - The agent answers in whatever language the visitor writes (subject to the agent’s persona). The UI language does not constrain the model.
- Change language at runtime with
handle.update({ locale: 'ar' })— no re-mount, the session is kept.
<!-- Arabic, right-to-left, launcher at the logical end (= left in RTL) -->
<script async src="https://console.agenticworkforce.me/embed/v1/hive-embed.js"
data-key="hive_pk_REPLACE_WITH_YOUR_PUBLISHABLE_KEY"
data-locale="ar"
data-title="Support assistant"
data-welcome="Hi! How can I help you today?"></script>Launcher, position and open state
mode: 'launcher'(default) appends a floating button todocument.body; clicking it opens a 400 × 640 px panel (full-screen under 480 px wide).theme.positionchooses the side;theme.z_indexthe stacking order.open: true(or theopenattribute /data-open) starts with the panel open.Escapecloses it in launcher mode.mode: 'inline'renders only the panel, filling the host element (give it a height). There is no launcher, andopenis ignored.- Programmatic control (
open(),close(),toggle()) is covered in the JavaScript API.
Welcome text
welcome is shown as the first agent bubble until the visitor sends something. It is rendered as Markdown (paragraphs, emphasis, links, inline code, lists) — the same renderer used for replies — and is not sent to the model. Up to 1000 characters when stored server-side.
Visitor id, storage and metadata
Every browser gets a random visitor id (UUID), persisted according to storage under hive-embed:<first 16 chars of the key>:visitor. The current thread id is stored next to it. Threads belong to the visitor: the API refuses history for a thread minted by another visitor.
// Share one anonymous conversation across app.acme.com and help.acme.com:
// generate the id once, store it in a cookie on .acme.com, pass it on both.
HiveChat.init({
key: 'hive_pk_REPLACE_WITH_YOUR_PUBLISHABLE_KEY',
visitorId: readCookie('acme_vid'), // 8–128 chars, [A-Za-z0-9_-]
storage: 'none', // you own persistence now
});Identified users
Pass user: { token } (attribute user-token, data-user-token) with a short-lived HS256 JWT signed with the key’s identity secret. The session is then bound to your user (end_user in the session response, identified: true in hive:session), threads are scoped to them, and the console shows them under Contacts. Keys created with Require a signed identity refuse anonymous sessions with 401 EMBED_IDENTITY_REQUIRED. Replacing the token at runtime with update({ user }) starts a new session (the anonymous thread is not carried over).
Bearer mode: pre-issued token, other mount point
Three options — basePath, authToken and binding — exist for platform surfaces that mount the embed wire protocol somewhere other than the public /v1/embed API and hand the page a credential of their own. Today that is the prototype agent bridge (/v1/prototypes/<id>/agent). Embedding on your own site does not use them: keep the publishable key, let the widget mint sessions, and identify signed-in users with user.token.
// A host that issued its own bearer for a non-default mount point.
// Not for the public /v1/embed API: there, use key (+ user.token for signed-in users).
const chat = HiveChat.init(
{
apiUrl: 'https://console.agenticworkforce.me',
basePath: '/v1/prototypes/PROTOTYPE_ID/agent', // where the embed endpoints live
authToken: grant, // pre-issued by the host; no session mint
binding: 'helpdesk', // which declared agent to talk to
mode: 'inline',
},
document.getElementById('chat'),
);
chat.element.addEventListener('hive:auth-expired', async () => {
const next = await reissueGrant(); // your host's re-issue endpoint
chat.setAuthToken(next); // same thread continues; fires hive:auth-resumed
});basePathreplaces/v1/embedin every request URL; the endpoint names after it stay the same.apiUrlstill supplies the origin.- With
authTokenthe widget mints no session and sendsAuthorization: Bearer <authToken>on every request, includingGET <basePath>/config.keybecomes optional;hive:readyreportskey_id: nullandhive:sessionfires withidentified: false. bindingtravels as?binding=on the config read and asbindingin each posted message; the server resolves it to the agent. The bridge requires it;/v1/embedignores it.- On a 401 the widget does not retry or re-mint (it cannot — only your authenticated host can issue a token). It dispatches
hive:auth-expired(detail.code= the problem code), shows the error state and ignoressend()untilsetAuthToken(newToken)(or a rewrittenauth-tokenattribute) resumes the same thread and fireshive:auth-resumed. Swapping the token is not a re-mint: the conversation and the visitor id are kept. - Storage is namespaced by
basePathandbindingwhen no key is set, so two mount points or two bindings never share a visitor or thread. - The React wrapper accepts the three options as props (they are part of
HiveChatOptions) and maps the two events toonAuthExpired/onAuthResumed; passing a newauthTokenprop is the token swap. It still requiresembedKey.