Twilio
Two URLs in the Twilio console. Every call is scored before your agent hears it, and on Guard a caller who looks wrong is asked to press a digit first. Your code does not change.
How it works
Twilio asks the voice webhook on a number what to do with an incoming call. You point that webhook at us. We record the call, score it, and answer with a few lines of TwiML: either hand the call straight to wherever it went before, or ask the caller to press a digit and only then hand it on. Twilio runs the TwiML on your account at your normal rates. We never touch the audio.
Setting it up
- Add the number in Murusai
Numbers → Add a number. Pick Twilio as the carrier and set the destination: the URL the number pointed at before (your own server, or a platform's inbound URL), or a phone number or
sip:address to dial. Or by API:RequestShell curl -X POST https://api.murusai.com/v1/numbers \ -H "Authorization: Bearer $MURUSAI_KEY" \ -H "Content-Type: application/json" \ -d '{ "number": "+14155550100", "gate_provider": "twilio", "gate_mode": "adaptive", "gate_destination": "https://agent.example.com/twilio/voice" }'The response carries a
gate_token. It is the credential in the URLs below; anyone holding it can post calls against this number, so treat it like a key. - Point the number at us
Twilio console → Phone Numbers → your number → Voice configuration.
- A call comes inWebhook · POST
https://api.murusai.com/v1/twilio/voice/gt_…- Primary handler failsWebhook · POST
- Your destination URL, if it is a URL. Calls go straight through if we are ever down.
- Call status changesPOST
https://api.murusai.com/v1/twilio/status/gt_…, so we learn when the call ended, and what Twilio's machine detection said.
The dashboard shows these three, filled in, on the number's page.
- Choose a mode
Mode What happens offEvery call is scored and handed straight on. You still get scores and alerts. This is what Watch does. adaptiveClean calls pass. A call that scores review or reject is challenged. A reject-level score latches the number into challenging everyone for thirty minutes, because one is rarely alone. alwaysEvery caller is challenged before they reach your agent. - Make a test call
Ring the number. On
offor a cleanadaptivecall you reach your agent as before, and the call appears on the Activity page with a score. Setalwaysfor a moment to hear the challenge yourself.
The challenge
A single digit, one to nine, chosen fresh each time. The caller can press it or say it. Two attempts, six seconds each. A right answer that arrives faster than a person could have heard the prompt is treated as a machine guessing.
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Gather input="dtmf speech" numDigits="1" timeout="6" speechTimeout="auto"
language="en-US" hints="one,two,three,four,five,six,seven,eight,nine"
action="https://api.murusai.com/v1/twilio/gather/gt_…?attempt=1&expected=4"
method="POST" actionOnEmptyResult="true">
<Say voice="Polly.Joanna">Thanks for calling. To continue, press or say the number 4.</Say>
</Gather>
</Response><?xml version="1.0" encoding="UTF-8"?>
<Response>
<Redirect method="POST">https://agent.example.com/twilio/voice</Redirect>
</Response><?xml version="1.0" encoding="UTF-8"?>
<Response>
<Dial answerOnBridge="true"><Sip>sip:agent@sip.example.com</Sip></Dial>
</Response><?xml version="1.0" encoding="UTF-8"?>
<Response>
<Say voice="Polly.Joanna">Sorry, we couldn't verify this call. Goodbye.</Say>
<Hangup/>
</Response>A caller who fails both attempts is blocked on your account, the call is ended with reason gate_fail or gate_timeout, and a caller.rate_exceeded alert goes out with gate: "failed". Every challenge and its outcome (pass, fail, timeout, how the caller answered, how fast) is on the Activity page and in /v1/activity.
What Twilio tells us
The voice webhook is the one place the carrier hands over geography, caller name and the STIR/SHAKEN result. We read FromCity, FromState, FromZip, FromCountry, CallerName and StirVerstat from it and keep them on the call as attrs. The zip is what lets us notice a dozen fresh callers from one place; the attestation is a reason on its own when it fails.
On Watch and on Guard
On Watch the gate is off: point the URLs at us and every call is scored and recorded, with alerts, and handed straight on. Setting adaptive or always needs Guard, and answers 402 plan_required otherwise. The number has to be a carrier number; a number bought inside a platform never reaches the carrier's webhook, so there is nothing for us to answer.
Telnyx and SignalWire
Both speak Twilio's dialect, TeXML and LaML, with the same parameters in and the same XML out. Pick the carrier when you add the number and the URLs change to match.
| Carrier | Where | URLs |
|---|---|---|
| Telnyx | Create a TeXML Application with these URLs, then assign the number to it. | /v1/telnyx/voice/gt_…/v1/telnyx/status/gt_… |
| SignalWire | Phone Numbers → your number → Voice settings, handled by LaML webhooks. | /v1/signalwire/voice/gt_…/v1/signalwire/status/gt_… |
Each has a fallback URL in the same place; set it to your destination as with Twilio.
Rotating the token
The token in the URL is the credential. To rotate it, remove the number and add it again: a new token is issued and the old URLs stop working at once. The number's history stays.