A BYO media session
What happens on the wire between Tone and your own voice stack, from dial to hang-up.
See Bring your own voice stack for setup and Tone Media Streams for the protocol reference.
The setup, once
| # | Step | Where |
|---|---|---|
| 1 | Declare your sender classification | Dashboard |
| 2 | Store the endpoint credential | Dashboard — Developer → Secrets |
| 3 | PATCH /v1/numbers/{id} with routingMode: "byo_ws" and mediaEndpoint | API |
Steps 1 and 2 are dashboard-only and will 403 with an API key, deliberately.
Then, per call
| # | What happens |
|---|---|
| 1 | POST /v1/calls without agentId — sending one is a 422 |
| 2 | The compliance gate runs, exactly as it does for an agent call |
| 3 | The carrier dials |
| 4 | Tone opens a WebSocket to your mediaEndpoint |
| 5 | A start frame arrives with the call id, direction, from, to and your customParameters |
| 6 | media frames flow both ways until someone hangs up |
| 7 | Tone writes the call record and bills at the BYO rate |
Inbound needs no extra work: a BYO number answers inbound the same way.
The endpoint
static names one wss:// URL, used for every call with the call id
appended as a query parameter. webhook names an https:// URL fetched per
call that answers {"url":"wss://…"} — which is how you route calls to different
backends.
🔴 Both must be publicly reachable. Private and link-local addresses are refused when you save the config, and refused again at connect time after DNS resolution — because the first check can be sidestepped by DNS and the second by a config that was never validated.
Three rules that decide whether it sounds right
- Send small chunks, 20–100ms. One large buffer is a caller waiting.
- Use
markto know when your audio finished playing. Guessing from byte counts drifts. - Hang up by closing the socket. There is no separate hang-up frame.
customParameters merge in three layers, later winning: static number config →
per-call variables → identity facts (callId, direction, from, to).
Every refusal reports a completed call
If Tone cannot reach you, the call still ends properly — it is never left hanging in your CDR:
endReason | |
|---|---|
refused_no_endpoint | The number is byo_ws with no mediaEndpoint |
refused_endpoint_unreachable | Your endpoint did not accept the connection |
refused_endpoint_forbidden | It resolved into private address space, or refused the credential |
refused_media_format | The negotiated audio format is not one we speak |
max_duration | The 60-minute ceiling |
Each writes a call record with a failed disposition. Watch for these before
blaming the carrier.
Two limitations
Campaigns do not run on BYO numbers — refused at launch rather than as thousands of per-call errors.
A negotiated full-stack rate does not apply. BYO bills the telephony-plus-platform subset; see Billing.