GuidesYour own stack

Your own stack

You run the agent. Your carrier hands you the call, your code answers it. Here is where we fit, and the three shapes that takes.

Three shapes

Ask

Before your code answers, it asks us. One GET, a score from 0 to 1 with where we would draw the line on it, and you decide what to do. Nothing about your call path changes; you have added an if.

Carrier
call arrives
Your server
GET /v1/score
Murusai
0 to 1, and our line
Your server
connect or refuse

Tell

Your code reports what happened: a call started, a call ended, and if you have them, the turns in between. The start is scored on the spot and answers with the same score, so this is also asking. But now the call is on record, the alerts fire, and the caller goes on the network for everyone.

Your server
POST /v1/events
Murusai
scores · alerts · remembers
You
webhook · email · text

The gate

The carrier asks us before it asks you. We serve the carrier a few lines of TwiML or NCCO: hand the call straight to your agent, or first ask the caller to press a digit. A caller who cannot is never connected, and never costs you anything. Your code does not change at all.

Carrier
voice webhook → us
Murusai
score · maybe challenge
Your agent
the ones that passed

Which to pick

AskTellGate
A bad call costs younothing, if you refuse itthe call, unless you also asknothing: it is refused before it connects
Code changeone request where you answertwo requests per callnone; a setting at the carrier
Builds historynoyesyes
Alertsnoyesyes
Live monitornoyes, if you send turnsno
Needsany keyfull key or ingest tokena carrier number; Guard to challenge

Most people who run their own stack do ask and tell together: ask when the call rings, tell when it starts and ends. If the number is on Twilio, Telnyx, SignalWire or Vonage, the gate does both for you and adds the challenge.

Ask and tell, together

Where you answer the callNode
import { murusai } from "./murusai.js"; // a thin fetch wrapper

// Your lines. 0.8 and 0.5 are where we would draw them; move either.
const REFUSE_AT = 0.8;
const VERIFY_AT = 0.5;

export async function onIncomingCall(call) {
  // 1. Ask. Fail open: silence is worse than a spam call.
  const s = await murusai.score({ caller: call.from, number: call.to }).catch(() => null);
  const score = s?.score ?? 0;
  if (score >= REFUSE_AT) return call.hangUp();

  // 2. Tell. The start is scored and remembered; the answer is the same shape.
  await murusai.event({ type: "call.started", call_id: call.sid, to: call.to, from: call.from });

  // 3. Connect, and tell us when it's over.
  const outcome = await connectToAgent(call, { verifyFirst: score >= VERIFY_AT });
  await murusai.event({ type: "call.ended", call_id: call.sid, reason: outcome.reason, cost: outcome.cost });
}

The score and the start can be the same request: a call.started answers with the score too. Ask separately only when you want the answer before anything is written down: a dry run for a number typed into a form, say.

What Guard means here

On a platform we connect to, Guard ends a bad call through the platform. On your own stack we have no handle on the call, so the acting is yours: you act on the score, and the gate refuses at the carrier. What Guard adds on your stack is the gate's challenge, the live monitor, texts and phone calls, unlimited numbers and the daily allowance.

The live monitor on your stack can flag and alert; it cannot reach into a call we do not control. If you send turns, act on call.suspicious from your own side.

Carriers

  • Twilio, and with the same shape, Telnyx and SignalWire.
  • Vonage.

Any other carrier or SIP stack works with ask and tell; only the gate needs the carrier to speak one of those two dialects.