Skip to main content
This page is the partner on-ramp, and it takes ten minutes from start to finish. You check your key, make one agent, ring one phone, run one campaign and read the report. You can paste every request below as it is; the only things to change are the key and the phone number. You need: your sk_live_ key, a phone number you are allowed to call, and (for step 5) a public HTTPS URL. Every request below uses the sandbox. After you go live, change the base URL, the key and the webhook secret. The requests stay the same.
Create your key in the console under Developers → Create key. It looks like 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.
1

Check the key (30 seconds)

200 OK
A 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.
2

Set up Postman (2 minutes, optional but worth it)

The easiest way is from your console: Developers → Postman gives you the collection and an environment that already has your API address (and your key, if you just made one).Or download them here:The collection is built from the API itself and ships with every release, so these links always give you the current one. Download it again whenever you want the latest.In Postman, click Import and drop in the collection and one environment. Select the environment (Mirai Voice — sandbox or Mirai Voice — production) and fill in 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.
3

Create an agent (2 minutes)

An agent is the reusable configuration a call runs: its prompt, opening line, voice, language and limits. You make it once and use it for thousands of calls.
201 Created
Keep that 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.
voice_id is ashu, neha, shruti, sameer, munshi or tanvi. The catalogue is the same on every tier. See Voices.
4

Place one real call (1 minute)

Put your own number in to for this one. It will actually ring.
202 Accepted
202 means the call is accepted but not yet connected, so the phone has not rung. Read the outcome when the call ends:
200 OK
96 seconds rounds up to 10 blocks of 10 seconds, which costs ₹8.34 on T5. See what a minute costs.
Always send Idempotency-Key on POST /v2/calls. A network timeout tells you nothing about whether the phone rang. With the key, a retry replays the original response and places no second call. Without it, your retry rings a real person twice.
5

Receive the webhook (3 minutes)

You can poll for the outcome, but subscribe to webhooks if you can. We 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.
The events are 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.
6

Run a campaign (2 minutes)

One call proves that the plumbing works. Campaigns are the main product: you upload a list, set a window and a budget, and the platform dials the list, handling pacing, retries and do-not-call suppression.
201 Created
The response is the campaign object itself, with the upload result as top-level keys. Bad rows come back by index, and they do not fail the rest of the upload.Start it:
202 Accepted
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.
7

Read the report (1 minute)

200 OK
Per contact, as CSV:
You have now run the whole loop. The rest of this page covers scale and hygiene.

Before you go live

If your sandbox has an end date, its keys stop on that date. Going live keeps everything you built and gives it no end date.Press Go live in the sandbox console. Then create a key in the live console, point your code at https://prod.voice.miraiminds.co, and add your webhook endpoint again with the new secret. Go live has every step.
Verify 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.
Every “stop calling me”, whether it comes on a call, over WhatsApp or by email, should become a 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.
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.
Check 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.
The concurrency limit paces your calls: extra calls wait instead of failing. Raising a ceiling is a provisioning change and needs no code change, so ask for it before the campaign starts. See Limits.
Branch on status first, then on error.code. Errors has the full table and a retry wrapper in two languages.
The business placing the calls is responsible for the calling window, consent records, national DND scrubbing and telling people they are speaking to an AI. Campaigns enforce the window and the suppression list that you set, but they do not make your calls compliant. Put the AI disclosure in first_message. It costs one clause, and it reduces early hang-ups. India calling rules.

Where to go next

Go live

₹100 to start, plus GST. Your agents and campaigns move across.

Campaigns

Windows, retries, budgets, per-contact reporting.

Agents

Prompts, voices, voicemail behaviour, duration caps.

Webhooks

Every event, the signature scheme, retries.
If you are stuck on something this page does not cover, email help@miraiminds.co with the call_id or campaign_id, and we can see exactly what happened.