WebSocket Events
Connect to WebSocket
Use the ws_url returned by POST /v1/call/session-token.
Browser clients cannot set arbitrary Authorization headers on a native WebSocket. Use the JWT subprotocol format:
const socket = new WebSocket(tokenResponse.ws_url, ["jwt", tokenResponse.token]);
Mobile clients may use either the supported subprotocol method above or an Authorization header if their WebSocket library supports it.
Envelope shape
{
"event": "call.invite",
"request_id": "req-1",
"data": {}
}
Core events
| Event | Direction | Purpose |
|---|---|---|
call.invite | caller -> service | start outgoing call |
call.ringing | service -> callee | incoming call signal |
call.accept | callee -> service | accept incoming call |
call.reject | callee -> service | reject incoming call |
call.cancel | caller -> service | cancel before connect |
call.end | either peer -> service | end active call |
call.connected | either peer -> service | mark media connected |
call.missed | service -> peers | missed timeout fired |
call.error | service -> client | request failed |
webrtc.offer | peer -> service -> peer | SDP offer relay |
webrtc.answer | peer -> service -> peer | SDP answer relay |
webrtc.ice_candidate | peer -> service -> peer | ICE candidate relay |
Invite payload
{
"callee_user_id": "receiver-456",
"callee_role": "receiver",
"context_type": "session",
"context_id": "SESSION-1001"
}
The service checks that callee_role is inside the JWT allowed_peer_roles. If not, the invite is rejected with:
{
"event": "call.error",
"request_id": "req-1",
"error": {
"code": "CALL_PEER_ROLE_NOT_ALLOWED",
"message": "callee role is not allowed by this token"
}
}
Accept payload
{
"call_uuid": "78927945-9ff0-4863-89ab-c08cbefed134"
}
Reject, cancel, and end payload
{
"call_uuid": "78927945-9ff0-4863-89ab-c08cbefed134"
}
WebRTC offer payload
{
"call_uuid": "78927945-9ff0-4863-89ab-c08cbefed134",
"sdp": {
"type": "offer",
"sdp": "v=0..."
}
}
WebRTC answer payload
{
"call_uuid": "78927945-9ff0-4863-89ab-c08cbefed134",
"sdp": {
"type": "answer",
"sdp": "v=0..."
}
}
ICE candidate payload
{
"call_uuid": "78927945-9ff0-4863-89ab-c08cbefed134",
"candidate": {
"candidate": "candidate:...",
"sdpMid": "0",
"sdpMLineIndex": 0
}
}
Incoming event examples
auth.ok
Sent after a WebSocket connection is authenticated.
{
"event": "auth.ok",
"success": true,
"data": {
"user_id": "caller-123",
"role": "caller",
"display_name": "Caller One",
"socket_id": "socket_uuid"
}
}
call.ringing
Sent to the callee when a caller starts a call.
{
"event": "call.ringing",
"data": {
"call_uuid": "78927945-9ff0-4863-89ab-c08cbefed134",
"caller_user_id": "caller-123",
"caller_role": "caller",
"display_name": "Caller One",
"context_type": "session",
"context_id": "SESSION-1001",
"call_type": "audio"
}
}
call.accepted
Sent to the caller after the callee accepts.
{
"event": "call.accepted",
"data": {
"call_uuid": "78927945-9ff0-4863-89ab-c08cbefed134",
"status": "accepted"
}
}