Skip to main content

Message Types

All messages are JSON objects with a type field. Messages are sent over WebSocket and/or WebRTC data channel.

Client → Server Messages​

ping​

Heartbeat to keep connection alive. Server responds with pong.

{
type: 'ping',
timestamp: number // Date.now() for RTT calculation
}

offer​

WebRTC SDP offer to initiate peer connection.

{
type: 'offer',
signal: RTCSessionDescriptionInit // From RTCPeerConnection.createOffer()
}

reconnect​

Attempt to recover a previous session after disconnect.

{
type: 'reconnect',
sessionId: string // Previous session ID to recover
}

audio​

Audio data for transcription (fallback when WebRTC audio track unavailable).

{
type: 'audio',
data: string, // Base64-encoded audio (WAV/WebM)
attachments?: VisionAttachment[] // Optional images to include
}

attachments​

Vision attachments sent via data channel, queued for next speech segment.

{
type: 'attachments',
attachments: VisionAttachment[] // Array of { data, mimeType?, alt? }
}

Server → Client Messages​

ready​

Sent immediately after WebSocket connection. Contains session info and ICE servers for WebRTC.

{
type: 'ready',
id: string, // Unique session ID assigned by server
protocolVersion: number, // Currently 1
iceServers?: RTCIceServer[] // STUN/TURN servers for WebRTC connection
}

ICE Servers: The iceServers array contains STUN and TURN server configurations that the client should use when creating its RTCPeerConnection. This enables the server to centrally manage ICE configuration, including Metered TURN credentials.

Example with Metered TURN:

{
"type": "ready",
"id": "abc123",
"protocolVersion": 1,
"iceServers": [
{ "urls": "stun:stun.metered.ca:80" },
{ "urls": "turn:global.relay.metered.ca:80", "username": "abc", "credential": "xyz" },
{ "urls": "turn:global.relay.metered.ca:443?transport=tcp", "username": "abc", "credential": "xyz" }
]
}

See Networking & TURN for configuration details.

pong​

Response to ping. Echoes timestamp for RTT calculation.

{
type: 'pong',
timestamp: number // Echoed from ping message
}

signal​

WebRTC SDP answer in response to client's offer.

{
type: 'signal',
signal: RTCSessionDescriptionInit // SDP answer
}

reconnect-ack​

Response to reconnect request.

{
type: 'reconnect-ack',
success: boolean, // Whether reconnection succeeded
sessionId: string, // Session ID (may be new if original expired)
historyRecovered: boolean // Whether conversation history was restored
}

Conversation Flow Messages​

transcript​

Speech-to-text transcription result.

{
type: 'transcript',
text: string, // Transcribed text
isFinal: boolean // true when transcription is complete
}

llm-chunk​

Streaming LLM response chunk.

{
type: 'llm-chunk',
content: string, // Partial response text
done: boolean // true for final chunk
}

llm​

Complete LLM response (non-streaming mode).

{
type: 'llm',
text: string // Full response text
}

TTS Messages​

tts-start​

TTS synthesis is starting. Sent before first audio chunk.

{
type: 'tts-start'
}

tts-chunk​

Streaming TTS audio chunk.

{
type: 'tts-chunk',
format: string, // 'pcm' | 'mp3' | 'ogg' | 'wav'
sampleRate: number, // e.g., 24000
data: string // Base64-encoded audio data
}

Note: When WebRTC audio track is available, TTS audio is sent directly over the track instead of via tts-chunk messages.

tts​

Complete TTS audio (non-streaming mode).

{
type: 'tts',
format: string, // 'mp3' | 'wav' | 'ogg'
data: string // Base64-encoded audio data
}

tts-complete​

TTS playback finished successfully.

{
type: 'tts-complete'
}

tts-cancelled​

TTS playback was interrupted (user barge-in).

{
type: 'tts-cancelled'
}

Speech Detection Messages​

speech-start​

VAD detected user started speaking.

{
type: 'speech-start'
}

speech-end​

VAD detected user stopped speaking. Processing begins.

{
type: 'speech-end'
}

Playbook Messages​

These messages are only sent when using playbook mode with tool calling.

tool-call-start​

Tool execution is starting.

{
type: 'tool-call-start',
name: string, // Tool function name
callId: string, // Unique ID for correlation
arguments: Record<string, unknown> // Arguments passed to tool
}

tool-call-end​

Tool execution completed.

{
type: 'tool-call-end',
callId: string, // Matches tool-call-start
result?: unknown, // Tool result (on success)
error?: string, // Error message (on failure)
durationMs: number // Execution time in milliseconds
}

stage-change​

Playbook transitioned to a new stage.

{
type: 'stage-change',
from: string, // Previous stage ID
to: string, // New stage ID
reason: string // Why transition occurred
}

Error Messages​

error​

Server-side error occurred.

{
type: 'error',
code: ErrorCode, // Structured error code
message: string // Human-readable description
}

Error Codes:

CodeDescription
WEBRTC_UNAVAILABLEWebRTC not supported on server
CONNECTION_FAILEDWebRTC connection failed
SESSION_NOT_FOUNDSession ID not found for reconnect
SESSION_EXPIREDSession expired (TTL exceeded)
STT_ERRORSpeech-to-text failed
STT_TIMEOUTSTT request timed out
LLM_ERRORLLM inference failed
LLM_TIMEOUTLLM request timed out
TTS_ERRORText-to-speech failed
TTS_TIMEOUTTTS request timed out
AUDIO_PROCESSING_ERRORAudio processing failed
VAD_ERRORVoice activity detection failed
INVALID_MESSAGEMalformed message received
INVALID_AUDIO_FORMATUnsupported audio format
TOOL_ERRORTool execution failed
PLAYBOOK_ERRORPlaybook execution error
INTERNAL_ERRORUnexpected server error
RATE_LIMITEDToo many requests

Type Definitions​

interface VisionAttachment {
data: string; // Base64 data URI or URL
mimeType?: string; // e.g., 'image/jpeg'
alt?: string; // Description for accessibility
}

interface RTCIceServer {
urls: string | string[];
username?: string;
credential?: string;
}