Telephony
Connect a number you already own. Twilio, Exotel and Plivo. We do not sell numbers, so your minutes and your regulatory position stay with your provider — which for Indian outbound is also the arrangement that is compliant, because the calling entity is you.
Receiving calls
Two steps. Map the number to an agent, then give your provider one URL. Inbound needs no credentials from you, because we are not placing the call — you only store credentials for outbound.
https://api.latency.cam/telephony/{twilio|plivo|exotel}/answerThe agent is chosen by the number that was dialled, so one URL serves every line you own and the routing table decides. A number that is not mapped is answered with a spoken sentence explaining that, rather than with dead air — which on a phone line is the hardest failure of all to diagnose.
Twilio
Console → Phone Numbers → your number → A call comes in. Webhook, HTTP GET,https://api.latency.cam/telephony/twilio/answer.
<Start><Stream> is send-only and <Connect><Stream> is not. Get that one word wrong and the agent hears the caller perfectly and can never speak, which looks exactly like a broken agent rather than the wrong verb. It was the most common setup mistake by a distance, so we removed the opportunity to make it.Plivo
Create an application whose Answer URL is https://api.latency.cam/telephony/plivo/answer with method GET, then assign that application to your number.
Exotel
Exotel connects calls to a flow you build rather than to a URL we supply, so this one needs a step in their dashboard. Create a flow with a Voicebot applet pointed at https://api.latency.cam/telephony/exotel/answer, then attach the flow to your Exophone.
Routing a number to an agent
curl -X POST https://api.latency.cam/v1/phone-numbers \
-H "Authorization: Bearer $LATENCY_KEY" -H "Content-Type: application/json" \
-d '{"number": "+91 98765 43210", "agent_id": "ag_...", "provider": "twilio",
"label": "Support line"}'Numbers are stored as their last ten digits. The same line arrives as +919876543210, 919876543210 or 09876543210 depending on the provider, and a routing table that matched literally would miss on two of those three and send the caller to the wrong agent with no clue why.
Making calls
Outbound uses your provider account. Store the credentials once — they are encrypted before they touch the disk, because they can spend your money — then a campaign dials through them. See outbound campaigns.
| Provider | Needs | Manual setup |
|---|---|---|
| Twilio | Account SID, auth token | None |
| Plivo | Auth ID, auth token | None |
| Exotel | SID, API key, API token, flow ID | One flow, built once in their dashboard |
Exotel is the odd one out because its outbound API dials into a flow you build in their console rather than a URL we supply. That flow's websocket URL is fixed, so it cannot carry a per-call token — an Exotel outbound call is matched to its contact by phone number instead, which is why the dialer refuses to have two live attempts against one number.
Barge-in needs the carrier's help
Cancelling our own synthesis is not enough. The provider has already buffered whatever we sent and will keep playing it, so the caller hears the agent continue for a second or more after interrupting — precisely the thing barge-in exists to prevent. We send the provider's flush message the moment a turn is abandoned.
Audio formats
| Format | Frame | Notes |
|---|---|---|
mulaw_8000 | 160 bytes | What every PSTN carrier delivers. Silence is 0xFF, not 0x00 |
pcm_8000 | 320 bytes | Transcribes best — 16-bit linear beats mu-law for fidelity |
pcm_16000 | 640 bytes | Exotel offers it |
Frames are 20ms. Voice detection, the barge-in threshold and the turn-end probe are all built on that, so a provider sending 100ms blocks — Exotel does — has them split before they reach the engine. Handing 100ms blocks straight through would coarsen every timing decision by five times.