Agentic Workforce ME Developer PortalDocs 1.21 · Widget 0.5.0 · API —

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:

Text
// 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-*).

OptionAttributeScript data-*TypeDefaultDescription
keyrequiredkeydata-keystring—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.
apiUrlapi-urldata-api-urlstring (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.
basePathbase-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.
authTokenauth-token—stringunset (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.
bindingbinding—string ([a-z][a-z0-9_]*)unsetBearer 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.
localelocaledata-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).
themetheme (JSON)data-theme (JSON)EmbedThemekey’s server themeLocal theme overrides — see the theme table below. Invalid values are dropped, never thrown. Wins over the key’s server-side theme.
titletitledata-titlestringserver config title, else the agent namePanel header title.
subtitlesubtitledata-subtitlestringserver config subtitleSmall line under the title. Hidden when empty.
welcomewelcomedata-welcomestringserver config welcomeFirst agent bubble shown before any message is sent (rendered as Markdown, dir="auto").
placeholderplaceholderdata-placeholderstringserver config placeholder, else a localized defaultComposer placeholder + accessible label.
useruser-tokendata-user-token{ token: string }anonymous visitorIdentified end user: an HS256 JWT minted by YOUR backend with the key’s identity secret. The attribute form takes the raw token.
modemodedata-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).
openopen (presence)data-openbooleanfalseStart with the panel open (launcher mode). Any data-open value except "false" counts as true.
storagestoragedata-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.
visitorIdvisitor-id—string (8–128 url-safe chars)generated uuid, persisted per storageOverride 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-*TypeDefaultDescription
data-containerCSS selectordocument.bodyElement to mount into (pair with data-mode="inline").
data-manualpresenceauto-init when data-key is setSkip 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.

FieldTypeDefaultCSS variableDescription
primaryhex color#4f46e5--hive-primaryBrand color: launcher, user bubbles, send button.
on_primaryhex color#ffffff--hive-on-primaryText/icon color drawn on top of primary.
backgroundhex color#ffffff--hive-bgPanel background.
foregroundhex color#111827--hive-fgPanel text. Muted/border/surface tones are derived from it.
radiusinteger 0–3216px--hive-radiusCorner radius (px) for the panel and bubbles.
fontstring (≤ 160 chars)system-ui, …, "Noto Sans Arabic", …--hive-fontCSS 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_indexinteger ≥ 02147483000--hive-zStacking order of the launcher and panel.
avatarURL 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.
TypeScript
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:

CSS
/* 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;
}
PartWhat it is
launcherThe floating bubble button (launcher mode).
panelThe chat panel (role="dialog").
headerTitle bar with avatar, title, subtitle, reset + close.
avatar-stageThe 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.
messagesScrollable transcript (role="log").
chartA schema-validated native bar, line, pie or KPI result rendered inside an agent message (since 0.4.0).
composerThe textarea + send form.
attachmentsSelected-document tray above the composer.
micThe voice button in the composer (voice option). Present only when the key’s effective voice flags and the host option allow a mode.
voiceThe orb + captions + mute/end row that replaces the composer while a voice session is live.
statusThe in-flight row under the transcript (since 0.2.1): data-status is sending, working or waiting.
errorThe 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 }.
brandingThe "Powered by" footer (hidden via show_branding: false; links to branding_url when set).
handleThe 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)).

SlotWhat it is
noticeA host-owned notice row (e.g. your own error/state copy) above the composer.
suggestionsA 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.

FieldTypeDescription
titlestring ≤ 80Panel header title (defaults to the agent name).
subtitlestring ≤ 120Line under the title.
welcomestring ≤ 1000First agent bubble before any message.
placeholderstring ≤ 120Composer placeholder.
locale'en' | 'ar' | 'auto'Default UI language when the host sets none.
launcher_labelstring ≤ 60Accessible label + tooltip for the launcher.
show_brandingbooleanShow the "Powered by" footer (default true).
branding_urlhttps URL ≤ 500Where 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" and lang="ar" on the panel: layout, icons and the launcher side flip. position is logical — end stays “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.
HTML
<!-- 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 to document.body; clicking it opens a 400 × 640 px panel (full-screen under 480 px wide). theme.position chooses the side; theme.z_index the stacking order.
  • open: true (or the open attribute / data-open) starts with the panel open. Escape closes it in launcher mode.
  • mode: 'inline' renders only the panel, filling the host element (give it a height). There is no launcher, and open is 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.

TypeScript
// 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.

TypeScript
// 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
});
  • basePath replaces /v1/embed in every request URL; the endpoint names after it stay the same. apiUrl still supplies the origin.
  • With authToken the widget mints no session and sends Authorization: Bearer <authToken> on every request, including GET <basePath>/config. key becomes optional; hive:ready reports key_id: null and hive:session fires with identified: false.
  • binding travels as ?binding= on the config read and as binding in each posted message; the server resolves it to the agent. The bridge requires it; /v1/embed ignores 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 ignores send() until setAuthToken(newToken) (or a rewritten auth-token attribute) resumes the same thread and fires hive:auth-resumed. Swapping the token is not a re-mint: the conversation and the visitor id are kept.
  • Storage is namespaced by basePath and binding when 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 to onAuthExpired / onAuthResumed; passing a new authToken prop is the token swap. It still requires embedKey.