跳到主要内容

Message Reference

Every message — both directions — uses the same envelope. This page is the catalog of every type and content_type value, with the content shape for each.

Envelope​

{
"type": "<message_type>",
"content_type": "<content_type>",
"content": <payload>,
"metadata": { ... }
}
FieldRequiredNotes
typeyesOne of the message types listed below.
content_typeyesTells you how to parse content.
contentyesThe payload. Type depends on content_type.
metadatanoFree-form key/value bag. The server adds sequence_number and timestamp to most messages.

Content types​

content_typecontent shapeUsed by
textstringMost text messages
jsonobjectControl messages, structured payloads
multimodal{ text, media[] }User turns with images / files

For voice, raw audio bytes flow as WebSocket binary frames — they're not wrapped in this envelope at all. The envelope is used for control (audio_stream_start, audio_stream_end).

Message types​

The full set of valid type values, grouped by purpose.

Lifecycle​

handshake (client → server)​

The first message. See Handshake.

handshake_ack (server → client)​

Reply to a successful handshake. See Handshake.

transaction_end (server → client)​

End-of-turn marker.

{
"type": "transaction_end",
"content_type": "text",
"content": "done",
"metadata": {
"sequence_number": 42,
"timestamp": 1714592834012,
"total_duration_ms": 1872
}
}

metadata.total_duration_ms is included in text mode (the time from the start of the turn to the end). It's omitted in voice mode.

User turns (client → server)​

user​

A user message. Either text or multimodal.

// Text
{ "type": "user", "content_type": "text", "content": "Hi!" }

// Multimodal
{
"type": "user",
"content_type": "multimodal",
"content": {
"text": "What's in this picture?",
"media": [
{
"type": "image",
"media_id": "med_abc",
"s3_key": "media/...",
"content_type": "image/png",
"filename": "x.png",
"size_bytes": 12345
}
]
}
}

media[].type can be "image", "video", or "document". Upload media via REST first to obtain media_id / s3_key.

Assistant output (server → client)​

assistant​

The assistant's reply. In non-streaming mode, one envelope per turn carries the full reply. In streaming mode, the final assembled reply is also emitted as assistant after the deltas finish — concatenate the assistant_delta chunks for the streamed view.

{ "type": "assistant", "content_type": "text", "content": "Hello, how can I help?" }

In voice mode, assistant envelopes are emitted as transcripts alongside audio frames. They carry metadata.generation_stage: "speculative" | "final" — see Voice Chat → User and assistant transcripts.

assistant_delta​

A streaming chunk of the assistant's reply. Emitted when the agent's text model is configured to stream. Concatenate content across deltas until transaction_end.

{ "type": "assistant_delta", "content_type": "text", "content": "Hello, how" }

thinking​

A complete extended-thinking block, emitted in non-streaming mode for models that expose chain-of-thought. Render this collapsibly if you support it; otherwise ignore.

{ "type": "thinking", "content_type": "text", "content": "The user is asking..." }

thinking_delta​

A streaming chunk of extended thinking. Same rendering guidance as thinking.

{ "type": "thinking_delta", "content_type": "text", "content": "The user is..." }

Tool-call messages​

See Tool Calls for end-to-end flow.

tool_call_info (server → client)​

Informational notice that a server-side tool was invoked.

{
"type": "tool_call_info",
"content_type": "text",
"content": "weather: {\"city\":\"Berlin\"}"
}

tool_result_info (server → client)​

Informational notice that a server-side tool returned.

{
"type": "tool_result_info",
"content_type": "text",
"content": "weather: {\"temp_c\":14}"
}

tool_request (server → client)​

The agent asks the client to execute a local tool.

{
"type": "tool_request",
"content_type": "json",
"content": {
"request_id": "req_abc",
"tool_name": "set_volume",
"input": { "level": 30 },
"server_name": "Living Room MCP"
}
}

server_name is optional; it's set when the tool was discovered through a specific local MCP server. See Tool Calls.

tool_response (client → server)​

Your reply to a tool_request. The request_id must match.

{
"type": "tool_response",
"content_type": "json",
"content": {
"request_id": "req_abc",
"result": { "ok": true }
}
}

Audio (voice mode)​

See Voice Chat for end-to-end flow.

audio_stream_start (both directions)​

{
"type": "audio_stream_start",
"content_type": "json",
"content": {},
"metadata": {
"role": "user" | "assistant",
"sample_rate": 8000,
"encoding": "pcm"
}
}

When the server sends audio_stream_start for the assistant, it also sets metadata.sample_rate and metadata.encoding. Today both are hard-coded to 8000 and "pcm" regardless of what was negotiated in the handshake — see the caveat in Voice Chat.

audio_stream_end (both directions)​

{
"type": "audio_stream_end",
"content_type": "json",
"content": { "reason": "end_turn" | "interrupted" | "timeout" },
"metadata": { "role": "user" | "assistant" }
}

