Agentic Workforce ME Developer PortalDocs 1.0 · Widget 0.1.0

Reference

REST reference

The public /v1/embed/* endpoints, SSE event types, error codes, the admin /v1/embed-keys API and the SDK.

Everything the widget does goes through six public endpoints under /v1/embed/ plus the hosted bundle. You only need them if you build a custom UI (a native app, a server-side integration, a different chat surface). Teams automating key management use the admin endpoints under /v1/embed-keys with a platform API key, or @hive/sdk.

Public endpoints

GET/v1/embed/configPublic display config for a key — what a widget needs before a session exists. The widget calls this on connect and fires hive:ready.
Auth
Publishable key in the X-Hive-Embed-Key header + Origin on the allowlist
Rate limit
60/min per IP
Errors
EMBED_KEY_INVALID EMBED_KEY_DISABLED EMBED_ORIGIN_DENIED VALIDATION

Request

FieldTypeDescription
X-Hive-Embed-Keyheader, stringThe publishable key hive_pk_… (a header, never the URL, so it stays out of access logs).

Response

FieldTypeDescription
key_iduuidEmbed key id.
agent{ name, emoji }Agent display name + emoji.
themeEmbedThemeServer theme.
configEmbedDisplayConfigServer display config.
identity_requiredbooleanAnonymous sessions refused when true.
  • No session and no credentials are involved; a page view costs exactly this one call.
  • Browsers omit Origin on same-origin GETs, so a missing header is evaluated as the implied page origin (Referer origin, else the request’s own origin) against the allowlist.
Shell
curl https://console.agenticworkforce.me/v1/embed/config \
  -H "Origin: https://www.acme.com" -H "X-Hive-Embed-Key: $EMBED_KEY"

# → 200
# { "key_id": "…", "agent": { "name": "Docs assistant", "emoji": null },
#   "theme": { "primary": "#4f46e5" }, "config": { "welcome": "Hi!" },
#   "identity_required": false }
POST/v1/embed/sessionExchange the publishable key (+ optional identity JWT) for a 15-minute session token.
Auth
Publishable key in the body + Origin header on the allowlist
Rate limit
30 mints/min per IP, then 10 × the key’s rate_limit_per_min per key
Errors
EMBED_KEY_INVALID EMBED_KEY_DISABLED EMBED_ORIGIN_DENIED EMBED_IDENTITY_REQUIRED EMBED_IDENTITY_INVALID EMBED_IDENTITY_UNAVAILABLE RATE_LIMITED VALIDATION

Request

FieldTypeDescription
keystringPublishable key hive_pk_….
visitor_idstring (8–128 url-safe chars)Stable per-browser visitor id generated by the widget.
identitystring (JWT), optionalHS256 JWT signed with the key’s identity secret. Required when the key has identity_required.

Response

FieldTypeDescription
tokenstringOpaque signed session token — send as Authorization: Bearer.
expires_atISO date-timeToken expiry (15 minutes). The widget re-mints lazily: when < 30 s remain before a call, or after a 401.
ttl_secnumberToken lifetime in seconds (900).
visitor_idstringEcho of the visitor id the token is bound to.
end_user{ id, name } | nullPresent when an identity JWT was accepted.
key_iduuidThe embed key’s id (never the key itself).
agent{ name, emoji }Display name + emoji of the bound agent.
themeEmbedThemeServer-side theme stored on the key.
configEmbedDisplayConfigServer-side display config stored on the key.
identity_requiredbooleanWhether anonymous sessions are refused for this key.
  • The widget mints lazily — on the first open (launcher), on mount (inline) or on the first send() — never on page load.
  • Identified mints (a valid identity JWT) are audited (embed.session, target = the contact); anonymous mints are counted in metrics only.
  • When the key allows * and the caller sent no Origin, the token is origin-less (o: "*"); otherwise it is pinned to the request origin.
Shell
curl -X POST https://console.agenticworkforce.me/v1/embed/session \
  -H "Origin: https://www.acme.com" -H "Content-Type: application/json" \
  -d '{"key":"'"$EMBED_KEY"'","visitor_id":"visitor-9f1c2b3a4d5e"}'

# → 200
# { "token": "…", "expires_at": "2026-09-03T10:15:00.000Z", "ttl_sec": 900,
#   "visitor_id": "visitor-9f1c2b3a4d5e", "end_user": null, "key_id": "…",
#   "agent": { "name": "Docs assistant", "emoji": null },
#   "theme": { "primary": "#4f46e5" }, "config": { "welcome": "Hi!" },
#   "identity_required": false }

# Identified visitor: add the backend-signed JWT
#   -d '{"key":"…","visitor_id":"…","identity":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…"}'
POST/v1/embed/messages202 AcceptedSend an end-user message; creates or continues the visitor’s thread and enqueues a governed run.
Auth
Session token (Authorization: Bearer) + the same Origin the token was minted for
Rate limit
key rate_limit_per_min (default 60) and 20 messages/min per visitor
Errors
EMBED_UNAUTHORIZED EMBED_ORIGIN_MISMATCH EMBED_ORIGIN_DENIED EMBED_KEY_DISABLED EMBED_RUN_IN_FLIGHT EMBED_THREAD_FULL RATE_LIMITED AGENT_NOT_PUBLISHED NOT_FOUND VALIDATION

Request

FieldTypeDescription
contentstring (1–8000 chars)The user message (trimmed).
thread_iduuid, optionalContinue an existing thread owned by this visitor; omit to start a new one.

Response

FieldTypeDescription
thread_iduuidThe thread (new or continued).
run_iduuidThe queued run — follow it on the events endpoint.
message_iduuidThe stored user message.
  • Thread ownership: threads.created_by = embed:eu:<end_user_id> (identified) or embed:vid:<visitor_id>; a foreign thread_id is a 404, never a 403.
  • The run is a normal chat run with input._origin = { embed_key_id, visitor_id, end_user_id? } — the console shows origin "Embedded widget".
  • One reply at a time per thread: a message while the previous run is queued/running is a 409 EMBED_RUN_IN_FLIGHT (with the run_id); a thread holds at most 200 messages (409 EMBED_THREAD_FULL).
Shell
curl -X POST https://console.agenticworkforce.me/v1/embed/messages \
  -H "Origin: https://www.acme.com" -H "Authorization: Bearer $SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"content":"How do I embed the widget in Next.js?"}'

# → 202
# { "thread_id": "…", "run_id": "…", "message_id": "…" }
GET/v1/embed/threads/:id/messagesRestore the visitor’s own thread (newest 100 messages, oldest first).
Auth
Session token + matching Origin
Rate limit
per-visitor read bucket: 60/min shared by history, cancel and event streams
Errors
EMBED_UNAUTHORIZED EMBED_ORIGIN_MISMATCH EMBED_ORIGIN_DENIED EMBED_KEY_DISABLED RATE_LIMITED NOT_FOUND VALIDATION

Response

FieldTypeDescription
thread_iduuidThe thread.
items[].iduuidMessage id.
items[].role'user' | 'agent'Only user/agent turns are returned (no system/tool rows).
items[].textstringPlain text of the message.
items[].run_iduuid | nullRun that produced an agent message.
items[].created_atISO date-timeCreation time.
Shell
curl "https://console.agenticworkforce.me/v1/embed/threads/$THREAD_ID/messages" \
  -H "Origin: https://www.acme.com" -H "Authorization: Bearer $SESSION_TOKEN"

# → 200
# { "thread_id": "…", "items": [
#   { "id": "…", "role": "user",  "text": "How do I …", "run_id": null, "created_at": "…" },
#   { "id": "…", "role": "agent", "text": "Use the React wrapper …", "run_id": "…", "created_at": "…" } ] }
GET/v1/embed/runs/:id/eventsServer-Sent Events relay of one run (redacted vocabulary), with ?since= backfill and a 15 s heartbeat.
Auth
Session token + matching Origin; Accept: text/event-stream
Rate limit
per-visitor read bucket (60/min) + at most 2 live streams per visitor and key (429 EMBED_TOO_MANY_STREAMS, Retry-After: 5); finished runs are served from the backfill without taking a slot
Errors
EMBED_UNAUTHORIZED EMBED_ORIGIN_MISMATCH EMBED_ORIGIN_DENIED EMBED_KEY_DISABLED EMBED_TOO_MANY_STREAMS RATE_LIMITED NOT_FOUND VALIDATION

Request

FieldTypeDescription
sincequery, integer, optionalHighest step idx already seen (from llm.delta / tool.* payloads); the server backfills persisted steps after it, then streams live.
  • event: is the event type; data: is the JSON payload. The stream ends after run.completed or run.failed.
  • Use fetch + ReadableStream (the token travels in the Authorization header; EventSource cannot set headers and is unsupported).
  • When the agent has output guardrails, llm.delta is withheld and the final text arrives only in run.completed.
Shell
curl -N "https://console.agenticworkforce.me/v1/embed/runs/$RUN_ID/events?since=-1" \
  -H "Origin: https://www.acme.com" -H "Authorization: Bearer $SESSION_TOKEN" \
  -H "Accept: text/event-stream"

# event: run.started
# data: {"type":"run.started","run_id":"…","status":"running"}
#
# event: tool.called
# data: {"type":"tool.called","idx":1}
#
# event: tool.result
# data: {"type":"tool.result","idx":1,"ok":true}
#
# event: llm.delta
# data: {"type":"llm.delta","idx":2,"text":"Use the "}
#
# event: llm.delta
# data: {"type":"llm.delta","idx":2,"text":"React wrapper…"}
#
# event: run.completed
# data: {"type":"run.completed","status":"succeeded","text":"Use the React wrapper…"}
POST/v1/embed/runs/:id/cancel202 AcceptedCancel the visitor’s own in-flight run (best effort).
Auth
Session token + matching Origin
Rate limit
per-visitor read bucket (60/min)
Errors
EMBED_UNAUTHORIZED EMBED_ORIGIN_MISMATCH EMBED_ORIGIN_DENIED EMBED_KEY_DISABLED RATE_LIMITED NOT_FOUND VALIDATION

Response

FieldTypeDescription
run_iduuidThe run.
statusstringRun status at the time of the request.
Shell
curl -X POST https://console.agenticworkforce.me/v1/embed/runs/$RUN_ID/cancel \
  -H "Origin: https://www.acme.com" -H "Authorization: Bearer $SESSION_TOKEN"

# → 202  { "run_id": "…", "status": "running" }
OPTIONS/v1/embed/*204 No ContentCORS preflight. Reflects the request origin; enforcement happens on the actual request (fail-closed: no Access-Control-Allow-Origin on denial).
Auth
none
Shell
curl -i -X OPTIONS https://console.agenticworkforce.me/v1/embed/session \
  -H "Origin: https://www.acme.com" -H "Access-Control-Request-Method: POST" \
  -H "Access-Control-Request-Headers: content-type"

# HTTP/1.1 204 No Content
# access-control-allow-origin: https://www.acme.com
# access-control-allow-methods: GET, POST, OPTIONS
# access-control-allow-headers: authorization, content-type, x-hive-embed-key
# access-control-max-age: 600
# vary: origin
GET/embed/v1/:assetThe hosted widget bundle: hive-embed.js (script tag, IIFE), hive-embed.mjs (ESM) and react.mjs (React wrapper; react stays external).
Auth
none (public, Access-Control-Allow-Origin: *)
Errors
NOT_FOUND EMBED_BUNDLE_UNAVAILABLE
  • Cache-Control: public, max-age=3600, stale-while-revalidate=86400, ETag (304 on If-None-Match), X-Content-Type-Options: nosniff.
  • /embed/v1/ is the major line; patches update it in place; breaking changes ship at /embed/v2/.
Shell
curl -I https://console.agenticworkforce.me/embed/v1/hive-embed.js

# HTTP/1.1 200 OK
# content-type: application/javascript; charset=utf-8
# cache-control: public, max-age=3600, stale-while-revalidate=86400
# etag: "…"
# access-control-allow-origin: *
# x-content-type-options: nosniff

SSE event types

GET /v1/embed/runs/:id/events relays a redacted subset of the platform’s run events. Each frame is event: <type> + data: <JSON>; the JSON always repeats type.

typedataMeaning
run.started{ run_id, status }The worker picked the run up.
llm.delta{ idx, text }A streamed chunk of the assistant reply — append to the pending bubble. idx is the step counter the chunk belongs to (see ?since). Withheld when the agent has output guardrails: then only the final text arrives in run.completed.
tool.called{ idx }A tool is running — no name, no arguments. The widget shows a generic "Working…" pill.
tool.result{ idx, ok }The tool finished — success flag only (no output).
run.waiting{}Parked for a human approval (HITL). The widget shows "Waiting for a human…". Decided in the console, never in the widget.
run.resumed{}The approval was decided and the run continues.
run.completed{ status, text }Terminal. text is the final assistant text when present (null otherwise). The stream closes.
run.failed{ code }Terminal. A stable code only — no internal message. The widget shows an error bubble with Retry.
heartbeat{}Every 15 s while the run is live; keeps proxies from closing the stream.

Not relayed — by design, so a widget never sees internals: step.started, llm.thinking, retrieval.result, node.started, node.completed, handoff, plus every payload field beyond the ones listed (no tool names, arguments or output, no prompts, no token or cost figures, no step metadata). idx on step-bound events is the persisted step index — pass the last one you saw as ?since= when reconnecting.

Consuming the stream

TypeScript
// Reading the relay with fetch (what the widget does). EventSource cannot send
// the Authorization header, so it is not supported.
const res = await fetch(`${apiUrl}/v1/embed/runs/${runId}/events?since=-1`, {
  headers: { authorization: `Bearer ${token}`, accept: 'text/event-stream' },
  signal,
});
const reader = res.body!.getReader();
const decoder = new TextDecoder();
let buffer = '';
for (;;) {
  const { value, done } = await reader.read();
  if (done) break;
  buffer += decoder.decode(value, { stream: true });
  let idx;
  while ((idx = buffer.indexOf('\n\n')) !== -1) {
    const frame = buffer.slice(0, idx); buffer = buffer.slice(idx + 2);
    const data = frame.split('\n').find((l) => l.startsWith('data:'))?.slice(5).trim();
    if (data) handle(JSON.parse(data)); // { type: 'llm.delta', text } …
  }
}
// Or: import { createSseParser } from '@hive/embed' and feed it chunks.

Reconnect with ?since=<last step idx> to backfill what you missed; the widget retries three times with exponential backoff before reporting STREAM_LOST. A run that is already terminal returns its backfill and closes immediately.

Error codes

HTTP
HTTP/1.1 403 Forbidden
Content-Type: application/problem+json

{
  "type": "about:blank",
  "title": "Origin not allowed for this embed key",
  "status": 403,
  "code": "EMBED_ORIGIN_DENIED",
  "detail": "Add https://evil.example to the embed key's allowed origins."
}
CodeStatusWhereMeaningWhat to do
EMBED_KEY_INVALID401publicUnknown publishable key — it was never issued, was rotated, or was revoked.Copy the current key from the console (Agent → Embed). After a rotation, redeploy the new key.
EMBED_KEY_DISABLED401publicThe key exists but enabled is false (paused) or it is revoked.Re-enable the key in the console, or create a new one.
EMBED_ORIGIN_DENIED403publicThe request Origin is not on the key’s allowlist (or a non-browser client sent no Origin and the key does not allow *). No Access-Control-Allow-Origin header is sent, so browsers report a CORS error.Add the exact origin (https://host[:port], no path) to the key’s allowed origins. Local dev: http://localhost:5173 and http://127.0.0.1:5173 are different origins.
EMBED_ORIGIN_MISMATCH403publicA session token is being used from a different origin than it was minted for.Do not share tokens across origins; let each page mint its own session.
EMBED_UNAUTHORIZED401publicMissing, malformed or expired session token (a key cannot be rebound to another agent after creation).Mint a new session (POST /v1/embed/session). The widget does this automatically on 401.
EMBED_IDENTITY_REQUIRED401publicThe key has identity_required and the session request carried no identity JWT.Mint an identity JWT on your backend for signed-in users and pass it as user: { token }.
EMBED_IDENTITY_INVALID401publicThe identity JWT was rejected; reason is one of malformed, alg, signature, expired, claims, exp_too_far, iat_in_future (iat more than 5 min ahead), not_yet_valid (nbf more than 60 s ahead).Sign with HS256 and the key’s CURRENT identity secret; include sub and an exp at most 24 h ahead; keep server clocks in sync; keep meta ≤ 16 keys.
EMBED_RUN_IN_FLIGHT409publicA second message was posted to a thread whose previous run is still queued or running; run_id names the run in flight.Wait for run.completed / run.failed (or cancel the run) before sending the next message. The widget disables the composer while a reply streams.
EMBED_THREAD_FULL409publicThe thread holds the maximum of 200 messages (user + agent).Start a new thread: post the next message without thread_id (the widget offers Reset).
EMBED_TOO_MANY_STREAMS429publicThis visitor already holds the maximum of 2 live event streams for the key; Retry-After: 5 is set. Finished runs are always served from the backfill without taking a slot.Abort streams you no longer read (AbortController.abort() on the fetch; EventSource cannot be used here) — one open stream per tab is enough.
EMBED_IDENTITY_UNAVAILABLE401publicAn identity JWT was sent but the key has no identity secret configured.Create the key with with_identity_secret: true or issue a secret via POST /v1/embed-keys/:id/identity-secret.
EMBED_BUNDLE_UNAVAILABLE503publicThe API is running but the widget bundle was not built into the deployment.Operators: run pnpm --filter @hive/embed build and restart the API (the API image does this automatically).
RATE_LIMITED429bothA token bucket is empty (per IP on session mint, per key or per visitor on messages). Retry-After and retry_after (seconds) are set.Back off for retry_after seconds. Raise the key’s rate_limit_per_min if the traffic is legitimate.
AGENT_NOT_PUBLISHED409publicThe bound agent has no published version yet.Publish the agent in the console (Agent → Versions) before embedding it.
NOT_FOUND404bothUnknown id — or a thread/run that belongs to a different visitor (existence is never revealed).Forget the stored thread_id and start a new thread (the widget does this on 404).
NAME_TAKEN409adminA live embed key with this name already exists for the agent.Pick another name or revoke the old key first.
VALIDATION400bothThe body or query failed schema validation; detail lists path: message pairs.Compare with the request tables on this page (content 1–8000 chars, origins without paths, hex colors, …).

Admin endpoints — /v1/embed-keys

Tenant-admin operations on embed keys. Authenticate with a platform API key (Authorization: Bearer hive_…) and the tenant (X-Tenant-Id); see API keys. The console’s Embed tab uses exactly these routes. Every mutation is audited.

GET/v1/embed-keysList embed keys ({ items: EmbedKey[] }) — never the hash or the identity secret.
Auth
API key (Authorization: Bearer hive_…) + X-Tenant-Id, tenant admin
Errors
VALIDATION

Request

FieldTypeDescription
agent_idquery, uuid, optionalFilter by agent.
include_revokedquery, boolean, optionalInclude revoked keys (default live only).

Response

FieldTypeDescription
iduuidKey id (stable across rotations).
agent_iduuidThe one agent this key can talk to.
namestringDisplay label, unique per agent among live keys.
key_prefixstringhive_pk_ab12cd34… — the only part of the key ever shown again.
allowed_originsstring[]Normalized origins (https://app.acme.com); * = any.
themeEmbedThemeServer-side theme.
configEmbedDisplayConfigServer-side display config.
identity_requiredbooleanRefuse anonymous sessions.
has_identity_secretbooleanWhether an identity secret exists (the secret itself is never returned).
rate_limit_per_mininteger 1–10000Messages per minute across all visitors of the key (default 60).
enabledbooleanKill switch; takes effect on the next request, sessions included.
last_used_atISO date-time | nullBumped on session mint (throttled to once a minute).
revoked_atISO date-time | nullSet by revoke; revoked rows are kept for audit.
created_at / updated_atISO date-timeTimestamps.
Shell
curl "https://console.agenticworkforce.me/v1/embed-keys?agent_id=$AGENT_ID" \
  -H "Authorization: Bearer $HIVE_API_KEY" -H "X-Tenant-Id: $TENANT_ID"
POST/v1/embed-keys201 CreatedCreate a key. Returns the plaintext key (and identity_secret when requested) exactly once.
Auth
API key + X-Tenant-Id, tenant admin
Errors
NOT_FOUND NAME_TAKEN VALIDATION

Request

FieldTypeDescription
agent_iduuidAgent to bind (must exist in the tenant).
namestring 1–80Display label.
allowed_originsstring[] ≤ 50Origins like https://app.acme.com (no path), or *. Default [] = nothing allowed.
themeEmbedTheme, optionalServer-side theme.
configEmbedDisplayConfig, optionalServer-side display config.
identity_requiredboolean, default falseRefuse anonymous sessions.
with_identity_secretboolean, default falseAlso mint an identity secret (returned once).
rate_limit_per_mininteger 1–10000, default 60Per-key message budget.
enabledboolean, default trueKill switch.

Response

FieldTypeDescription
iduuidKey id (stable across rotations).
agent_iduuidThe one agent this key can talk to.
namestringDisplay label, unique per agent among live keys.
key_prefixstringhive_pk_ab12cd34… — the only part of the key ever shown again.
allowed_originsstring[]Normalized origins (https://app.acme.com); * = any.
themeEmbedThemeServer-side theme.
configEmbedDisplayConfigServer-side display config.
identity_requiredbooleanRefuse anonymous sessions.
has_identity_secretbooleanWhether an identity secret exists (the secret itself is never returned).
rate_limit_per_mininteger 1–10000Messages per minute across all visitors of the key (default 60).
enabledbooleanKill switch; takes effect on the next request, sessions included.
last_used_atISO date-time | nullBumped on session mint (throttled to once a minute).
revoked_atISO date-time | nullSet by revoke; revoked rows are kept for audit.
created_at / updated_atISO date-timeTimestamps.
keystringThe publishable key — shown once.
identity_secretstring, optionalHS256 secret for identity JWTs — shown once, only with with_identity_secret.
Shell
curl -X POST https://console.agenticworkforce.me/v1/embed-keys \
  -H "Authorization: Bearer $HIVE_API_KEY" -H "X-Tenant-Id: $TENANT_ID" \
  -H "Content-Type: application/json" \
  -d '{
    "agent_id": "'"$AGENT_ID"'",
    "name": "Marketing site",
    "allowed_origins": ["https://www.acme.com", "http://localhost:5173"],
    "theme": { "primary": "#0f766e", "radius": 14 },
    "config": { "welcome": "Hi! How can we help?", "locale": "auto" },
    "with_identity_secret": true,
    "rate_limit_per_min": 120
  }'

# → 201  { …EmbedKey, "key": "hive_pk_…", "identity_secret": "…" }   (both shown once)
GET/v1/embed-keys/:idRead one key.
Auth
API key + X-Tenant-Id, tenant admin
Errors
NOT_FOUND VALIDATION

Response

FieldTypeDescription
iduuidKey id (stable across rotations).
agent_iduuidThe one agent this key can talk to.
namestringDisplay label, unique per agent among live keys.
key_prefixstringhive_pk_ab12cd34… — the only part of the key ever shown again.
allowed_originsstring[]Normalized origins (https://app.acme.com); * = any.
themeEmbedThemeServer-side theme.
configEmbedDisplayConfigServer-side display config.
identity_requiredbooleanRefuse anonymous sessions.
has_identity_secretbooleanWhether an identity secret exists (the secret itself is never returned).
rate_limit_per_mininteger 1–10000Messages per minute across all visitors of the key (default 60).
enabledbooleanKill switch; takes effect on the next request, sessions included.
last_used_atISO date-time | nullBumped on session mint (throttled to once a minute).
revoked_atISO date-time | nullSet by revoke; revoked rows are kept for audit.
created_at / updated_atISO date-timeTimestamps.
Shell
curl https://console.agenticworkforce.me/v1/embed-keys/$KEY_ID \
  -H "Authorization: Bearer $HIVE_API_KEY" -H "X-Tenant-Id: $TENANT_ID"
PATCH/v1/embed-keys/:idUpdate name, origins, theme, config, identity_required, rate limit or enabled (at least one field).
Auth
API key + X-Tenant-Id, tenant admin
Errors
NOT_FOUND VALIDATION

Request

FieldTypeDescription
namestring, optional
allowed_originsstring[], optionalReplaces the whole list.
themeEmbedTheme, optionalReplaces the whole theme.
configEmbedDisplayConfig, optionalReplaces the whole config.
identity_requiredboolean, optional
rate_limit_per_mininteger, optional
enabledboolean, optionalPause without revoking.

Response

FieldTypeDescription
iduuidKey id (stable across rotations).
agent_iduuidThe one agent this key can talk to.
namestringDisplay label, unique per agent among live keys.
key_prefixstringhive_pk_ab12cd34… — the only part of the key ever shown again.
allowed_originsstring[]Normalized origins (https://app.acme.com); * = any.
themeEmbedThemeServer-side theme.
configEmbedDisplayConfigServer-side display config.
identity_requiredbooleanRefuse anonymous sessions.
has_identity_secretbooleanWhether an identity secret exists (the secret itself is never returned).
rate_limit_per_mininteger 1–10000Messages per minute across all visitors of the key (default 60).
enabledbooleanKill switch; takes effect on the next request, sessions included.
last_used_atISO date-time | nullBumped on session mint (throttled to once a minute).
revoked_atISO date-time | nullSet by revoke; revoked rows are kept for audit.
created_at / updated_atISO date-timeTimestamps.
Shell
curl -X PATCH https://console.agenticworkforce.me/v1/embed-keys/$KEY_ID \
  -H "Authorization: Bearer $HIVE_API_KEY" -H "X-Tenant-Id: $TENANT_ID" \
  -H "Content-Type: application/json" \
  -d '{"allowed_origins": ["https://www.acme.com", "https://help.acme.com"], "enabled": true}'
POST/v1/embed-keys/:id/rotateIssue a new publishable key. The old one stops matching immediately; live sessions (bound to the key id) keep working.
Auth
API key + X-Tenant-Id, tenant admin
Errors
NOT_FOUND VALIDATION

Response

FieldTypeDescription
iduuidKey id (stable across rotations).
agent_iduuidThe one agent this key can talk to.
namestringDisplay label, unique per agent among live keys.
key_prefixstringhive_pk_ab12cd34… — the only part of the key ever shown again.
allowed_originsstring[]Normalized origins (https://app.acme.com); * = any.
themeEmbedThemeServer-side theme.
configEmbedDisplayConfigServer-side display config.
identity_requiredbooleanRefuse anonymous sessions.
has_identity_secretbooleanWhether an identity secret exists (the secret itself is never returned).
rate_limit_per_mininteger 1–10000Messages per minute across all visitors of the key (default 60).
enabledbooleanKill switch; takes effect on the next request, sessions included.
last_used_atISO date-time | nullBumped on session mint (throttled to once a minute).
revoked_atISO date-time | nullSet by revoke; revoked rows are kept for audit.
created_at / updated_atISO date-timeTimestamps.
keystringThe new publishable key — shown once.
Shell
curl -X POST https://console.agenticworkforce.me/v1/embed-keys/$KEY_ID/rotate \
  -H "Authorization: Bearer $HIVE_API_KEY" -H "X-Tenant-Id: $TENANT_ID"

# → 200  { …EmbedKey, "key": "hive_pk_NEW…" }
POST/v1/embed-keys/:id/identity-secret(Re)generate the HS256 identity secret. JWTs signed with the old secret stop verifying immediately.
Auth
API key + X-Tenant-Id, tenant admin
Errors
NOT_FOUND VALIDATION

Response

FieldTypeDescription
iduuidKey id.
identity_secretstringThe new secret — shown once. Store it server-side only.
Shell
curl -X POST https://console.agenticworkforce.me/v1/embed-keys/$KEY_ID/identity-secret \
  -H "Authorization: Bearer $HIVE_API_KEY" -H "X-Tenant-Id: $TENANT_ID"

# → 200  { "id": "…", "identity_secret": "…" }
DELETE/v1/embed-keys/:id204 No ContentRevoke (soft delete). Every widget and session using the key stops on its next request.
Auth
API key + X-Tenant-Id, tenant admin
Errors
NOT_FOUND VALIDATION
Shell
curl -X DELETE https://console.agenticworkforce.me/v1/embed-keys/$KEY_ID \
  -H "Authorization: Bearer $HIVE_API_KEY" -H "X-Tenant-Id: $TENANT_ID"

# → 204

With @hive/sdk

The TypeScript SDK wraps the admin endpoints as client.embedKeys:

MethodCallsReturns
list({ agentId?, includeRevoked? })GET /v1/embed-keys{ items: EmbedKey[] }
create(input)POST /v1/embed-keysEmbedKeyCreated (with key, identity_secret?)
get(id)GET /v1/embed-keys/:idEmbedKey
update(id, input)PATCH /v1/embed-keys/:idEmbedKey
rotate(id)POST /v1/embed-keys/:id/rotateEmbedKeyCreated (new key)
rotateIdentitySecret(id)POST /v1/embed-keys/:id/identity-secret{ id, identity_secret }
revoke(id)DELETE /v1/embed-keys/:idvoid
provision.tsTypeScript
import { HiveClient } from '@hive/sdk';

const hive = new HiveClient({
  baseUrl: 'https://console.agenticworkforce.me',
  apiKey: process.env.HIVE_API_KEY!,      // hive_… from the console (API keys)
  tenantId: process.env.HIVE_TENANT_ID!,
});

// Create a key for a new customer site
const created = await hive.embedKeys.create({
  agent_id: AGENT_ID,
  name: 'Customer: Acme',
  allowed_origins: ['https://www.acme.com'],
  with_identity_secret: true,
});
console.log(created.key);              // hive_pk_… — hand to the customer once
console.log(created.identity_secret);  // store in THEIR backend secrets, never in a browser

// Rotate on schedule
const rotated = await hive.embedKeys.rotate(created.id);

// Pause / resume
await hive.embedKeys.update(created.id, { enabled: false });

// Clean up
await hive.embedKeys.revoke(created.id);