Skip to main content

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
}
FieldTypeRequiredDescription
type"ping"YesMessage type.
server_timeintegerNoServer 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
}
FieldTypeRequiredDescription
type"resume"YesMessage type.
session_idstring (uuid)YesSession to resume.
last_seqintegerYesLast 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
}
FieldTypeRequiredDescription
session_idstring (uuid)YesUnique session identifier.
resumebooleanNoWhether this session supports resume.
server_timeintegerNoServer time in milliseconds.
heartbeat_interval_msintegerNoHeartbeat 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
}
FieldTypeRequiredDescription
participantsarrayYesArray of participant objects.
epochintegerYesRoster version counter.

Participant fields:

FieldTypeDescription
user_idstring (uuid)User identifier.
display_namestringDisplay name.
rolestringOne of: host, speaker, listener.
mutedbooleanWhether the participant is muted.
speakingbooleanWhether the participant is currently speaking.
joined_atstring (date-time)When the participant joined.

resumed

Sent after a successful session resume. Missed events are replayed before this message.

{
"type": "resumed",
"replayed": 3
}
FieldTypeRequiredDescription
replayedintegerYesNumber 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
}
FieldTypeRequiredDescription
typestringYesEvent type.
eventobjectYesEvent payload.
seqintegerNoPer-session sequence number for resume.

State Machine

disconnected ──connect()──> connecting ──hello──> connected
^ |
| (ws close)
| |
└──(max retries)── resuming <──(has sessionId)───┘
|
(resumed msg)
v
connected
  1. Client connects with a ticket parameter from the room join response.
  2. Server sends hello with the session_id.
  3. Client sends periodic ping at the server-specified interval.
  4. On unexpected disconnect, client reconnects with resume=1 and sends a resume frame.
  5. Server replays missed events and sends resumed.
  6. On graceful leave, client sends bye before closing.