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_xxxxThe 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
- Sign in to the dashboard with your company account.
- Go to Settings → API Keys.
- 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.
- Pass it on every request as
Authorization: Bearer be_xxxx. - 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
/api/v1/conversations| Field | Type | Required | Notes |
|---|---|---|---|
agentId | string (UUID) | yes | Must belong to your company |
customSystemMessage | string (1–8000) | no | Applied to every turn of this conversation; additive to the agent's configured prompt |
metadata | object | no | Structured context the agent sees on every turn (see below) |
Response 201:
{
"conversationId": "a1b2c3d4-…",
"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.
Send a message
/api/v1/conversations/:conversationId/messages| Field | Type | Required | Notes |
|---|---|---|---|
message | string (1–32000) | yes | The user's new message — only the new message |
customSystemMessage | string (1–8000) | no | Added for this turn only, on top of the conversation-level one |
customTools | array (max 10) | no | Extra tools for this turn, additive to the agent's configured tools (see Custom tools) |
stream | boolean | no | true switches to SSE; default synchronous |
Synchronous response 200 (returned after all tool calls have run server-side):
{
"conversationId": "a1b2c3d4-…",
"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 }
}message.contentis the full reply (multiple assistant messages from tool-call continuations are joined with blank lines);messageslists them individually.toolCallscontains the names of tools the agent invoked this turn.
Streaming ("stream": true)
The response is text/event-stream; each event is a data: <json> line:
| Event | Payload | Meaning |
|---|---|---|
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) |
done | { "type": "done", "conversationId": "…", "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:
| Field | Type | Required | Notes |
|---|---|---|---|
name | string (≤ 20, [a-zA-Z0-9_-]) | yes | Exposed to the agent as custom_<conversationId>_<name> |
description | string (≤ 1024) | yes | Tells the agent when to use the tool |
url | string | yes | https:// only. Supports {param} path placeholders |
method | GET / POST / PUT / DELETE | yes | GET sends params as query string; POST/PUT as JSON body |
authType | none / basic / apikey / bearer | no (default none) | How the server authenticates to YOUR endpoint |
authConfig | object | no | { username, password } / { headerName, apiKey } / { token } — held in memory for the request only, never persisted or logged |
parameters | array (max 20) | no | { name (≤64), type, required, description (≤512) } |
Update conversation metadata
/api/v1/conversations/:conversationId/metadataReplace 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.
| Field | Type | Required | Notes |
|---|---|---|---|
metadata | object | yes | Replaces the stored metadata wholesale |
customSystemMessage | string (1–8000) | no | When 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
/api/v1/conversations/:conversationId/manualAppend 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.
| Field | Type | Required | Notes |
|---|---|---|---|
message | string (1–32000) | yes | The note to add |
username | string (1–120) | no | Who added it; defaults to admin when blank or omitted |
metadata | object | no | Stored alongside the message (e.g. department, ticketId) |
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
/api/v1/conversations/:conversationId/api/v1/conversations/:conversationId?include=allResponse 200:
{
"conversationId": "a1b2c3d4-…",
"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
/api/v1/conversations/:conversationId/endResponse 200: { "conversationId": "…", "status": "ended" }. Transcripts are retained and remain retrievable; further messages to the conversation return 409.
Errors
| Status | When |
|---|---|
400 | Invalid body (details in details), e.g. non-https custom tool URL |
401 | Missing, malformed, unknown, or revoked API key |
402 | Company has insufficient AI credits (minCredits / totalBalance included) |
403 | Conversation exists but belongs to a different company |
404 | Unknown conversation/agent, or the session is not an API-channel session |
409 | Conversation has ended / has no agent assigned / agent no longer available |
500 | Unexpected processing failure |
504 | Agent 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.
- Channel-inapplicable tools are excluded:
end_call,barge_in,handoff_to_agent,call_internal_contact,end_sessionare never offered on this channel even if configured on the agent. - Billing: each turn consumes AI credits under the
apireference 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).
