What you’ll build
- An AI Calling section inside your application where a brand selects its WhatsApp number, configures its voice agent and tests calling.
- Incoming WhatsApp calls answered by that brand’s agent.
- Customer-requested callbacks after Meta confirms calling permission.
- A brand-specific history of calls, transcripts and available results.
- One partner workspace and one combined Voice Infra bill for all your brands.
Is this like BYO telco?
Yes. You bring the brand’s existing WhatsApp number, configure its Meta calling settings and register it with Voice Infra. The number stays with the brand’s Meta assets, and you do not buy a Plivo number or port the number. Your backend can create the integration each number needs through our API, but Voice Infra still approves ownership of each number. Once the partner workspace is enabled, adding another approved number does not require a deployment for that brand. You need: an activated partner workspace, its backend API key and webhook signing secret, an authorized Meta integration for each number, and a public HTTPS webhook receiver. The examples use cURL andjq, Python 3.10+ with
httpx, or Node.js 18+.
How it works
Keep your existing Meta messaging integration. Before changing SIP settings,
check whether the number already uses another calling provider, because
changing the destination changes where its voice calls go. Meta charges remain
under your existing Meta arrangement, separate from Voice Infra billing.
Step 1: Activate the partner workspace
Complete Go live and the billing setup with Voice Infra once for your partner workspace. Confirm your billing contact, commercial terms, funded balance and call limit. There is no public partner-signup endpoint that turns a sandbox key into production access. We supply these values through a private channel:VOICE_BASE_URL, VOICE_API_KEY and VOICE_WORKSPACE_ID from your backend
configuration or secret manager. Confirm access:
balance_inr and currency, and the voice
catalogue is under data. 403 production_workspace_required means production
activation is incomplete. Changing only the base URL does not upgrade a sandbox
workspace. Before you register numbers, confirm that the public number APIs are
available.
Keep the partner key on your backend. Brands must not receive this shared key
or unrestricted access to your partner workspace. Our API enforces workspace
ownership; your application enforces which brand can access each resource.
Store a mapping for every brand
Your database needs a durable mapping of:BRAND_ID to that brand’s permanent ID in your application before running
the creation examples. Run shell examples in Bash with set -euo pipefail so a
missing value or failed request stops the sequence.
Also retain secret-manager references for Meta access, your callback jobs, and
historical agent/number assignments. Select this mapping using the brand
identity you authenticated. A brand_id supplied in a request or in call
metadata is only a label and must not be used to authorize access.
Step 2: Create the brand’s agent
An agent holds the brand’s instructions, greeting, voice, language and call duration. Use one agent per brand so that business information and future changes stay separate.An acceptance test agent
Start each number with a test agent like this one, with voiceneha, language
en and a 120-second limit. Its job is to confirm two-way audio, and it does
not claim to access orders or customer records. Use it before you enable a
brand’s business workflow.
Save this complete definition as agent.json:
GET /v2/agents/{agent_id} and reuse its ID instead of creating a duplicate.
You cannot use an ID from another workspace.
Check the selected voice in your workspace’s voice catalogue.
Set the top-level language as well as any voice language. Replace the test
instructions with the brand’s approved content before serving real customers.
Order lookup or booking requires a real tool integration or supplied
context. Writing an instruction in the prompt does not create that integration.
- cURL
- Python
- Node.js
id (agt_...). If you are not
sure a request went through, retry it with the same creation key and body. Use
PATCH /v2/agents/{agent_id} for later edits.
Step 3: Configure the brand’s WhatsApp number
Your existing Meta integration does this step. With Meta, check that the selected number is eligible for calling and that your app can access it. A messaging integration on its own does not make a number eligible for calling. LoadMETA_GRAPH_VERSION, META_ACCESS_TOKEN and PHONE_NUMBER_ID from the
brand’s authorized integration. PHONE_NUMBER_ID is Meta’s phone-number ID,
not the WABA ID. BUSINESS_NUMBER is the brand’s E.164 number, including +.
Generate meta-settings.json from the workspace and agent IDs you stored:
ws hint is our workspace ID, and agent is our agent ID. Neither is your
platform’s brand ID or a Meta WABA ID. SDES and the additional codecs must match
this integration. Check the response before continuing.
Retrieve the SIP password
Meta generates the number’s SIP password, so the brand does not choose one. Retrieve it with your authorized Meta token:calling.sip.servers[].sip_user_password for the entry whose
hostname is sip.miraiminds.co. The SIP username is the business number in E.164.
Store the password in your secret manager. The Meta token, SIP password and
Voice Infra API key are three different credentials.
Do not reset an already working SIP integration just to read its password. For
an existing registered number, verify its settings and reuse its registration.
Coordinate any move to a different workspace with Voice Infra.
Step 4: Register and activate the number
Register the number with our API. The example below builds a private request file without printing the SIP password. SetVOICE_WEBHOOK_URL to the public
HTTPS URL of the call-result receiver you build in step 7.
business_number, phone_number_id, agent_id,
sip_username and sip_password. webhook_url and resolver_url are optional
public HTTPS endpoints. Leave them out until you have built them. The API works
out workspace ownership from your key, so do not send a workspace ID in the
body.
The response includes these fields (the values are examples):
id and version in the brand mapping. The full response
also includes meta_settings_example. Compare it with the settings you applied.
Our API never returns the SIP password. Once the password is in your secret
manager and the integration is complete, delete the temporary files that hold
it.
Approval and status
To get ownership approved, send Voice Infra your brand identifier, business number, workspace ID and registration ID. You do not need to send the password. If you are onboarding many brands, arrange batch approval. There is no self-approval API for customers. Poll each number’s status with bounded backoff:active means the configuration is in place. It does not prove that speech is
audible or that Meta routes calls correctly. There is no activation webhook in
this version. For inventory, use GET /v2/whatsapp/numbers?limit=100 and follow
next_cursor. To reconcile activation, use individual GETs. Do not poll
thousands of numbers every second.
Step 5: Receive incoming calls
Your application does not send aPOST /v2/calls request for an incoming call:
- The customer taps the voice-call button in the brand’s WhatsApp chat.
- Meta delivers the call to Voice Infra.
- The registered agent answers and conducts the conversation.
- Events go to the number registration’s
webhook_url, if one is set.
identity.caller_number in our call object. For
incoming WhatsApp calls, to identifies the business, so do not assume from
contains the caller. identity.wacid links the call to Meta’s records.
Optional caller context
Setresolver_url on the registration if you want your backend to supply
context before the agent answers. We send a signed request with this shape:
X-Mirai-Signature over the raw bytes as described in step 7. Identify
the brand from the registered business number and return HTTP 200:
{"allow":false} declines the call, while a timeout or error falls back
to the default agent. Treat the resolver as optional enrichment. Because it does
not fail closed, it cannot act as an authorization check or a per-brand spend
limit. Verify the caller before any sensitive action.
Step 6: Request a permitted callback
Your Call me button records the customer’s callback request. You also need native Meta calling permission, which is separate. Use your existing Meta messaging integration to request permission and process the authenticated confirmation, following Meta’s current messaging-window and template rules. Do not dial merely because a generic button was clicked or a cached expiry says permission is valid. Check the current Meta verdict for the actual brand and recipient:CUSTOMER_WHATSAPP_DIGITS is the country code and number without +. Continue
only when the last command succeeds. Meta can still refuse the call if a
permission or quota changes afterward. Use the current provider verdict rather
than hard-coded country lists or permission lifetimes.
Store the callback job before dialing
Your backend must keep a durable job with a unique(brand_id, callback_id) pair.
Freeze the recipient, agent, business number, request body, original timestamp
and idempotency key. Create a job only for a customer request that is still
outstanding. Permission on its own does not authorize a new callback.
Save this call request as callback.json, substituting values from that job:
variables and metadata are optional. agent_id, to, route: "whatsapp"
and the registered from select this call. Include webhook_url to receive
outbound results. Inbound results go to the registration’s webhook.
- cURL
- Python
- Node.js
id (call_...) and
status. An accepted request does not mean the customer answered. Voice Infra
handles the SIP signaling and audio, so you do not send SDP to Meta’s /calls
endpoint for this integration.
Retry without calling twice
- Process permission webhooks through your durable queue. Duplicate deliveries must select the same callback job.
- Retry uncertain API outcomes with the same body and idempotency key. Do not mint a new callback ID to get around a timeout.
- Keep completed jobs for longer than our 24-hour call-idempotency window, and stop automatic retries before the window expires so that an old webhook cannot start a new call.
- Persist the returned call ID and read its result. Do not automatically redial a completed or failed call, or move it to another channel.
Step 7: Receive and display results
Voice Infra sends signed webhooks to your HTTPS receiver. The envelope contains an event ID, type, creation time anddata.call:
X-Mirai-Signature using the workspace signing secret and the exact raw
request bytes. This signature is separate from Meta’s webhook signature and from
your API keys. The same Python verifier works on resolver request bytes:
(workspace_id, event_id) key, then return
2xx. Process the inbox asynchronously. If storage fails, return a retryable
failure instead of acknowledging an event you have lost.
For outbound calls, the stored callback job tells you which brand owns the call.
For inbound calls, match the business number and agent against your stored brand
mapping. Keep historical mappings so that late events still match. Do not assign
a call to a brand from client-provided metadata alone.
Handle call.started, call.completed, call.failed, call.aborted and, where
applicable, call.voicemail. If post-call processing is configured, you may
also receive call.processed. Use data.call.status and ended_reason to tell
outcomes apart. There is no call.no_answer event. Do not let a late
call.started overwrite a terminal result.
Check brand ownership before every read or control operation. The transcript,
recording and analysis can become available some time after the call ends.
Step 8: Operate one workspace across many brands
Combined billing
One funded partner wallet covers the brands’ calls. UseGET /v2/wallet for balance and GET /v2/wallet/transactions for reconciliation.
Match settled call costs to brands through your stored brand mapping, and use
those records for your own resale billing. Voice Infra does not create separate
invoices or balances for brands inside this shared workspace.
Confirm the workspace’s current pricing and wallet
terms. Do not assume a pilot charge applies to everyone. Meta charges are
separate. Coordinate changes to keys and the workspace’s pinned inbound calling
configuration with Voice Infra before changing production billing assumptions.
If the shared balance runs out, every brand can be affected, so monitor it
centrally. Per-brand dashboards and fair allocation are up to your application.
This shared-workspace recipe does not provide strict per-brand inbound spend
limits, and the optional resolver cannot serve as a reliable hard budget limit.
Onboard in batches
Use a resumable queue and save IDs after each successful step. Back off on 429/503 responses. Arrange ownership approval in batches, then test and enable brands gradually. A database test with 2,000 registrations does not show how many simultaneous calls your customers can make. Measure expected calls per second, concurrent calls and average call duration with Voice Infra before you agree on traffic limits. Monitor connection success, time to greeting, missing or one-way audio, dropped calls, provider errors, callback backlog, webhook delivery and wallet settlement. Healthy HTTP endpoints and acompleted status do not prove both parties heard the conversation.
Update or disconnect a brand
For example, to change where results go, send the version from your latest
read of the number:
Test it
Use the 120-second test agent from step 2 on one approved number. Record a call ID for every test:- Call the number in WhatsApp. Listen for the greeting, say a short sentence, and confirm the agent repeats its meaning. Do this more than once to catch intermittent failures.
- Make a genuine callback request through your app, accept native Meta calling permission, and confirm your job worker starts one call with audible speech.
- Replay the permission webhook. Confirm it reuses the job and does not ring the customer again.
- Test missing or revoked permission, a failed call, and a duplicate result webhook. Confirm your app records the correct outcome.
- Check signed result delivery, any available transcript or recording, and settled usage.
- Confirm another brand cannot read, change or cancel those calls through your application.
Troubleshooting
Privacy and compliance
Tell callers they are speaking with a voice agent and explain recording or transcript retention where applicable. Keep keys, SIP passwords and recording links out of browsers, application logs and public documents. Apply brand-level access checks to stored results and set retention rules that suit your use case. Meta calling permission does not give you consent to record. Work out which privacy and calling requirements apply to your operation, including GDPR, HIPAA or TCPA where relevant. Following this recipe does not make you compliant or authorize you to handle regulated data. Confirm the applicable platform terms and your organization’s requirements before enabling those workflows.Production checklist
- Production workspace activation, central funding, commercial terms and the public APIs you need are confirmed.
- Every brand’s number and agent mapping is correct, approved and active.
- Repeated incoming and permitted outgoing calls have audible two-way speech.
- Your actual Meta permission webhook, durable callback worker, result receiver and brand dashboard have been tested together.
- Duplicate and replayed events cannot start extra calls or corrupt final results.
- Concurrent and long calls pass within the measured limit, including provider failure, wallet exhaustion, alerting and recovery tests.
- Credential rotation, brand updates and disconnects work as documented.
- Brand isolation, retention, caller notices and settled usage are verified.
Do not enable every brand just because the first number registered. Keep each
brand in setup until its real call acceptance tests pass, then expand in
measured batches. Even when every API request succeeds, you still need number
approval, partner production activation and reliable audio.
References
- Agents, calls, webhooks and wallet.
- Meta SIP configuration.
- Meta call settings.
- Meta calling permissions.