/v1/agents/{agent_id}/conversations/... and takes your normal API key (x-api-key) or an OAuth2 JWT. Xpander Chat uses these same routes.
Use Invoke Agent for a single one-shot request. Use a conversation when you want a thread: send a message, get the reply, send the next message with the same conversation_id, add follow-ups while a reply is being written, steer or stop the current turn, edit an earlier message, or answer a question the agent asked.
How a conversation works
Each message you send is one turn: the agent reads your message, works, and replies. The first message creates the conversation and returns itsconversation_id; every later message carries that id and continues the same thread. The agent keeps the full history, so you never resend earlier messages.
Within a conversation you can:
- Send a message and get the reply with Run Conversation Turn (waits for the reply) or its streaming variant (events as they happen). Pass
id= yourconversation_idto continue; omit it to start a new conversation. - Add a follow-up while a turn runs with Send Conversation Message: the message waits in the conversation’s queue for the next turn, or steers the turn that is already running.
- Check the state with Run State: is a turn running, can it take a steer, how many follow-ups are queued.
- Run queued follow-ups with Drain Queue when the conversation is idle.
- Stop the running turn with Stop; queued follow-ups stay unless you clear them.
- Edit an earlier message with Edit Message: everything after it is dropped and the rewritten message runs as a new turn.
- Answer a question card with Submit Answers, or approve or decline a confirmation card with Confirm.
The conversation record
A conversation has one record. Get Conversation returns it together with the full thread, token usage and the record of every sub-task the agent created. The same record is also reachable with Get Task on theconversation_id, because the record uses the Task schema: id on that object is the conversation_id.
To continue a conversation recorded before September 2026, start a new conversation with the same agent.
Sub-tasks
A turn runs on the agent you addressed. When that agent hands part of the work to another agent, it creates a sub-task: a separate record with its own thread, listed under the conversation in the Task Manager and on Get Conversation. Whether the agent waits for the sub-task or lets it run on is decided when the agent creates it. A sub-task that outlives the turn reports into the conversation when it finishes (asub_task_finished event on a live stream). A sub-task never creates background sub-tasks of its own.
The sub_tasks query parameter on Run Conversation Turn and its streaming variant sets how the agent’s sub-tasks run inside the turn, not whether your call waits: sync runs every sub-task inline, async runs every sub-task in the background, auto lets the agent choose per sub-task. The setting sticks to the conversation until you change it. Turns started from Slack, Teams, WhatsApp or email always use sync, because a result landing after the channel reply has no way back to the channel.
Sending messages while a turn runs (queue and steer)
A conversation runs one turn at a time. A follow-up sent while a turn is running never runs in parallel: it either waits in the queue for the next turn, or it steers the turn that is already running. Send Conversation Message takes amode:
mode=auto(default): queue the message if a turn is running. If the conversation is idle, the response isstarted, which means you run the turn yourself with Run Conversation Turn passingid=conversation_id.mode=queue: always queue, even when idle.mode=steer: hand the message to the running turn so the agent takes it into account at its next skill call, without waiting for the turn to finish. Run State tells you whether a steer is possible right now (is_steerable); the response echoessteer_target.mode=interrupt: stop the running turn and run this message as the next turn.
{ action, message_id, queue_depth, steer_target }: queued with the new depth, steered, interrupted, or started when idle.
How queued messages run:
- If a stream is open on the running turn, the queue drains on that stream: when the turn finishes, the next message runs as the following turn. Each drain is announced with a
gateway_queue_updatedevent (last_action: "drain"). - If no stream is open (you queued over plain REST and disconnected, or queued while idle), call Drain Queue to run the pending messages and stream them. A queue left without a stream is also picked up on its own within a few minutes.
queue_depth and previews), Cancel Queued Message (drop one) and Clear Queue (drop all). Stop cancels the running turn and keeps the queue by default.
Reading the reply
Three ways, depending on how you called:- From the stream. On any streaming endpoint,
turn_finishedcloses the turn and carries the reply inresult. Sub-task results arrive assub_task_finishedevents. - From the conversation. Get Conversation returns the record (with
statusandresultof the latest turn), the full thread with every turn’s messages and skill calls, token usage, and every sub-task’s record, thread and usage. - From the task routes. The
conversation_idand every sub-task id work with Get Task (status and result) and Get Task Thread (the thread).
Without holding a stream
For a request/response integration:- Send the message with Run Conversation Turn. It waits for the reply and returns the record with
result; itsidis theconversation_idto continue with. - Add follow-ups with Send Conversation Message using
mode=queue, then run them with Drain Queue. - Check progress any time with Run State (
is_running,queue_depth) and read replies with Get Conversation.
Streaming
The streaming endpoints (run a turn, drain, answers, confirm, edit-stream) return Server-Sent Events. The stream opens with aconnected event as soon as the request lands; each following data: line is a JSON TaskUpdateEvent, the same shape as Invoke Agent (Stream). In order:
Every event carries
task_id = conversation_id. The reply text streams as chunk events for every agent. A client that opens the stream without Last-Event-ID receives the latest turn from its first event and the stream ends at that turn’s turn_finished; reconnect with Last-Event-ID set to the last id: you received to get the events after it. sequence on an event grows across the whole conversation. Chunks are not stored; your messages, skill calls, plan and process updates and one agent message per turn are kept in the thread in order, so reading the conversation back later shows the same text, skill call, text sequence the stream showed.
Auth and identity
Every route is scoped to the agent in the path and authorized by your API key’s access to that agent, like the rest of the v1 API. There is no end-user session, so message and answer bodies accept an optionaluser object when you want to attribute the turn to a person; without it the turn runs unattributed, and a continued conversation keeps whatever user it already carries.
