Skip to main content

API Channel Reference

The API channel is a REST API for talking to your AI agents over HTTPS: create a conversation, send one message at a time, and receive the agent's reply synchronously or as a server-sent event stream. Authentication requires an API key, created in Dashboard → Settings → API Keys, sent as Authorization: Bearer be_xxxx. Conversation history is managed server-side, so each request carries only the new message.

Download OpenAPI spec (OpenAPI 3.1)

Overview

The API channel lets external systems converse with a company agent over HTTP. The agent behaves exactly as it does on the call, chat, and email channels: same configuration, same system prompt, and its tools execute on the server as part of handling each request.

The API is stateful: conversation history is maintained on the platform. Each request carries only the new message and a conversation ID — the server reconstructs the full context, runs the agent, and returns the reply. Do not include previous messages in requests; history is managed for you.

  • Base URL: https://ai.binaryelements.com
  • All bodies are JSON (Content-Type: application/json).
  • All timestamps are ISO 8601.

Authentication

Every request needs a per-company API key, created in Dashboard → Settings → API Keys. The raw key is shown exactly once at creation; only its SHA-256 hash is stored. Keys can be revoked at any time and revocation takes effect on the next request (no caching).

Authorization: Bearer be_xxxx

The key identifies your company server-side — you never pass a companyId. Requests for agents or conversations belonging to another company fail with 404/403.

Getting your API key

  1. Sign in to the dashboard with your company account.
  2. Go to Settings → API Keys.
  3. Click Create API Key, give the key a name (e.g. "Production integration"), and copy the key immediately — it is shown only once. Only a SHA-256 hash is stored server-side, so it cannot be recovered later.
  4. Pass it on every request as Authorization: Bearer be_xxxx.
  5. Keys can be revoked at any time from the same page; revocation is immediate — any integration using a revoked key stops working on its next request.

Treat API keys like passwords: store them in a secrets manager, never commit them to source control, and create separate keys per integration so you can revoke them independently.

Quickstart

BASE="https://ai.binaryelements.com"
KEY="be_xxxx"   # from Dashboard → Settings → API Keys

# 1. Start a conversation
CONV=$(curl -s -X POST "$BASE/api/v1/conversations" \
  -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -d '{"agentId":"9f2b6c1e-1111-4111-8111-111111111111"}' | jq -r .conversationId)

# 2. Send a message (single message only — the server holds the history)
curl -s -X POST "$BASE/api/v1/conversations/$CONV/messages" \
  -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -d '{"message":"Hi, where is my order 42?"}' | jq .

# 3. Continue the conversation — send only the new message
curl -s -X POST "$BASE/api/v1/conversations/$CONV/messages" \
  -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -d '{"message":"And when will it arrive?"}' | jq .

# 4. Fetch the conversation + history
curl -s "$BASE/api/v1/conversations/$CONV" -H "Authorization: Bearer $KEY" | jq .

# 5. End the conversation
curl -s -X POST "$BASE/api/v1/conversations/$CONV/end" -H "Authorization: Bearer $KEY" | jq .

Create a conversation

POST/api/v1/conversations
FieldTypeRequiredNotes
agentIdstring (UUID)create: yesRequired when minting; optional on resume
conversationIdstring (UUID)noWhen set, resumes that conversation (200) instead of minting (201)
contactIdintegernoBind an existing company contact; cross-tenant ids return 404
contactobjectnoHints: phone, email, externalUserId, optional name. Lookup-first; creates when a stable identifier is new
customSystemMessagestring (1–8000)noApplied to every turn of this conversation; additive to the agent's configured prompt
metadataobjectnoStructured context the agent sees on every turn (see below)

Response 201 (create) / 200 (resume) — always includes contactId (may be null when anonymous):

{
  "conversationId": "a1b2c3d4-…",
  "contactId": null,
  "agentId": "9f2b6c1e-…",
  "createdAt": "2026-06-12T08:00:00.000Z"
}

Conversation metadata

Any metadata object you supply is injected into the agent's context as a system message on every turn, so the model can personalise replies and pull values (IDs, account tier, cart state…) straight into tool calls instead of asking the user:

CONVERSATION METADATA:
{
  "username": "john_doe",
  "accountId": "ACC-12345",
  "accountTier": "premium"
}

Keep it reasonably small (≈2 KB) — it is re-sent on every turn. Use PATCH /api/v1/conversations/:id/metadata to update it as your app's state changes.

Contact identity

