sk_live_ key, a phone number you are allowed to call, and
(for step 5) a public HTTPS URL.
sk_live_ followed by 32 hex characters, and it is shown only once. We
store only its SHA-256, so we cannot recover a lost key; you rotate it instead.
Put it in your secret manager now, before you go further.You also get a webhook signing secret, whsec_…. It belongs to the workspace
rather than the key, so rotating a key does not change it and your receiver
keeps verifying without an outage.Check the key (30 seconds)
401 here is almost always a trailing newline from cat-ing a key file.
Paste the key; do not pipe it.That balance is real money, and it pays for the calls below. We bill
in 10-second blocks, rounded up, with a one-block minimum, at your plan’s
tier. The examples below ran on T5, where every new
workspace starts. That is ₹5 a minute, or ₹0.84 a block.Set up Postman (2 minutes, optional but worth it)
api_key (your sk_live_…) and test_number (an E.164 number
you own). Then open Agents → Create agent and work down through
Calls. Requests that create something save its id for the next request,
so you don’t have to copy and paste anything.You can skip this entirely and use the curl below. The collection is the
faster path if more than one person on your side will touch the API.Create an agent (2 minutes)
id.{{order_id}} and {{customer_name}} are agent placeholders, which each
call fills from its variables. They are not shell or Postman variables, so
leave them exactly as they are.Place one real call (1 minute)
to for this one. It will actually ring.202 means the call is accepted but not yet connected, so the phone has
not rung. Read the outcome when the call ends:Receive the webhook (3 minutes)
POST a
signed JSON event at each lifecycle transition. Verify the signature before
you trust the body.v1 is HMAC-SHA256(whsec, "{t}.{raw_body}"), hex-encoded. Sign the raw
bytes. If your framework parsed the JSON and serialized it again, the string is
different and the signature will never verify.call.started, call.completed, call.voicemail, call.failed
and call.aborted, plus campaign.started / paused / resumed /
completed / stopped. Dedupe on id, because a retried delivery repeats it.
Webhooks has the full reference and a test vector.Run a campaign (2 minutes)
play means the dialler is running. Outside the 10:00 to 19:00 window it
waits in play until the window opens. That is expected, and the campaign is
not stuck. Campaigns has the full semantics.Use your own dates, from today onward. If no slot can open again on or before
end_date, start answers 409 window_closed
instead of 202.Read the report (1 minute)
Before you go live
Go live and switch the base URL
Go live and switch the base URL
https://prod.voice.miraiminds.co, and add your webhook endpoint again with the
new secret. Go live has every step.Verify webhook signatures — and reject what fails
Verify webhook signatures — and reject what fails
X-Mirai-Signature over the raw body, enforce the 5-minute timestamp
window, and return 401 when it does not match. Dedupe on the event id,
since retries repeat it. Don’t order your state machine by arrival order,
because a retried call.started can land after call.completed. Order by
data.call.status instead.
See signature verification.Wire the do-not-call list to your opt-outs
Wire the do-not-call list to your opt-outs
POST /v2/dnc in the same minute. The request is idempotent, so
you don’t need a read-modify-write. We check suppression at dial time, so it
also takes effect on campaigns that are already running. See the
do-not-call list.Cap every campaign with budget_paise
Cap every campaign with budget_paise
budget_paise limits the cost of a mistake in the list, such as a duplicated
CSV or a column shifted by one. Code review cannot catch that kind of failure.
When the campaign hits the cap, it pauses with
pause_reason: "budget_exhausted", keeps its place, and continues when you raise
the cap. Alert on campaign.paused.Alert on wallet balance, hourly
Alert on wallet balance, hourly
GET /v2/wallet and alert at a threshold that covers a day of traffic.
A 402 in the middle of a campaign is an expensive way to learn that your
wallet is empty. An underfunded campaign still starts, dials what it can, and
then pauses itself on insufficient_balance.
Once your company is live,
add credit on Billing.Know the three limits that will actually bite you
Know the three limits that will actually bite you
Handle these errors specifically
Handle these errors specifically
error.code. Errors has the full
table and a retry wrapper in two languages.Get the compliance basics right
Get the compliance basics right
first_message. It
costs one clause, and it reduces early hang-ups.
India calling rules.Where to go next
Go live
Campaigns
Agents
Webhooks
call_id or campaign_id, and we can see exactly what happened.