Agentic Workforce ME Developer PortalDocs 1.0 · Widget 0.1.0

Help

FAQ & troubleshooting

Common errors, CSP and CORS questions, RTL, persistence, HITL and limits.

Start with the verification checklist if the widget does not behave; the entries below cover the questions that come up most.

The widget does not appear at all.

Open the browser console. A 503 EMBED_BUNDLE_UNAVAILABLE on the script URL means the operator deployed the API without building the bundle. A blocked request means your page’s Content-Security-Policy is missing script-src https://HIVE (see Authentication & security).

If the script loads but nothing renders, check for a [hive-embed] warning: key must be a publishable embed key (hive_pk_…) means the key is truncated or malformed; apiUrl is required means you bundled the ESM build without passing apiUrl.

The console shows a CORS error / 403 EMBED_ORIGIN_DENIED.

The page origin is not on the key’s allowlist. Origins are exact: scheme, host and port all matter, and there is no path. http://localhost:5173 and http://127.0.0.1:5173 are two different origins; https://www.acme.com and https://acme.com too.

Add the origin in the console (Agent → Embed → the key → Allowed origins) or via PATCH /v1/embed-keys/:id. The change applies to the next request; no redeploy needed — but a page whose widget already failed must be reloaded (a widget stays in its offline state after a setup error; automatic recovery is planned for v1.1). An empty allowlist denies everything; * allows any origin.

I get 401 EMBED_KEY_INVALID or EMBED_KEY_DISABLED.

EMBED_KEY_INVALID: the key does not exist anymore — it was rotated (the old value stops matching immediately) or revoked. Paste the current key from the console.

EMBED_KEY_DISABLED: the key is paused (enabled: false). Re-enable it in the console — no redeploy or snippet change is needed. A page that already showed the offline notice picks the key up again on its next load.

What CSP directives do I need?

Only two: script-src https://HIVE and connect-src https://HIVE, where HIVE is the origin in your snippet. Styles are installed with a constructed stylesheet (adoptedStyleSheets), which style-src does not govern — no unsafe-inline needed on current browsers.

Safari older than 16.4 falls back to a <style> element inside the shadow root; if you must support it under a strict style-src, add unsafe-inline (or a hash) there. No frame-src, no cookies, no third-party origins.

Messages send but no reply arrives.

The reply streams over Server-Sent Events from GET /v1/embed/runs/:id/events. Corporate proxies or CDNs that buffer responses can hold the stream; the API sends X-Accel-Buffering: no and a heartbeat every 15 s, and the widget reconnects up to three times with ?since=. If your own reverse proxy sits in front of the platform origin, disable response buffering for /v1/embed/.

A 409 AGENT_NOT_PUBLISHED means the agent bound to the key has no published version yet — publish it in the console.

A "Waiting for a human…" status means the agent hit a human-in-the-loop approval; a console user must decide it (Approvals page). The widget resumes automatically.

What happens when we hit a rate limit?

The API answers 429 RATE_LIMITED with Retry-After (seconds). Budgets: 60 config reads and 30 session mints per minute per IP, session mints per key at 10 × rate_limit_per_min, the key’s rate_limit_per_min (default 60) for messages across all visitors, 20 messages and 60 reads (history, cancel, stream opens) per minute per visitor, and at most 2 concurrent event streams per visitor (429 EMBED_TOO_MANY_STREAMS). The widget shows a retryable error bubble labelled RATE_LIMITED.

Raise rate_limit_per_min on the key for legitimate traffic (up to 10,000). The per-visitor, per-IP and stream limits are fixed platform guards. Two related 409s are not rate limits: EMBED_RUN_IN_FLIGHT (a reply is still streaming in this thread) and EMBED_THREAD_FULL (200 messages — start a new conversation).

How do I ship the widget in Arabic / RTL?

Set locale: "ar" (or data-locale="ar"). The whole panel flips to dir="rtl", all UI strings switch to Arabic, and position stays logical (end = left in RTL). With locale: "auto" the widget follows <html lang> first, then the browser language.

Message bubbles use dir="auto", so an English answer inside an Arabic widget (or vice versa) renders in its own direction. Your welcome text and titles are free text in any language.

Where is conversation state stored? How do I reset it?

By default the visitor id and current thread id live in localStorage under hive-embed:<key prefix>:visitor and …:thread. Use storage: "session" to scope to a tab, or storage: "none" to keep everything in memory. The browser never holds transcripts — history is fetched from the API on reload.

Call handle.reset() (or the panel’s new-conversation button) to start a fresh thread. A stored thread that no longer belongs to the visitor (e.g. after rotating the key) is silently forgotten on a 404.

How do I attach the chat to a logged-in user?

Create the key with an identity secret, sign a short HS256 JWT on your backend (sub, name, email, meta, exp ≤ 24 h) and pass it as user: { token }. The platform upserts a contact (end_users) and scopes threads and memory to it. Never ship the secret to the browser. Full walkthrough with Node, Next.js, Python, .NET and Java samples on Authentication & security.

Is @hive/embed on npm?

Not yet on the public registry. Use the hosted script tag (/embed/v1/hive-embed.js), import the hosted ESM build by URL (/embed/v1/hive-embed.mjs, /embed/v1/react.mjs), or vendor those files into your repository. The API is identical, so switching to npm install @hive/embed later is a one-line import change. Ask your platform contact for a tarball or registry access.

Why do the SDK identifiers say hive?

Hive is the platform’s internal codename; the product is Agentic Workforce ME. Identifiers (@hive/embed, HiveChat, hive_pk_, X-Hive-Embed-Key) are stable and will not be renamed — the same goes for the <hive-chat> element, the hive:* events, the /embed/v1/hive-embed.js bundle path and the HIVE_* environment variables. Nothing you ship needs to change if the product name changes again.

Can I put two widgets on one page?

Yes — one <hive-chat> per key. HiveChat.init() de-duplicates by key: calling it twice with the same key updates the existing widget instead of creating a second one. Two different keys give two independent widgets (each with its own session, thread and storage namespace).

Can visitors upload files or use voice?

Not in v1 — the embed path is text only. File uploads and voice exist on the console/Workspace path and are v2 candidates for the widget (POST /v1/embed/attachments). Human handoff / CSAT for embed threads are also v2 (they are threads, not contact-center conversations).

Can I consume the SSE endpoint with EventSource?

No. EventSource cannot send an Authorization header and the API deliberately does not accept tokens in the query string. Read the stream with fetch + ReadableStream (the widget’s own EmbedClient does this), or reuse createSseParser from the ESM build.

How is widget usage attributed and billed?

Every message becomes a normal governed run on the bound agent, so usage events, cost, guardrails and audit apply unchanged. The run carries _origin.embed_key_id and the console lists it with the origin "Embedded widget". Session mints are audited as embed.session (one row per mint, not per message).