Durable Conversation Streaming
Use these two endpoints to submit a message and read its events independently.
The Assistant service continues processing when your stream connection closes.
The existing POST /v1/conversations and /v1/conversations/with-uploads retain
their current behavior.
Accept a message
Send POST /v1/conversations/messages/accept with your x-api-key header and a
JSON body:
{
"agent_id": "0e546d24-09a3-41f7-9a2f-11a8bbc2160e",
"query": "Summarize the report",
"client_message_id": "5981bb90-5d26-4071-a11e-cc29be8aae3c"
}
Use agent_name instead of agent_id to select an agent by name. Generate a
new client_message_id for each turn; reuse it unchanged when retrying that
same request. It is required and must contain 1–128 characters. The upstream
service deduplicates repeated accepts using this key.
The endpoint returns 202 Accepted after the turn is persisted:
{
"conversation_id": "c4b12a3e-a456-4890-a12e-1fdd20d9dcbb",
"stream_id": "f05d7c78-7951-45e3-859a-48b930cd130a",
"assistant_message_id": "f05d7c78-7951-45e3-859a-48b930cd130a",
"user_message_id": "997f125f-dd6c-46ba-8577-007c774bfbe0",
"stream_start_id": "1790071000000-0"
}
For the next turn, supply conversation_id and set parent_message_id to the
previous assistant message ID. stream_start_id and user_message_id may be
null when replaying records from older deployments.
You can include collection_id, uploads, knowledge_base, and user_context
as in the existing message API. Upload files through the document upload API
first; the stream reports ingestion progress. New conversations default to
is_incognito: false. Eligible tool approvals default to automatic approval;
set bypass_tool_approval: false to handle approval events and submit decisions
through the existing tool interventions endpoint.
stream, message_only, and run_in_background are not accepted by this
endpoint. The POST always returns JSON, and the GET always streams all events.
Read and reconnect
Send a GET with your x-api-key header to:
/v1/conversations/{conversation_id}/streams/{stream_id}?stream_start_id={stream_start_id}
Use the cursor returned by accept for the first GET; omit it if null. The
response is text/event-stream. Preserve the last SSE id you receive. On a
reconnection, send that value in the Last-Event-ID request header, or use
since_id as a query parameter. Replay is exclusive of the supplied cursor.
Last-Event-ID takes precedence over since_id when both are supplied. Use
stream_start_id for the initial connection.
Events, IDs, retry hints, multiline data, and heartbeat comments pass through
unchanged. Use an SSE parser that handles arbitrary network chunk boundaries.
After a transport failure, reconnect with the last received event ID. A broken
connection is not a completion signal. To cancel generation explicitly, use
the existing interruption endpoint with stream_id as its message_id.
Browser clients that authenticate with x-api-key should use a streaming fetch
client capable of sending custom headers.
Recover after a page reload
On deployments that expose is_streaming and last_event_id in message history,
read /v1/conversations/{conversation_id}/messages. For a still-streaming assistant
message, use its message_id as stream_id and its last_event_id as the
Last-Event-ID header (omit the header if null). This resumes the existing turn
without submitting it again.
Permissions and errors
Accept requires conversations:create and respects the API key's agent scope.
Reading requires conversations:read; the Assistant service also checks access
to the requested conversation and stream.
- 409 on accept: Durable streaming is unavailable for this deployment or caller. A client may explicitly fall back to the legacy send-message API.
- 410 on GET: Replay has expired. Read the persisted transcript from the
public
messages_urlreturned in the JSON response, alongsideassistant_message_id. - Cursor-expired SSE error after GET has started: Read
/v1/conversations/{conversation_id}/messagesusing your API key. The relayed error event may contain an internal/chat/...metadata.messages_url; do not use that URL against the public API. A followingcloseevent ends replay. - 401, 403, or 404: Resolve authentication, access, or missing-resource errors before retrying.
Availability depends on the upstream streaming rollout. Verify the full flow against your target environment before adopting it in production.