Memory scopes: conversation (anonymous conversation transcripts), contact (shared across API / voice / chat after identity), and company operational aggregates. Pass memoryScope: "company" on create (or a later message) to contribute a redacted company memory when the session ends; agents on this channel can call search_company_memory for aggregate questions. Requires COMPANY_MEMORY_ENABLED=true (or 1) on private-api and jambonz; otherwise the API returns 503 instead of silently ignoring the flag. Store both conversationId and contactId from the create/resume response. Pass contactId (or stable contact hints) on the next conversation so the agent shares that contact's memory across API turns — the same store used after contact identification on voice/chat.

  • Anonymous — no contactId / hints → contactId: null; transcripts stay on the conversation until identity is supplied; no lasting contact memory is written.
  • Known contact — pass contactId belonging to your company.
  • External system person — pass contact.externalUserId (and optional phone/email/name); we look up or create once and return the id.
  • Late bind — on POST …/messages (or resume) you may send contactId when the session is still unbound and active (409 if already bound to a different contact, or if the conversation has ended).

Example create with identity:

curl -s -X POST "$BASE/api/v1/conversations" \
  -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -d '{"agentId":"9f2b6c1e-…","contact":{"externalUserId":"sap-cust-42","name":"Sam"}}'
{
  "conversationId": "a1b2c3d4-…",
  "contactId": 42,
  "agentId": "9f2b6c1e-…",
  "createdAt": "2026-06-12T08:00:00.000Z"
}

Send a message

POST/api/v1/conversations/:conversationId/messages
FieldTypeRequiredNotes
messagestring (1–32000)yesThe user's new message — only the new message
contactIdintegernoLate-bind when the conversation has no contact yet
customSystemMessagestring (1–8000)noAdded for this turn only, on top of the conversation-level one
customToolsarray (max 10)noMerged onto the conversation (by name) and reused on later turns; see Custom tools
streambooleannotrue switches to SSE; default synchronous

Synchronous response 200 (returned after all tool calls have run server-side):

{
  "conversationId": "a1b2c3d4-…",
  "contactId": 42,
  "message": { "role": "assistant", "content": "Order 42 shipped yesterday…" },
  "messages": [{ "role": "assistant", "content": "Order 42 shipped yesterday…" }],
  "toolCalls": ["custom_a1b2c3d4-…_lookup_order"],
  "usage": { "promptTokens": 512, "completionTokens": 64, "totalTokens": 576 },
  "handoff": {
    "roleName": "Support",
    "greeting": "Hi, you've reached Support. How can I help you?",
    "agentId": "9f2b6c1e-…"
  },
  "agentId": "9f2b6c1e-…"
}
  • message.content is the full reply (multiple assistant messages from tool-call continuations are joined with blank lines); messages lists them individually.
  • toolCalls contains the names of tools the agent invoked this turn.
  • When the agent hands off to another configured agent, the response also includes handoff (role name, greeting, target agentId) and top-level agentId for the agent now handling the conversation.

Streaming ("stream": true)

The response is text/event-stream; each event is a data: <json> line:

EventPayloadMeaning
content{ "type": "content", "content": "…" }A chunk of assistant text
new_message{ "type": "new_message" }A new assistant message starts (after a tool run)
tool{ "type": "tool", "name": "…" }The agent is executing a tool (server-side)
handoff{ "type": "handoff", "roleName": "…", "greeting": "…", "agentId": "…" }The conversation was handed off to another agent (roster must be configured on the entry agent)
done{ "type": "done", "conversationId": "…", "contactId": null, "usage": { … } }Turn complete
error{ "type": "error", "error": "…" }An error occurred; the stream ends

Custom tools

Server-executable HTTP tool definitions (the same shape as platform API tools). The server — never your client — calls url with the parameters the LLM chooses. Tools sent on a messages request are merged onto the conversation (by name, incoming wins; omit or [] is a no-op) and reused on later messages until the conversation ends.

  • Per request: messages accept up to 10 tools.
  • Per conversation: at most 10 stored tools after merge (overflow → 400). Concurrent merges are best-effort.
  • Auth: authConfig is encrypted at rest, never logged, and never returned on conversation/session reads. Authenticated tools require the platform credentials encryption key to be configured.
FieldTypeRequiredNotes
namestring (≤ 20, [a-zA-Z0-9_-])yesExposed to the agent as custom_<conversationId>_<name>
descriptionstring (≤ 1024)yesTells the agent when to use the tool
urlstringyeshttps:// only. Supports {param} path placeholders
methodGET / POST / PUT / DELETEyesGET sends params as query string; POST/PUT as JSON body
authTypenone / basic / apikey / bearerno (default none)How the server authenticates to YOUR endpoint
authConfigobjectno{ username, password } / { headerName, apiKey } / { token } — persisted encrypted for the conversation; never logged or returned on session GET
parametersarray (max 20)no{ name (≤64), type, required, description (≤512) }

