Reference
JavaScript API & events
HiveChat.init handle methods, element events and programmatic control recipes.
Three objects matter: the global HiveChat API (the loader puts it on window; the ESM build exports it), the handle returned by HiveChat.init, and the <hive-chat> element itself, which dispatches DOM events.
HiveChat
| Member | Signature | Description |
|---|---|---|
version | readonly version: string | Build version of the loaded bundle (the @hive/embed package version, currently 0.5.0); the URL path /embed/v1/ is the compatibility line. |
init | init(opts: HiveChatOptions, container?: Element | null): HiveChatHandle | Creates a <hive-chat> (launcher mode: appended to document.body; or into container). Calling it again with the same key — or, in bearer mode without a key, the same basePath + binding — reuses the existing widget and applies the new options. |
HiveChatHandle
| Member | Signature | Description |
|---|---|---|
element | readonly element: HiveChatElement | The underlying custom element — add event listeners here. |
open | open(): void | Open the panel and focus the composer. |
close | close(): void | Close the panel; focus returns to the launcher. |
toggle | toggle(): void | Open or close. |
send | send(text: string): Promise<void> | Send a message as the end user and follow the run until it settles. Ignored while another turn is in flight. |
reset | reset(): void | Forget the current thread and start fresh (also cancels a live stream). |
update | update(opts: Partial<HiveChatOptions>): void | Change options in place — theme/locale/texts re-render without a new session; changing key, apiUrl, basePath, binding, user.token, storage or visitorId re-mints. A new authToken on an otherwise identical config is a token swap (same as setAuthToken()), not a new session. |
setAuthToken | setAuthToken(token: string): void | Bearer mode only: replace the pre-issued token after your host re-issued it. Takes effect on the next request, keeps the thread, and — if the widget had paused on a 401 (hive:auth-expired) — clears the error state and fires hive:auth-resumed. Empty strings and calls before the widget is configured are ignored; it never mints or refreshes a token by itself. |
destroy | destroy(): void | Remove the element and forget the handle. |
const chat = HiveChat.init({ key: 'hive_pk_REPLACE_WITH_YOUR_PUBLISHABLE_KEY' });
chat.open(); // show the panel
await chat.send('Where is my order?'); // send as the visitor, resolves when the run settles
chat.update({ locale: 'ar' }); // re-render in Arabic, same session
chat.reset(); // new thread
chat.destroy(); // remove the elementHiveChatElement
The element registered as hive-chat. Everything on the handle is a thin call into it. Useful when you create elements in markup (Vue, Angular, plain HTML):
| Member | Signature | Description |
|---|---|---|
configure | configure(opts: Partial<HiveChatOptions>): void | Merge options programmatically (what the loader and React wrapper use). Attributes still win over programmatic values. |
open | open(): void | Same as the handle. |
close | close(): void | Same as the handle. |
toggle | toggle(): void | Same as the handle. |
send | send(text: string): Promise<void> | Same as the handle. |
reset | reset(): void | Same as the handle. |
setAuthToken | setAuthToken(token: string): void | Same as the handle (bearer mode). Rewriting the auth-token attribute has the same effect. |
threadId | readonly threadId: string | null | The current thread id (null before the first message). |
<!-- Drive the element directly (no loader) -->
<hive-chat id="support" key="hive_pk_REPLACE_WITH_YOUR_PUBLISHABLE_KEY" api-url="https://HIVE" mode="inline"
style="display:block;height:600px"></hive-chat>
<script type="module">
import 'https://HIVE/embed/v1/hive-embed.mjs';
const el = document.getElementById('support');
el.addEventListener('hive:ready', () => el.send('Hello!'));
console.log(el.threadId); // null until the first message is accepted
</script>Events
All events are CustomEvents dispatched on the <hive-chat> host, bubbles: true and composed: true, so you can also listen on document. The React wrapper maps each to a prop.
| Event | detail | When | React prop |
|---|---|---|---|
hive:ready | { key_id } | Display config loaded (GET /v1/embed/config); the launcher/panel is rendered and usable. No session exists yet. | onReady |
hive:session | { visitor_id, identified } | A session token was minted — on the first open (launcher mode), on mount (inline mode) or on the first send(), whichever comes first. identified is true when a valid identity JWT was presented. | onSession |
hive:open | undefined | The panel opened (launcher click, open(), or the open attribute). | onOpen |
hive:close | undefined | The panel closed (close button, Escape, or close()). | onClose |
hive:message | { role: 'user' | 'agent', text } | A user turn was sent, or an agent reply completed. | onMessage |
hive:attachments | { count, files: [{ name, mime }] } | Selected documents were uploaded and attached to an accepted turn. It exposes safe metadata only, never file bytes. | onAttachments |
hive:error | { code, retry_after } | Config fetch, session or run failure — an API problem code (e.g. EMBED_ORIGIN_DENIED, RATE_LIMITED), a run failure code relayed from the stream’s run.failed event (MODEL_ERROR, BUDGET_EXCEEDED, RETRIES_EXHAUSTED, TOOL_ERROR, … — since 0.2; 0.1 only showed these in the bubble), or a widget code (NETWORK, STREAM_LOST, HTTP_<status>). retry_after (seconds, since 0.2.1) is set only when the API problem carried it — today 429 RATE_LIMITED — and is undefined otherwise; use it for a real "try again in Ns" instead of a fixed number. The widget itself never shows the code to the visitor: its error row is part="error" with the code in data-code (hive-chat::part(error) { display: none } hides it when your page owns the copy). | onError |
hive:auth-expired | { code } | Bearer mode only (authToken): the pre-issued token was refused with a 401 — code is the problem code. The widget shows the error state and ignores send() until setAuthToken() (or a rewritten auth-token attribute) supplies a new token; it never refreshes one itself. | onAuthExpired |
hive:auth-resumed | undefined | Bearer mode only: a replacement token reached a widget paused on hive:auth-expired; the error state clears and the same thread continues (not a re-mint). | onAuthResumed |
hive:status | { status: 'idle' | 'sending' | 'streaming' | 'waiting' | 'error' | 'listening' | 'thinking' | 'speaking' } | Emitted on every conversation-state transition; the first value after hive:ready is idle. idle — no run in flight (composer enabled); sending — the message was accepted, no visible text yet; streaming — the reply is being rendered (first delta, or the final text when deltas are buffered); waiting — run.waiting received (an approval is open on this thread); error — hive:error fired for the current turn (back to idle on the next send or reset()). While a voice session is live (voice option) the status reads listening, thinking or speaking instead and returns to the conversation status when the session ends. Lets a host composer disable itself while a turn is in flight instead of hitting 409 EMBED_RUN_IN_FLIGHT. | onStatus |
hive:voice | { state: 'idle' | 'connecting' | 'listening' | 'thinking' | 'speaking' | 'muted' | 'error' | 'ended', transcript? } | Voice sessions (voice option): every state change of the mic row — connecting while the microphone and socket open, listening / thinking / speaking through a turn, muted, error (a fatal problem; the code arrives on hive:error), ended. transcript is the visitor’s final transcript for the turn in flight once the server has it (also emitted, with the current state, when it arrives). Completed spoken turns additionally fire hive:message for the user and the agent like typed turns. | onVoice |
hive:block | { lang, source, index, message_id } | With blocks.render set to host or both: one event per processed fence of a FINAL agent message (run.completed), in document order. In both mode, a validated full-message chart JSON also emits a synthetic lang: chart event. Never fired for a restored thread. source is untrusted model text: parse and validate it on your page; never inject it as HTML. | onBlock |
hive:auth-expired and hive:auth-resumed only fire in bearer mode (authToken); a widget using a publishable key never dispatches them.
const chat = HiveChat.init({ key: 'hive_pk_REPLACE_WITH_YOUR_PUBLISHABLE_KEY' });
const el = chat.element;
el.addEventListener('hive:ready', (e) => {
console.log('config loaded for key', e.detail.key_id); // no session yet
});
el.addEventListener('hive:session', (e) => {
const { visitor_id, identified } = e.detail; // minted on first open / first send
analytics.identify(visitor_id);
});
el.addEventListener('hive:message', (e) => {
const { role, text } = e.detail; // 'user' | 'agent'
analytics.track('chat_message', { role, length: text.length });
});
el.addEventListener('hive:error', (e) => {
console.warn('hive-chat error', e.detail.code); // e.g. 'RATE_LIMITED'
});
el.addEventListener('hive:open', () => document.body.classList.add('chat-open'));
el.addEventListener('hive:close', () => document.body.classList.remove('chat-open'));// TypeScript: the events are plain CustomEvents — type the detail yourself
import type { HiveChatElement } from '@hive/embed';
type HiveEvents = {
'hive:ready': { key_id: string };
'hive:session': { visitor_id: string; identified: boolean };
'hive:message': { role: 'user' | 'agent'; text: string };
'hive:error': { code: string };
};
function on<K extends keyof HiveEvents>(
el: HiveChatElement, name: K, fn: (detail: HiveEvents[K]) => void,
) {
el.addEventListener(name, (e) => fn((e as CustomEvent<HiveEvents[K]>).detail));
}Error codes in hive:error
detail.code is either a platform problem code (see the REST reference — EMBED_ORIGIN_DENIED, EMBED_KEY_DISABLED, RATE_LIMITED, AGENT_NOT_PUBLISHED…) or one of the widget’s own:
| Code | Meaning |
|---|---|
EMBED_THREAD_REQUIRED_FOR_ATTACHMENT | Documents were selected before the conversation had an accepted first turn and therefore no owned thread existed for a safe upload. |
CONFIG | Invalid init options (bad key format, or apiUrl could not be inferred). Logged as a [hive-embed] console warning and the panel shows the offline notice; this one is internal — no hive:error event is dispatched. |
NETWORK | The fetch itself failed (offline, DNS, CSP connect-src block, mixed content). |
STREAM_LOST | The SSE stream dropped and three reconnect attempts (with ?since) failed. |
HTTP_<status> | The API answered with a non-JSON error body (e.g. a proxy 502). |
A run that ends with the SSE event run.failed renders an error bubble with the run’s failure code and a Retry action; it is a run outcome, not a transport failure, so it does not dispatch hive:error.
Recipes
Open on a button click
<button id="help">Need help?</button>
<script async src="https://HIVE/embed/v1/hive-embed.js" data-key="hive_pk_REPLACE_WITH_YOUR_PUBLISHABLE_KEY"></script>
<script>
// The script is async: a click can land before it has loaded, when
// window.HiveChat is still the stub below and has no init(). The loader
// drains the queue (q: [[options, container?]]) as soon as it runs.
window.HiveChat = window.HiveChat || { q: [] };
document.getElementById('help').addEventListener('click', () => {
if (typeof window.HiveChat.init === 'function') {
// Loaded: init() is idempotent per key and returns the auto-initialized handle.
window.HiveChat.init({ key: 'hive_pk_REPLACE_WITH_YOUR_PUBLISHABLE_KEY' }).open();
} else {
// Not yet: queue an init that opens the panel once the bundle arrives.
window.HiveChat.q.push([{ key: 'hive_pk_REPLACE_WITH_YOUR_PUBLISHABLE_KEY', open: true }]);
}
});
</script>Prefill from page context
// Open with a context-aware first message (sent as the visitor).
const chat = HiveChat.init({ key: 'hive_pk_REPLACE_WITH_YOUR_PUBLISHABLE_KEY' });
document.querySelector('#ask-about-product').addEventListener('click', async () => {
chat.open();
await chat.send(`I have a question about ${document.title} (${location.pathname})`);
});Pass page URL / user metadata
// Pass page URL and user metadata — the safe way.
//
// The widget sends no free-form metadata from the browser (it is untrusted).
// Page context therefore travels either as part of the message text (visible
// to the visitor, above) or — for attributes you vouch for — inside the
// identity JWT your backend signs:
//
// { sub: user.id, name: user.name, meta: { plan: 'pro', region: 'ae', page: '/pricing' } }
//
// meta lands on the contact record (end_users.attributes.embed) and is visible
// to operators in the console; it is not injected into the prompt.
const { token } = await fetch('/api/hive-identity?page=' + encodeURIComponent(location.pathname))
.then((r) => r.json());
HiveChat.init({ key: 'hive_pk_REPLACE_WITH_YOUR_PUBLISHABLE_KEY', user: { token } });Single-page apps, login and logout
// Single-page apps: create once, update on route change, destroy on logout.
let chat;
export function mountChat(user) {
chat = HiveChat.init({
key: 'hive_pk_REPLACE_WITH_YOUR_PUBLISHABLE_KEY',
locale: document.documentElement.lang.startsWith('ar') ? 'ar' : 'en',
user: user ? { token: user.hiveToken } : undefined,
});
}
router.afterEach((to) => {
// e.g. hide the launcher on checkout
chat?.element.toggleAttribute('hidden', to.path.startsWith('/checkout'));
});
export function onLogout() {
chat?.reset(); // drop the identified thread
chat?.update({ user: undefined }); // continue as anonymous …
// … or chat?.destroy() to remove it entirely
}ESM exports
Beyond HiveChat, the ESM build (@hive/embed / /embed/v1/hive-embed.mjs) exports the building blocks, so you can reuse the API client, the Markdown renderer or the SSE parser in a custom UI:
HiveChat, HiveChatElement, TAG, defineHiveChat, setDefaultApiUrl, VERSION, EmbedClient, EmbedApiError, HiveChatConfigError, resolveOptions, resolveBlocks, resolveStrings, sanitizeTheme, mergeTheme, themeToCssVars, resolveLocale, dirFor, STRINGS, renderMarkdown, liftFences, escapeHtml, createSseParser, reduce, initialState
Types: HiveChatOptions, HiveChatHandle, HiveChatApi, EmbedTheme, EmbedDisplayConfig, EmbedPublicConfig, EmbedSession, EmbedEvent, HistoryMessage, ProblemDocument, ChatState, ChatMessage, Strings, SseFrame.