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}/answer
You do not write any XML, and no API key goes into your carrierThe carrier fetches that URL when a call arrives and we answer with the correct document for that provider, containing a websocket URL and a single-use token good for one call and a few minutes. Both verbs work, because providers disagree about which one a webhook uses and some retry with the other after a failure.

The 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.

Why we generate the TwiML rather than let you write it<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.

We read the audio format rather than assuming itExotel's default is linear PCM, not mu-law. An adapter that assumed mu-law would decode 16-bit PCM through a mu-law table and produce noise, with nothing raised anywhere — the caller would simply hear static. The format is taken from the stream's own start message, so mu-law 8kHz and linear PCM at 8kHz or 16kHz all work without you declaring which.

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.

ProviderNeedsManual setup
TwilioAccount SID, auth tokenNone
PlivoAuth ID, auth tokenNone
ExotelSID, API key, API token, flow IDOne 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.

The subtle part, since it cost us a debugging sessionSynthesis runs faster than playback at RTF 0.83, so a seven-second reply is fully sent in under six and the carrier plays the rest from its own buffer. A caller who interrupts after generation finished produces no cancellation event at all. Barge-in therefore keys off the interruption itself, not off cancelled synthesis — and the call tracks when audio will finish playing, not just when it finished generating.

Audio formats

FormatFrameNotes
mulaw_8000160 bytesWhat every PSTN carrier delivers. Silence is 0xFF, not 0x00
pcm_8000320 bytesTranscribes best — 16-bit linear beats mu-law for fidelity
pcm_16000640 bytesExotel 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.

What we have not testedNo PSTN call has been placed through this. Each provider's protocol is spoken exactly as documented and tested against a simulator that behaves like the real thing, but carrier behaviour that only appears on a live line — jitter, packet loss, how fast Twilio drains its buffer after a flush — is unverified. This proves our end of the protocol, not the carrier's.