OxagenDocs
REST API

Chat streaming

How to consume the chat SSE stream — event types, request fields, and connection management.

The chat capability (chat.message.send) streams its response as Server-Sent Events (SSE). This is the same stream the in-app agent uses — there is no separate "streaming API vs. web API."

Endpoint

POST https://api.oxagen.sh/v1/{orgSlug}/{workspaceSlug}/chat/stream

The org and workspace slugs are derived from the API key scope. You still include them in the URL path.

Request

POST /v1/acme-corp/engineering/chat/stream
Authorization: Bearer ox_…
Content-Type: application/json

{
  "content": "Summarize the last 10 agent runs and identify any failures.",
  "conversationId": "uuid"
}
FieldTypeRequiredDescription
contentstringYesThe user's message text.
conversationIdstringNoExisting conversation to continue. Omit to start a new conversation.
activeServerIdsstring[]NoMCP server IDs to enable for this turn. Omit or pass [] to load all workspace servers.
tierstringNoModel tier override: "fast", "balanced", or "precise". Omit to use workspace defaults.
modelstringNoExplicit model ID override. Omit to use workspace defaults.
effortstringNoReasoning effort override: "low", "medium", or "high". Omit to use workspace defaults.

Response

The response has Content-Type: text/event-stream. Each event is a JSON object preceded by data: and terminated by a blank line.

data: {"type":"text","text":"Here are the last 10 agent runs:"}

data: {"type":"tool-call-start","toolCallId":"uuid","capability":"agent.tool.list","inputPreview":{},"riskLevel":"low"}

data: {"type":"tool-call-end","toolCallId":"uuid","status":"completed","output":{"tools":[...]},"durationMs":142}

data: {"type":"usage","usage":{"promptTokens":1240,"completionTokens":312,"totalTokens":1552}}

event: done
data: [DONE]

Event types

TypeFieldsDescription
texttextA chunk of streamed assistant text. Concatenate in order.
reasoning-startreasoningIdBeginning of a reasoning block (emitted for models with extended thinking).
reasoning-deltareasoningId, textIncremental reasoning text chunk.
reasoning-endreasoningId, durationMsReasoning block complete.
step-startstepIndexThe agent is beginning a new reasoning or tool step.
step-finishstepIndexA step has completed.
tool-input-starttoolCallId, capabilityThe model is streaming tool input (before execution).
tool-input-deltatoolCallId, deltaIncremental tool-input text chunk.
tool-call-starttoolCallId, capability, inputPreview, riskLevelThe tool is about to execute.
tool-call-endtoolCallId, status, durationMs, output?, errorReason?Tool execution finished. status is "completed" or "failed".
approval-requiredapprovalId, capability, inputPreview, riskLevel, expiresAtThe agent has paused for user approval. Resolve with agent.approval.resolve.
usageusage.promptTokens, usage.completionTokens, usage.totalTokensToken usage totals for the turn, emitted after the model call finishes.
errormessageAn error occurred. The stream closes after this event.

The stream terminates with a named SSE event rather than a JSON object:

event: done
data: [DONE]

Handling approval events

When approval-required is emitted, the stream pauses. The event contains approvalId, capability, inputPreview, riskLevel, and expiresAt. Resolve the approval with a separate API call:

POST /v1/{orgSlug}/{workspaceSlug}/agent/approvals/resolve
Authorization: Bearer ox_…
Content-Type: application/json

{
  "approvalId": "uuid",
  "decision": "approved"
}

The stream resumes automatically after resolution. decision is "approved" or "denied"; an optional note string may also be sent.

Message threading

Pass conversationId to continue an existing thread. The route loads the last 50 messages from the conversation automatically — no message ID is needed to maintain history.

Omit conversationId to start a new conversation. Persist the conversation ID returned in the first response for subsequent messages in the same thread.

Token usage

The usage event is emitted after the model call completes. It contains the token counts for the turn under a usage object:

FieldDescription
promptTokensInput tokens sent to the model
completionTokensOutput tokens generated
totalTokensSum of prompt and completion tokens

Error handling

Stream errors arrive as error events:

data: {"type":"error","code":"capability_denied","message":"agent.code.execute requires approval","requestId":"req_…"}

After an error event, the stream closes. Reconnect with the same conversationId to continue the conversation.

Connection management

SSE connections are long-lived. If your HTTP client enforces a short timeout, set it to at least 120 seconds to accommodate multi-step agent runs. The connection: keep-alive HTTP header is set on the response; no periodic SSE heartbeat comment is sent.

On this page