What you’ll build
- A shared number whose calls go to one agent.
- A lookup on your server that answers within two seconds: whose call it is, the greeting, and the voice and languages for that user. When it says “not ours”, or can’t answer in time, the caller is not answered and nothing is charged.
- A background lookup for slower data, such as notes about the caller. Its values reach the agent during the call.
- A summary of each call in the user’s own language, delivered to your webhook.
- A way for your app to end a call while it runs.
- A workspace API key and its webhook secret. See the Quickstart.
- A phone number in your workspace that answers incoming calls. The inbound calling cookbook covers buying or connecting one.
- A public HTTPS server on port 443 for the lookups and the webhook. While you build, a tunnel to your laptop works.
- Python 3.10+ with FastAPI and
httpx, or Node.js 18+ with Express. The setup steps also work with cURL andjq.
How it works
The caller is ringing while your lookup runs, so time matters:
The two-second budget is shared by every pre-call tool the call waits for. The
Tools page explains how it works on
incoming calls.
Step 1: Answer “whose call is it?” on your server
Each incoming call sends your server the caller’s number (caller_number), the
number they dialled (called_number) and the call’s id. Your server decides
whose call it is from those and your own records, and answers with:
Each value you want in the prompt is at most 512 characters, so keep
instructions short. Save the call id against the user: the webhook in
step 4 uses it to find whose call it was.
The second route serves the background lookup. It starts at the same moment
as the first, so it works the user out again instead of waiting for the first
request to finish.
Both routes check a shared key that Mirai sends in the Authorization header.
db, crm and verify_mirai_signature stand in for your own code.
- Python
- Node.js
server.py
/mirai/events, see
signature verification.
Step 2: Create the agent
Save this asshared-line-agent.json, with your server’s address in place of
api.example.com.
shared-line-agent.json
ownerwaits for your answer. Itstimeout_msof 1.5 seconds leaves room inside the incoming-call budget, andon_failure: "abort"means a caller is never answered with a greeting that has no user in it. A call your server couldn’t answer endsfailedwithprecall-tool-failed.- The
call_settingsin your answer set the voice and languages.selectdoesn’t need to name them. See Set the voice and language for the call. skip_callruns afterownerand turns the call away when your server answered"answer": false. The call endsabortedwithskipped-by-condition, and nothing is charged.notesruns in the background, so it never holds the answer. It reads the call’s own variables only: a background tool can’t readowner’s values, because both start together. When it answers, the agent gets the caller’s name and last message. See Run a slow lookup in the background.analysis.promptreads{{owner.summary_language}}, so each summary is written in that user’s language. See Variables in the prompt.- The agent’s own
voiceandlanguageare what a call uses when your answer leavescall_settingsout.
Step 3: Store the key, create the agent and point the number at it
Store the key your server checks as a workspace secret, create the agent, then assign it to the number with a webhook. The number update needs its currentversion.
- cURL
- Python
- Node.js
Step 4: Read each call’s result
Every call carries the voice and languages it used, and who chose each, incall_settings. On a call your server
configured, the sources name your tool:
call.processed brings the summary in data.call.analysis.
The /mirai/events route in step 1 finds the user from the call id it saved
and stores the message. A call your server turned away sends call.aborted
(skipped-by-condition) or call.failed (precall-tool-failed) and no
summary.
To show a call while it runs, open its live events with your
key from the moment whose-call gives you the call id. The pre-call tools’
events arrive while the caller’s phone is still ringing.
Step 5: End a call from your app
Your app has the callid from the lookup, so it can end a call at any time,
for example when your user picks up elsewhere or blocks the caller.
- cURL
- Python
- Node.js
aborted with
aborted-by-api. See Abort a call.
Test it
This test agent needs no server of your own. It uses httpbin.org, a public echo service, in place of both lookups. Anything you send it is public, and it is sometimes slow, so use it only for this test.shared-line-test.json
- Call the number. The agent says “The secret word is PINEAPPLE. You called
from” and your number, in the
sameervoice. - Read the call with
GET /v2/calls/{id}.call_settings.sourcesshowspre_call:echoforvoice_idandlanguage. - Wait about five seconds into the call and ask “Who am I?”. The agent answers “Ravi”, from the background tool.
- Hang up. The summary in
GET /v2/calls/{id}/analysisis in Gujarati. ChangesummarytoTamil, call again, and the next summary is in Tamil. - Change
voicetorobotand call. The agent speaks inneha, the agent’s own voice. Theechoreceipt inGET /v2/calls/{id}/tool_runshas a warning namingcall_settings.voice_id. - Call again and, while the agent talks, send
POST /v2/calls/{id}/abort. The call hangs up and endsaborted.
Privacy and compliance
- The caller’s number goes to your server on every call. Treat it as personal data: keep it only as long as you need it and protect it like the rest of your user records.
- Caller ID can be faked. Use it to choose a greeting and settings, never as proof of who is calling. Don’t let the agent read out a user’s private details because of the caller’s number alone.
- Tell callers they’re speaking with an AI voice agent, and give a recording notice if you record. Your greeting is the place for both.
- The callers are usually not your users and have no account with you. Their words reach your users as summaries, so tell your users what is stored and for how long, and honour the data protection law that applies to you, such as India’s DPDP Act or GDPR.
- Keep
lookup_keyin workspace secrets. Never put keys in prompts or variables. Check webhook signatures on every event. - Transcripts, recordings and summaries are kept for 90 days with the call. Copy what you need into your own system.
Production checklist
whose-callanswers in well under 1.5 seconds at your busiest hour, including the network between us and your server.- Every user has a voice and languages your server can send, and the
receipts of test calls carry no
warnings. - Calls that aren’t your users’ answer
"answer": false, and you’ve tested that the caller is not answered. - The background lookup has a
timeout_msthat fits it, and the agent copes when its values never arrive. - Summaries arrive in each user’s language on test calls for at least two languages.
- Webhooks are verified, deduplicated on the event
id, and matched to users by the callidsaved during the lookup. - Your app can end a call with
POST /v2/calls/{id}/abort, and you’ve tested it on a live call.
Related
Tools
Pre-call tools, the incoming-call budget, call settings and background lookups.
Calls
Call settings, the call object and ended reasons.
Call analysis
Summaries and success checks with variables.
Live events
Follow a call, its pre-call tools included.
Receive inbound phone calls
Buy or connect the number and turn on incoming calls.
Webhooks
Signed events, final results and signature checks.