WebSocket Signaling Protocol
The signaling WebSocket at /v1/ws provides real-time event delivery between the platform and connected clients. It handles session establishment, roster synchronization, room events, and session resume.
Connection
Connect via WebSocket upgrade:
GET /v1/ws?ticket=st_... # New connection (ticket from room join)
GET /v1/ws?resume=1 # Resume mode (reconnecting)
Client Messages
Messages sent from the client to the server.
ping
Heartbeat message sent at the interval specified by the server.
{
"type": "ping",
"server_time": 1721484600000
}
| Field | Type | Required | Description |
|---|---|---|---|
type | "ping" | Yes | Message type. |
server_time | integer | No | Server timestamp echoed from last pong. |
bye
Graceful disconnect signal.
{ "type": "bye" }
state_query
Requests a roster_snapshot from the server.
{ "type": "state_query" }
resume
Sent within 5 seconds of connecting in resume mode. Requests replay of missed events.
{
"type": "resume",
"session_id": "550e8400-e29b-41d4-a716-446655440000",
"last_seq": 42
}
| Field | Type | Required | Description |
|---|---|---|---|
type | "resume" | Yes | Message type. |
session_id | string (uuid) | Yes | Session to resume. |
last_seq | integer | Yes | Last sequence number received by client. |
Server Messages
Messages sent from the server to the client.
hello
Sent once after connection. Establishes the session.
{
"type": "hello",
"session_id": "550e8400-e29b-41d4-a716-446655440000",
"resume": true,
"server_time": 1721484600000,
"heartbeat_interval_ms": 15000
}
| Field | Type | Required | Description |
|---|---|---|---|
session_id | string (uuid) | Yes | Unique session identifier. |
resume | boolean | No | Whether this session supports resume. |
server_time | integer | No | Server time in milliseconds. |
heartbeat_interval_ms | integer | No | Heartbeat interval (default 15000). |
pong
Response to a ping message.
{
"type": "pong",
"server_time": 1721484600000
}
roster_snapshot
Full participant list. Sent on connect and in response to state_query.
{
"type": "roster_snapshot",
"participants": [
{
"user_id": "550e8400-e29b-41d4-a716-446655440000",
"display_name": "Alice",
"role": "host",
"muted": false,
"speaking": true,
"joined_at": "2026-07-20T14:00:00Z"
}
],
"epoch": 5
}
| Field | Type | Required | Description |
|---|---|---|---|
participants | array | Yes | Array of participant objects. |
epoch | integer | Yes | Roster version counter. |
Participant fields:
| Field | Type | Description |
|---|---|---|
user_id | string (uuid) | User identifier. |
display_name | string | Display name. |
role | string | One of: host, speaker, listener. |
muted | boolean | Whether the participant is muted. |
speaking | boolean | Whether the participant is currently speaking. |
joined_at | string (date-time) | When the participant joined. |
resumed
Sent after a successful session resume. Missed events are replayed before this message.
{
"type": "resumed",
"replayed": 3
}
| Field | Type | Required | Description |
|---|---|---|---|
replayed | integer | Yes | Number of buffered frames replayed. |
room_event
Incremental room events forwarded from room pub/sub.
{
"type": "participant.joined",
"event": { "user_id": "...", "display_name": "Bob" },
"seq": 43
}
| Field | Type | Required | Description |
|---|---|---|---|
type | string | Yes | Event type. |
event | object | Yes | Event payload. |
seq | integer | No | Per-session sequence number for resume. |
State Machine
disconnected ──connect()──> connecting ──hello──> connected
^ |
| (ws close)
| |
└──(max retries)── resuming <──(has sessionId)───┘
|
(resumed msg)
v
connected
- Client connects with a
ticketparameter from the room join response. - Server sends
hellowith thesession_id. - Client sends periodic
pingat the server-specified interval. - On unexpected disconnect, client reconnects with
resume=1and sends aresumeframe. - Server replays missed events and sends
resumed. - On graceful leave, client sends
byebefore closing.