Protocol
The following sequence applies to every call. Fonada acts as the WebSocket client; your endpoint acts as the WebSocket server.
| Step | Party | Action |
|---|---|---|
| 1 | Fonada | The call is answered (inbound) or the end user answers (outbound). |
| 2 | Fonada | Fonada opens a WebSocket connection to your URL. |
| 3 | You | Your endpoint accepts the upgrade (HTTP 101). |
| 4 | Fonada | Fonada sends session_start with call context and audio settings. |
| 5 | You | Your endpoint optionally replies with ready. |
| 6 | Both | Bidirectional audio streaming takes place. |
| 7 | Either | The call ends, or it is transferred to a human agent. |
- One connection per call. Connections are not pooled or reused, so 200 calls result in 200 connect-and-disconnect cycles.
- Fonada begins sending audio immediately after session_start and does not wait for
ready. If your service needs warm-up time, accept the connection and buffer incoming audio rather than delaying the accept. - session_start always arrives first, as a single text frame, before any audio.
session_start — text, once per call
{
"type": "session_start",
"session_id": "1711234567.42",
"client_id": "acme-voicebot",
"customer_phone": "919876543210",
"business_phone": "18001234567",
"audio": {
"codec": "pcm16",
"input_sample_rate": 16000,
"output_sample_rate": 16000,
"channels": 1
},
"metadata": {
"language": "en",
"agent_id": "sales-agent-v2"
}
}| Field | Description |
|---|---|
session_id | Unique call identifier. Log and retain it — it correlates your logs with Fonada's during support, and it is the key passed to the transfer API. |
client_id | Your configuration name on the Fonada platform. Useful where a single endpoint serves several campaigns or brands. |
customer_phone | The end user. See Call flows for inbound vs outbound roles. |
business_phone | Your platform number. |
audio.codec | Always pcm16 unless otherwise agreed: signed 16-bit little-endian PCM. |
audio.input_sample_rate | Sample rate of the audio Fonada sends to you. |
audio.output_sample_rate | Sample rate Fonada expects you to send back. |
audio.channels | Always 1 (mono). |
metadata | Agreed key/value pairs — language, agent or prompt identifier, campaign reference. Configurable per account. |
The call-direction value is delivered here too, under the field name you choose. Only type and audio are fixed; every other field name is configurable, and Fonada can add fields. See Customisation.
End-user audio — continuous
The end user's voice is delivered as raw PCM16 little-endian mono audio at input_sample_rate. By default these are binary WebSocket frames containing nothing but audio samples — no header, no wrapper. If your stack cannot handle binary frames, Fonada can send base64 text frames instead; see Audio.
The frame (chunk) size is configurable and determines how often a frame is received:
| Chunk size | At 8 kHz | At 16 kHz | Suitability |
|---|---|---|---|
| 320 bytes | every 20 ms | every 10 ms | Streaming ASR, barge-in detection |
| 640 bytes | every 40 ms | every 20 ms | Streaming ASR (common choice) |
| 1280 bytes | every 80 ms | every 40 ms | Balanced |
| 16384 bytes | every 1024 ms | every 512 ms | Batch processing only (Fonada default) |
Audio arrives at approximately real-time pace for the whole call, including during silence — silence is delivered as PCM silence, not as an absence of frames.
playback_completed — text
{ "type": "playback_completed" }Sent once everything you provided for the current turn has finished playing to the end user. Use it to know the end user is now hearing silence and to begin listening for the next utterance.
session_end — text
{ "type": "session_end" }The call is ending from Fonada's side — most commonly because the end user hung up. Immediately afterwards Fonada sends a WebSocket close frame (code 1000) and closes the socket. Release per-call resources when this message is received.
Messages you send
All messages in this section are optional. Audio flows in both directions with no control messages at all — a minimal endpoint can accept the connection, read audio, and write audio back. The control messages enable interruption handling and precise turn boundaries.
- Fonada sends session_start.
- You optionally send ready.
- Fonada streams caller audio continuously.
- You send TTS audio (media).
- You send end_of_generation.
- Fonada sends playback_completed.
ready — optional
{ "type": "ready" }Acknowledges session_start. Informational; Fonada logs it and continues.
end_of_generation — optional
{ "type": "end_of_generation" }Signals that everything for the current turn has been sent. This is what lets Fonada emit playback_completed once your audio finishes playing. Without it, turn boundaries are not signalled back to you.
end_of_call — optional
{ "type": "end_of_call" }Requests that Fonada end the call, for example after a goodbye message. Fonada lets any remaining audio finish playing first and then hangs up. If you have sent no audio at all, Fonada hangs up immediately.
error — optional
{ "type": "error", "message": "ASR model unavailable", "code": "asr_error" }Reports a problem on your side. Fonada logs it for diagnostics but takes no action, and the call continues. To terminate the call, send end_of_call or close the WebSocket (which also ends the call).