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.1.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 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, user.token, storage or visitorId re-mints. |
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. |
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:error | { code } | Config fetch, session or run failure — an API problem code (e.g. EMBED_ORIGIN_DENIED, RATE_LIMITED), or a widget code (NETWORK, STREAM_LOST, HTTP_<status>). | onError |
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 |
|---|---|
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, sanitizeTheme, mergeTheme, themeToCssVars, resolveLocale, dirFor, STRINGS, renderMarkdown, escapeHtml, createSseParser, reduce, initialState
Types: HiveChatOptions, HiveChatHandle, HiveChatApi, EmbedTheme, EmbedDisplayConfig, EmbedPublicConfig, EmbedSession, EmbedEvent, HistoryMessage, ProblemDocument, ChatState, ChatMessage, Strings, SseFrame.