Update conversation metadata

PATCH/api/v1/conversations/:conversationId/metadata

Replace the conversation's metadata (and, optionally, its conversation-level customSystemMessage). The new metadata fully replaces the previous metadata and is used on every subsequent turn. No AI response is generated.

FieldTypeRequiredNotes
metadataobjectyesReplaces the stored metadata wholesale
customSystemMessagestring (1–8000)noWhen present, replaces the conversation-level custom system message; left untouched when omitted

Response 200:

{
  "conversationId": "a1b2c3d4-…",
  "metadata": { "accountTier": "enterprise", "upgradeDate": "2026-06-22" },
  "updatedAt": "2026-06-12T08:30:00.000Z"
}

Returns 409 if the conversation has ended.

Add a manual message

POST/api/v1/conversations/:conversationId/manual

Append a message from a human operator or external system without triggering an AI response. It is stored as an admin turn, becomes part of the history, and is replayed into the agent's context on the next message (the model sees [ADMIN MESSAGE - <username>] …). No AI credits are consumed.

FieldTypeRequiredNotes
messagestring (1–32000)yesThe note to add
usernamestring (1–120)noWho added it; defaults to admin when blank or omitted
metadataobjectnoStored alongside the message (e.g. department, externalId)

Response 201:

{
  "conversationId": "a1b2c3d4-…",
  "messageId": "f0e1d2c3-…",
  "addedBy": "Martin",
  "timestamp": "2026-06-12T08:05:00.000Z"
}

Returns 409 if the conversation has ended.

Get a conversation + history

GET/api/v1/conversations/:conversationId
GET/api/v1/conversations/:conversationId?include=all

Response 200:

{
  "conversationId": "a1b2c3d4-…",
  "contactId": 42,
  "agentId": "9f2b6c1e-…",
  "status": "active",
  "createdAt": "2026-06-12T08:00:00.000Z",
  "messages": [
    { "role": "user", "content": "Where is my order 42?", "timestamp": "…" },
    { "role": "assistant", "content": "Order 42 shipped yesterday…", "timestamp": "…" },
    { "role": "admin", "content": "Refund approved manually.", "timestamp": "…", "metadata": { "username": "Martin", "source": "manual" } }
  ]
}

messages contains user/assistant turns plus any admin (manual) messages by default; admin entries include a metadata object with username and source. ?include=all additionally adds system/tool entries.

End a conversation

POST/api/v1/conversations/:conversationId/end

Response 200: { "conversationId": "…", "status": "ended" }. Transcripts are retained and remain retrievable; further messages to the conversation return 409.

Errors

StatusWhen
400Invalid body (details in details), e.g. non-https custom tool URL
401Missing, malformed, unknown, or revoked API key
402Company has insufficient AI credits (minCredits / totalBalance included)
403Conversation exists but belongs to a different company
404Unknown conversation/agent, or the session is not an API-channel session
409Conversation ended or mismatched; contact binding conflicts
503Authenticated custom tools cannot be stored or resolved because credentials encryption is not configured on the server
500Unexpected processing failure
504Agent turn exceeded the 120 s timeout

Error shape: { "error": "<message>" } (plus details for validation errors).

Behaviour notes

  • Agent config is intact. The agent's configured system prompt, model, and tools all apply. Custom system messages and custom tools are strictly additive — they can never replace or bypass the agent's configuration. Conversation custom tools persist across turns; they remain additive to dashboard API Tools assigned to the agent.
  • Channel-inapplicable tools are excluded: end_call, barge_in, call_internal_contact, end_session are never offered on this channel even if configured on the agent.
  • Agent handoff: when handoff_to_agent is enabled on the entry agent and its handoffRoster lists target agents, the API channel supports the same roster-based handoff as chat. Verification requirements (OTP, etc.) apply when configured on target agents. After handoff, subsequent messages use the target agent and the session's agentId is updated.
  • Contact memory: when contactId is set on the session (create, resume, late-bind while active, or agent identify_contact / create_contact), memory tools and post-session memory use that contact — same path as identified voice/chat. Anonymous API conversations stay conversation-scoped (transcripts only) until a contact is bound. Public API cannot bind identity after the conversation has ended (409).
  • Billing: each turn consumes AI credits under the api reference type (same minimum-credit setting as chat). PAYG companies are billed by invoice and are not credit-blocked.
  • Sessions are stored with channel='api' and are not shown in the dashboard chat views in v1.
  • Timeout: synchronous turns are capped at 120 s (long tool chains should use stream: true).