Chat

Run a chat agent from your own backend - your app relays messages, the agent replies.

Use the chat API to put a chat agent inside your own product: an in-app assistant, a WhatsApp bot, a support flow. Your backend opens a session, sends each of your user’s messages, and gets the agent’s reply back in the response.

This is the server-to-server way in and uses a secret key. To put chat on a website, use the embeddable widget and a publishable key instead (Chat guide). Either way it’s the same agents, billing and console inbox: a session opened through the API appears in Inbox, and a teammate can take it over there.

The agent must be a published chat agent. Chat must be turned on for your workspace, and each session is billed from your wallet.

Open a session

POST /v1/chat/sessions  ·  scope chat:write

{ "agentId": "agt_…" }

Returns 201 with the agent’s opening message:

{ "data": { "sessionId": "chatsess_…", "agentId": "agt_…", "reply": "Hi! How can I help?" } }
Status Code Meaning
404 agent_unavailable No such agent, or it isn’t published.
403 chat_not_enabled It’s a voice agent.
403 agent_offline Outside the agent’s business hours.
403 plan_feature_locked Chat is turned off for your workspace.
402 insufficient_balance The wallet can’t cover a chat session.
422 brain_unavailable The agent’s flow can’t run over chat (for example, it isn’t published).

Send a message

POST /v1/chat/sessions/:id/messages  ·  scope chat:write

{ "text": "Where is my order?" }

Waits for the agent and returns its reply:

{ "data": { "reply": "It shipped yesterday and arrives Friday.", "ended": false, "handedOff": false } }
  • ended: true - the agent finished the conversation (for example, it said goodbye). Open a new session to talk again.
  • handedOff: true - a teammate is handling the conversation in the console inbox, so reply is null. Their messages appear in the session’s transcript (below).
  • Send one message at a time: a message sent while the previous reply is still being written is refused with 409 reply_in_progress.
  • 409 session_ended - the session is over. 502 reply_failed - the agent couldn’t reply; send the message again.
  • Up to 4,000 characters. Rate limited to 120 messages a minute per workspace (429 rate_limited).

Get a session

GET /v1/chat/sessions/:id  ·  scope chat:read

{
  "data": {
    "id": "chatsess_…", "agentId": "agt_…", "state": "ai", "messageCount": 2,
    "createdAt": "…", "endedAt": null,
    "messages": [
      { "role": "agent", "text": "Hi! How can I help?" },
      { "role": "user", "text": "Where is my order?" },
      { "role": "agent", "text": "It shipped yesterday and arrives Friday." }
    ]
  }
}
  • state - ai (the agent is answering), awaiting_human or human (a teammate has it), or ended.
  • role - user (the messages you sent), agent, or human (a teammate in the console).

Only sessions opened with the API can be read or used here. Website widget sessions aren’t visible to secret keys (404).

End a session

POST /v1/chat/sessions/:id/end  ·  scope chat:write

Ends the session and bills it. Calling it again is harmless. Idle sessions are also ended automatically. A session where your user never sent a message isn’t billed.

Next: Compliance records →