Tonedocs
Flows

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

#StepWhere
1Declare your sender classificationDashboard
2Store the endpoint credential, then Copy IDDashboard — Settings → Secrets
3PATCH /v1/numbers/{id} with routingMode: "byo_ws" and mediaEndpointAPI

Steps 1 and 2 are dashboard-only and will 401 with an API key, deliberately.

In step 3, routingMode: "byo_ws" must come with mediaEndpoint in the same request unless the number already has one stored — otherwise 422. Clearing the endpoint of a byo_ws number is also 422. byo_sip cannot be set or cleared by PATCH (409); use POST/DELETE /v1/numbers/{id}/sip.

Then, per call

#What happens
1POST /v1/calls without agentId — sending one is a 422
2The compliance gate runs, exactly as it does for an agent call
3The carrier dials
4Tone opens a WebSocket to your mediaEndpoint
5A start frame arrives with the call id, direction, from, to and your customParameters
6media frames flow both ways until someone hangs up
7Tone writes the call record and bills at the BYO rate

Inbound needs no extra work: a BYO number answers inbound the same way.

With a tone_test_ key and a sandbox number, steps 3–6 never happen: the sandbox carrier closes the call on a timer and Tone opens no WebSocket to your endpoint. The dial, the compliance gate, the call record and the webhooks are real. Exercising your endpoint takes a live number and a real, billed call.

Tone forwards DTMF to your bot but records no opt-out on a BYO call, and runs no extraction (summary and outputs stay null). Record opt-outs with POST /v1/compliance/call-outcomes or POST /v1/consent/opt-out.

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 mark to know when your audio finished playing. Guessing from byte counts drifts. Marks are echoed on Exotel and Plivo calls; on a call through Tone's SIP gateway they are currently not echoed.
  • 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_endpointThe number is byo_ws with no mediaEndpoint, or its authSecretId can no longer be decrypted
refused_endpoint_unreachableYour endpoint did not accept the connection — a 401/403 or any failed upgrade — or a webhook endpoint answered non-2xx
refused_endpoint_forbiddenIt resolved into private or forbidden address space
refused_media_formatThe negotiated audio format is not one we speak
refused_capacityTone's BYO capacity was full
refused_internal_errorSomething failed on Tone's side
caller_hung_up_during_resolveThe caller hung up before your endpoint was opened
errorThe connection to your endpoint errored during the call; always failed
max_durationThe 60-minute ceiling. Ends answered, and is billed

Every refused_* writes a call record with a failed disposition, bills nothing, and fires call.completed and call.failed. The dashboard's call detail shows the reason as "Ended because …". 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.

Was this page helpful?

On this page