The WebSocket transport connects VoxCore to the iCallMate telephony dialler. Calls arrive as WebSocket connections carrying audio and control events in a custom JSON protocol.

Connection flow

Endpoint

WebSocket /ws/{bot_id}
bot_id identifies which bot configuration to fetch from VoxBridge.

Audio format

PropertyValue
EncodingLINEAR16 (signed 16-bit PCM)
Sample rate8,000 Hz
ChannelsMono
Transport encodingBase64 in JSON payload field

Handshake events

The dialler sends exactly three events after connection:
First event. Contains caller metadata.
FieldTypeDescription
eventstring"connected"
callerIdstringCaller’s phone number
didstringDialled number (DID)
callDirectionstring"incoming" or "outgoing"
streamIdstringStream identifier (optional, may come in start)
All fields from the connected event (minus event) are forwarded to VoxBridge as connected_event query params during config fetch.
Second event. Contains stream and media format info.
FieldTypeDescription
eventstring"start"
streamIdstringStream identifier
mediaFormatobjectAudio format metadata
Third event. Signals handshake complete, pipeline can start.
FieldTypeDescription
eventstring"answer"

Protocol events

Incoming (dialler → VoxCore)

EventDescription
mediaAudio frame. payload field contains base64-encoded LINEAR16 PCM.
hangup-callCustomer or network disconnection. disconnectedBy field indicates source.

Outgoing (VoxCore → dialler)

EventDescription
reverse-mediaBot audio response. Base64-encoded LINEAR16 PCM in payload.
reverse-media-stopClears the dialler’s audio buffer. Sent before hangup.
reverse-hangup-callDrops the call. Sent after reverse-media-stop.
reverse-call-transferTransfers the call to a specified number.

reverse-media payload

{
  "event": "reverse-media",
  "chunk": 42,
  "did": "+911234567890",
  "payload": "<base64 audio>",
  "timestamp": "2026-04-24 14:30:00",
  "streamId": "stream-abc",
  "callerId": "+919876543210",
  "chunk_durn_ms": 20,
  "callDirection": "incoming",
  "encoding": "LINEAR",
  "source": "ai"
}

Hangup behavior

Bot-initiated hangup:
  1. VoxCore sends reverse-media-stop (clears audio buffer)
  2. VoxCore sends reverse-hangup-call
  3. disconnected_by = "bot"
Customer hangup:
  1. Dialler sends hangup-call event
  2. VoxCore receives CancelFrame, pipeline winds down
  3. disconnected_by = "customer"

Transfer behavior

When the bot transfers the call, VoxCore sends a single reverse-call-transfer event (transfer method icallmate_reverse_transfer) carrying the destination transferno. Transfer is terminal for the bot leg: VoxCore sets the serializer’s _hangup_sent flag so the EndFrame auto-hangup does not fire a reverse-hangup-call after the transfer.
{
  "event": "reverse-call-transfer",
  "streamId": "stream-abc",
  "callerId": "+919876543210",
  "did": "+911234567890",
  "transferno": "+911112223333",
  "source": "ai"
}

Outside active hours

If VoxBridge returns no config (bot unavailable or outside its active-hours window), fetch_config() returns None. VoxCore drops the call immediately by sending reverse-media-stop followed by reverse-hangup-call to the dialler, and records disconnected_by = "outside_hours".

Capacity

When a worker is at MAX_CONCURRENT_CALLS, new WebSocket connections are rejected with close code 1008 (“Server at capacity”). The nginx least_conn balancer routes to the least-loaded worker.