Headless AI agent API
The OctaDezx headless Agent API: POST /agent/start and /agent/reply return the agent's checked reply, status, handoff and actions, with Server-Sent Events streaming. Available at version 0.1.
Status: available (v0.1). The OctaDezx Agent API puts the support agent behind your own UI. Your server sends the customer's message to POST /agent/start or POST /agent/reply with an API key and gets back the agent's final, checked reply, a status of answered, needs_input or handed_off, a handoff reason when a person takes over, and the actions the turn filed, such as an order or a refund request. Send Accept: text/event-stream to receive the turn as Server-Sent Events. The Agent API is live at https://api.octadezx.com/v1, version 0.1.
Start a conversation
All requests use an API key, Authorization: Bearer odx_... or x-api-key, from your server only. The user object is trusted because it comes from your server: the conversation is bound to user.id and the email and phone are linked to that contact, so the user can ask about their own orders and bookings.
| Field | Notes |
|---|---|
| message | 1 to 4000 characters. |
| user | Optional. id (1 to 255), email, phone, name, attributes. |
| conversationId | Optional. Continue an existing conversation. A conversation bound to a different user returns 409 conversation_user_mismatch. |
| metadata | Optional JSON object, at most 4 KB, stored on the conversation. |
Request
curl -X POST "$BASE/agent/start" \
-H "Authorization: Bearer $OCTADEZX_API_KEY" -H "Content-Type: application/json" \
-d '{"message":"Where is my order 1001?","user":{"id":"u_123","email":"jo@example.com"},"metadata":{"page":"/orders"}}'Response
{
"conversationId": "6f1c...",
"status": "answered",
"reply": "Order 1001 shipped yesterday with Pathao, tracking PTH123.",
"user": { "id": "u_123" },
"messageAccepted": true
}Continue and read it
Reading returns state (open, handed_off, resolved), the raw status, handoff, the verified user (verifiedBy api_key or user_hash), and up to 500 messages with role user, assistant, agent (a teammate) or system.
Reply
curl -X POST "$BASE/agent/reply" -H "Authorization: Bearer $OCTADEZX_API_KEY" -H "Content-Type: application/json" \
-d '{"conversationId":"6f1c...","message":"Can I change the address?"}'Read
curl "$BASE/agent/conversations/6f1c..." -H "Authorization: Bearer $OCTADEZX_API_KEY"Statuses
| status | Meaning | What to do |
|---|---|---|
| answered | The agent replied. | Show reply. |
| needs_input | The final reply ends by asking the customer something, such as an order number. This is a reading of the reply text. | Show reply and wait for the answer. |
| handed_off | The agent escalated, or a person already has the conversation. handoff.reason says why. | Tell the customer a person will reply. Read later replies or subscribe to conversation.handed_off. |
Actions
When a turn files something, actions lists it, read from the database rather than from the reply. Refund requests are requests for a person to decide; no money moves.
actions
"actions": [
{ "type": "order.created", "id": "...", "status": "pending", "totalAmount": 2450 },
{ "type": "appointment.requested", "id": "...", "status": "requested", "service": "Fitting", "startsAt": "2026-11-05T10:00:00Z" },
{ "type": "refund.requested", "id": "...", "status": "requested", "amountCents": 4999, "currency": "USD" }
]Streaming with Server-Sent Events
Send Accept: text/event-stream to /agent/start or /agent/reply. The stream sends a status event with state thinking at once, then the final status, then the reply in delta chunks, then done with the full turn. Keep-alive comments arrive every 15 seconds.
Unverified text is never streamed. OctaDezx checks every reply before a customer sees it, against the catalogue, the brand safety rules and the multi-step inquiry's verified facts, so the stream waits for the checked reply and then sends it in chunks. You get an immediate thinking signal and a progressive render, not a faster first word.
| Event | Data | When |
|---|---|---|
| status | { state, conversationId } | First with state thinking, then once with the final status. |
| delta | { text } | The reply in chunks. Concatenated, they equal done.reply. |
| done | The full turn | Last event. |
| error | { error, code, httpStatus, conversationId?, messageAccepted? } | Instead of deltas and done, when the turn fails after the stream started. |
Event stream
event: status
data: {"state":"thinking","conversationId":"6f1c..."}
event: delta
data: {"text":"Yes, the Brown Boot comes in 42. "}
event: delta
data: {"text":"Would you like the brown or the black?"}
event: done
data: {"conversationId":"6f1c...","status":"needs_input","reply":"Yes, the Brown Boot comes in 42. Would you like the brown or the black?","messageAccepted":true}Reliability
- A turn is not idempotent. Do not blindly retry a failed start or reply. When the error carries messageAccepted, read the conversation first.
- transcriptSaved: false means the reply was generated but could not be confirmed in the transcript. Show it, do not resend.
- Turns wait for the full checked reply, typically a few seconds. Use a 60 second client timeout, or stream.
- The response does not list knowledge sources yet. A sources field will be added when the reply pipeline exposes them, and will never be guessed.
Install
@octadezx/sdk (available, v0.1.0, MIT license)
Server SDK for Node.js 20 and newer: Agent API with streaming, signed user tokens, webhook verification and devices.
npm install @octadezx/sdkQuestions or a larger rollout? Contact us (kevin@octadezx.com).
Frequently asked questions
- What does the headless Agent API return? The conversation id, a status of answered, needs_input or handed_off, the checked reply, a handoff reason when a person takes over, the verified user and any actions the turn filed, such as an order, an appointment request or a refund request.
- Does the Agent API stream responses? Yes, with Server-Sent Events when you send Accept: text/event-stream. The reply is streamed only after it has passed the checks, so unverified text is never sent.
- Can I retry a failed turn? Not blindly. Turns are not idempotent, so read the conversation first when an error says the message was accepted.
- How do I hand a conversation to a person? The agent hands off on its own when it should, and the turn returns status handed_off with a reason. Read later replies from the conversation endpoint or subscribe to the conversation.handed_off webhook.
- Is the Agent API available now? Yes, at version 0.1, at https://api.octadezx.com/v1 with a business API key. The Node.js SDK on npm wraps it, and the original /message and /messages endpoints remain available.
- Is it an alternative to Fin's agent API? It covers the same kind of building blocks: start and reply endpoints, streaming, handoff, actions, verified users, webhooks and server SDKs. Compare them against your own requirements.