barge_in (server → client)​

The platform's voice model detected user speech overlapping with assistant audio and cancelled the assistant turn. Use this to stop local playback. See Voice Chat → Barge-in.

{
"type": "barge_in",
"content_type": "text",
"content": "interrupted",
"metadata": {
"interrupted_at_ms": 1714592834012,
"sequence_number": 17,
"timestamp": 1714592834012
}
}

event (client → server)​

Voice-mode-only control message that lets the client end the session gracefully. Content is "end", "stop", or "close".

{ "type": "event", "content_type": "text", "content": "end" }

Usage and quotas (server → client)​

The shapes of these envelopes differ slightly between text and voice modes.

usage_info​

Per-turn usage update. Emitted in text mode only.

In text mode, content_type is "text" and content is a JSON-encoded string — clients should JSON.parse(content) to read the fields:

{
"type": "usage_info",
"content_type": "text",
"content": "{\"total_input_tokens\":1234,\"total_output_tokens\":567,\"total_tokens\":1801}"
}

usage_warning​

Quota approaching (typically over 80%).

In text mode, content_type is "text" and content is a human-readable string with no metadata:

{
"type": "usage_warning",
"content_type": "text",
"content": "⚠️ Quota warning: 85% used (8500 / 10000 tokens)."
}

In voice mode, the structured form is used: content_type is "json", content carries the usage fields, and metadata.content carries the human string:

{
"type": "usage_warning",
"content_type": "json",
"content": {
"current_usage": 8500,
"limit": 10000,
"percentage": 85
},
"metadata": { "content": "Quota warning: 85% used." }
}

usage_error​

Quota exceeded or limit hit. Same text-vs-voice split as usage_warning. The chat input should be disabled in your UI when this fires.

Error​

error (server → client)​

error envelopes come in two shapes — pick the one that matches content_type. See Errors & Lifecycle for the full split and a recommended client-side parser.

Structured (handshake validation, TIMED_OUT, handshake-time QUOTA_EXCEEDED):

{
"type": "error",
"content_type": "json",
"content": {
"code": "INVALID_INPUT_FORMAT",
"message": "Invalid input format: 'mp3'"
}
}

Plain text (mid-session errors, AUDIO_STREAM_NOT_STARTED, mid-session voice quota, tool execution errors):

{
"type": "error",
"content_type": "text",
"content": "human-readable message",
"metadata": { "code": "ERROR_CODE" }
}

metadata.code is set on some text-shaped errors (e.g. AUDIO_STREAM_NOT_STARTED) and absent on others (e.g. mid-session voice quota, tool execution errors).

Common metadata fields​

The server attaches these to most outgoing messages. They're optional reading for clients but useful for debugging:

FieldMeaning
metadata.role"user" / "assistant" for audio messages.
metadata.sequence_numberMonotonically increasing across messages within a session. Useful when you need ordering. Not attached to handshake_ack.
metadata.timestampServer time in milliseconds. Not attached to handshake_ack.
metadata.codeOn text-shaped error messages — the machine-readable error code (when set). For json-shaped errors, the code lives in content.code.
metadata.transcripttrue on voice-mode user envelopes that carry transcribed user speech.
metadata.generation_stage"speculative" or "final" on voice-mode assistant envelopes.
metadata.interrupted_at_msOn barge_in — server time of the detected interruption.
metadata.total_duration_msOn text-mode transaction_end — turn duration in ms.

Quick lookup​

DirectionTypecontent_typeNotes
C→ShandshakejsonFirst message.
S→Chandshake_ackjsonConfirms params + conversationId.
S→Clocal_tools_discoverjsonMid-handshake; only when capabilities includes local_tools_discover.
C→Slocal_tools_supportedjsonReply to local_tools_discover.
C→Susertext / multimodalA user turn.
S→CassistanttextFinal reply (or per-stage transcript in voice).
S→Cassistant_deltatextStreaming chunk (text-mode streaming).
S→Cthinking / thinking_deltatextExtended-thinking blocks.
S→Ctool_call_info / tool_result_infotextServer-side tool info.
S→Ctool_requestjsonLocal tool to run.
C→Stool_responsejsonLocal tool result.
C↔Saudio_stream_start / audio_stream_endjsonAudio framing.
S→Cbarge_intextServer-detected interruption notice.
C→SeventtextVoice-mode graceful end ("end"/"stop"/"close").
C↔S(binary frame)—Raw audio bytes.
S→Ctransaction_endtextEnd of turn.
S→Cusage_infotextPer-turn token usage (text mode only; JSON-encoded string in content).
S→Cusage_warning / usage_errortext (text mode) / json (voice mode)Quota / billing.
S→Cerrorjson (handshake/lifecycle) / text (mid-session)Recoverable or fatal.

On this page