Outbound calls
Your agent calls the customer — what they hear first, which agent speaks, the guards that protect you, and the compliance that is yours to own.
An outbound call is your agent picking up the phone and dialling someone. A payment failed on a delivery that is already moving, a ₹40,000 cart stalled at checkout, an appointment needs confirming today, a lead filled a form ninety seconds ago — moments where a message sits unread and a call does not.
You trigger it from your own systems. The full request contract — every parameter, every error, the response shape — is in the Calls API reference. This page is the other half: what actually happens on the phone, and what you are on the hook for.
This is a phone ringing in someone's pocket
Everything on this page follows from that. A message that lands badly is ignored. A call that lands badly is an interruption, and at scale it is a complaint. Read the responsible calling section before you ship anything that dials in a loop.
Which agent speaks #
The agent is resolved from the number you call out on, not from anything else in the request.
Every number in your workspace has exactly one agent deployed to it. Pick the caller ID and you have picked the agent, its knowledge base, its instructions and its call language mode. The agent is read as it is currently saved: an edit you save on the agent reaches the next call you place, so try risky changes in the agent's playground first. There is no way to place a call with an agent that is not deployed to the number you are dialling from.
That design is not a limitation to work around; it is what makes the call auditable. The customer sees a number, and that number always maps to one agent. If you need a different agent, call from a different number.
The API's agent_id parameter does not select the agent. It asserts which one you expect, and the call is refused if it does not match what is actually deployed — a loud failure instead of the wrong agent quietly having the wrong conversation. Pass it for anything money-adjacent. The details are in the API reference.
What the customer hears first #
The agent speaks the moment the call connects. The opening is a fixed template — not your inbound greeting, and not generated at call time.
Hello, this is {agent name}, a virtual assistant calling on behalf of {company}. Is now a good time to talk?
Namaste, main {agent name} bol rahi hoon — {company} ki virtual assistant. Kya abhi baat karne ka sahi samay hai?
{agent name} is the agent's name, {company} is your company name as set on the agent. If either is missing you get a generic stand-in, which sounds exactly as vague as it looks — set both.
Three things happen in that one sentence, in this order, and the order is the point:
- It names the business. The person who answers knows within two seconds who is calling. Not a number they do not recognise; a company they have a relationship with.
- It discloses that the caller is a virtual assistant. Before anything is asked of them, and without being asked. Not doing this is the difference between an AI call people tolerate and one they report.
- It asks whether now is a good time. A real question, with a real answer. Someone in a meeting says no, and the call ends there.
Your inbound greeting is deliberately discarded
"Thank you for calling" is wrong on a call you placed, so the greeting configured on the number is not used for outbound. There is nothing to configure here. The opening is the same template on every outbound call. It is in English when the number's opening language (Greet callers in) is English, and in Hindi/English when it is Hindi. For any other opening language it follows the call language: English on English only, Hindi/English otherwise.
How purpose shapes the call #
purpose is the errand. It is free text you send with the request, it reaches the agent as the reason for the call, and it stays in context for the whole conversation.
Write it the way you would brief a colleague who is about to make the call for you — the situation, the outcome you want, and the fallback:
{"purpose": "Confirm Asha's dental appointment on Tuesday 3 October at 4pm, and offer to reschedule if it does not suit."}
{"purpose": "Rohit's autopay for order AM-4821 failed this morning. Tell him the delivery is still scheduled, ask if he wants to pay by UPI link instead, and do not ask for card details on the call."}
Note the second one includes something the agent must not do. Purpose is the right place for that, because it is the only part of the call that knows about this specific customer.
What purpose is not:
- It is not the greeting. The opening line is fixed. Purpose shapes what the agent says after the customer answers the "is now a good time" question.
- It is not knowledge. The agent still answers from the knowledge base bound to it. Facts a customer might ask about — your refund window, your delivery areas — belong in the knowledge base, where every call can use them. Purpose is for what is true about this call only.
- It is not a script. The agent is not going to read it out. A purpose written as dialogue produces a stilted call.
Purpose is stored on the call record, so a transcript months later still shows why the call was placed.
The guards that protect you #
These run before anything is dialled, and the first one that trips is what you get back. They exist because the failure modes they prevent are expensive.
Self-dial is rejected. You cannot call one of your own numbers. Connecting the agent to the agent produces a conversation between two machines, on your minutes, that ends when one of them hits the time cap.
Caller ID must be yours. The number you dial out on has to be an active number in your workspace. You cannot present someone else's number, and you cannot present a number you have released. Number spoofing is not a feature and is not achievable through a misconfiguration.
Destinations are restricted to an allowed list of countries. A number written without a country code is completed using your workspace's own country before the guard runs, so a local number means what it means where you are. A destination outside the allowed list returns 403 country_not_allowed, and satellite, international premium-rate, and other blocked ranges return 403 destination_blocked. This guard runs before the carrier is contacted. The allowed list is India at launch — talk to us if you need to dial elsewhere.
Ten concurrent calls per workspace. A workspace runs at most ten live calls at once. Attempt the eleventh and it is refused with a rate-limit error naming concurrency as the reason — a distinct error from calling the API too fast, and it wants a different response from your code. Your loop is not too quick; your queue is draining faster than calls are ending. Wait for calls to finish rather than retrying immediately.
This cap is also a useful blast radius. A bug in your trigger logic can waste ten simultaneous calls, not ten thousand.
Daily safety budget. A workspace can reserve at most 100 outbound calls or 600 maximum-call minutes per UTC day. Each accepted call reserves its configured worst-case duration before dialling, so a runaway job is stopped even before earlier calls finish. With the standard 10-minute call cap every call reserves 10 minutes, so in practice the minute budget is reached at 60 calls in a day, before the 100-call count. Crossing either limit returns 429 daily_call_budget with the current counts and both limits.
A suspended workspace cannot dial. Billing suspension (an unpaid plan invoice) stops outbound calling and click-to-call before any number is contacted. It is a configuration state, not a transient error — retrying will not clear it. Scheduled call-backs and call campaigns are held rather than failed, use up no attempt, and are placed by themselves once the invoice is paid. See When a payment fails.
An empty credit balance cannot dial either, and it is a different state from suspension. Once your plan's voice minutes are used, calls are charged against prepaid credits; with no minutes left and no credits, a new call is refused with wallet_empty before anything is dialled. The same applies to incoming calls: they are refused before they are answered. A call already in progress always finishes and is charged normally — the floor stops chargeable work starting, never work already running. Like suspension it is not retryable: an owner has to top up.
A number held after a plan change cannot dial. If a plan change took phone numbers away, the number is held for a short period so an upgrade can keep it. While it is held, calls from it are refused with channel_dormant, and calls to it are not answered. Starter includes no voice minutes at all, so a Starter workspace needs credits before its first outbound call.
The exact error codes, reasons, and details payloads for all of these are in the API reference.
Always send an Idempotency-Key
A network timeout on a request that actually succeeded, retried without an idempotency key, is a second phone call to a real person about the same thing. Key it on the business event — cart-88213-recovery, not a random UUID — and a retry replays the original result instead of dialling again. See Idempotency.
After you place the call #
The request returns while the phone is still ringing. Everything the customer experiences happens after your code has moved on.
- Nobody has answered yet. A successful response means the carrier accepted the call. Duration, outcome, and timestamps are all empty by definition at that moment.
- Get the result from
call.ended. Subscribe to the webhook and you are pushed the outcome the instant the call finalises. Polling works, but it spends your API rate limit and tells you nothing until the call is over anyway. - Unanswered calls still finalise. A call that rings out ends with a zero duration and an outcome that says why — nobody answered, the line was busy, the number was invalid. Read the outcome, not the status.
- The transcript and recording behave exactly as they do inbound. Same page, same detail pane, same rules. See How voice works.
Designing the thing that decides to call #
The API call is the easy part. Almost every outbound problem originates in the system that decided to place it, so it is worth building that side deliberately.
Trigger on an event, not on a schedule. "Call everyone in this segment" produces calls with no reason attached, and the person who answers can tell. "Call this customer because their payment failed eleven minutes ago" produces a call with an obvious purpose and a much better reception.
Make the decision idempotent before the request is. The idempotency key protects you from a retried HTTP request. It does not protect you from two different jobs both deciding the same customer needs a call. Record "we called about this event" in your own database, and check it first.
Cap yourself below the platform cap. Ten concurrent calls is the ceiling, not a target. If you are draining a queue, hold your own in-flight count somewhere lower and leave headroom, so a genuinely urgent call is not refused because a batch job filled every slot.
Decide what each ending means. Map the outcome to a next action once, in one place, instead of scattering the logic:
| Outcome | A reasonable next action |
|---|---|
caller_hangup, agent_hangup, agent_ended |
The call happened. Read the transcript if you need the content; otherwise close the task. |
no_answer, busy |
They were unavailable. At most one retry, hours later, never the same minute. |
rejected |
They actively declined. Treat it as a soft signal to stop, not as a reason to try again. |
invalid_number |
The number is wrong. Fix the record — retrying cannot help. |
unreachable, congestion |
A network problem. One retry later is fair. |
idle |
Connected but nobody engaged. Do not immediately redial; look at a few transcripts first. |
max_duration |
The conversation ran long without concluding. A human should look at this one. |
setup_error, originate_error |
Your side and the customer both did nothing wrong. Retry once, then alert. |
Log the call_id against your own record at the moment you place the call, not when it ends. If the process dies in between, the id is the only way back to what happened.
Calling responsibly #
This section is not boilerplate. Outbound calling is the one thing on this platform that can do real damage to people who never asked to hear from you, and to your business through them.
Get consent before you call #
Not implied consent, not "they are a customer so it is fine". A specific, recorded basis for calling this person about this subject: they requested a callback, they bought something and this call is about that purchase, they filled in a form that said you would call. If you cannot point at the moment they agreed, do not place the call.
Keep that basis where your trigger logic can see it, so the check happens before the API call rather than after a complaint.
Respect the time of day #
Use the customer's local time, not your server's. Do not call early in the morning, do not call late at night, and be more conservative than the legal minimum — the legal minimum is not a target. A call at 8:45 p.m. that is technically permitted still lands as an intrusion.
Calls you place through the API are not held to calling hours. There is no quiet-hours window, no per-day cap per recipient, and no scheduling gate on POST /calls. If you place a call at 3 a.m., the call goes out at 3 a.m. Call campaigns are different: they only dial inside their calling window. See Scheduling.
Keep your own suppression list #
Someone who says "stop calling me" must stop being called — on every number, for every campaign, permanently, and the moment they say it.
WhatsApp opt-outs are handled for you because WhatsApp has a keyword convention to hook into. Voice has no equivalent. A caller who tells your agent to stop calling has told your agent; nothing on the platform records that as a suppression, and the next job that queries your database will call them again.
So build the list yourself:
- Store an explicit do-not-call flag per contact and check it immediately before every call.
- Feed it from every channel a person can use to say no — the call transcript, an email, a support ticket, someone on your team.
- Make adding to it trivial and bypassing it impossible. A suppression list a campaign can skip is not a suppression list.
- Never delete an entry to "re-permission" someone.
Do not use this for cold bulk calling #
Placing calls to people who have no relationship with you and did not ask to hear from you is not what this channel is for. It generates complaints, gets numbers blocked, and puts your business on the wrong side of telecom regulation. There is no configuration that makes it acceptable.
Compliance is yours, and it is not optional
Amomic-AI places the call you ask for. It does not check the destination against any do-not-call, do-not-disturb, or preference registry. It does not check DND. It does not enforce calling hours. It does not cap calls per recipient per day. There is no consent registry and no built-in opt-out mechanism on this channel.
Compliance with Indian telecom regulation — including the DND registries, registration and scrubbing obligations, and the rules on unsolicited commercial communication — is the caller's responsibility, which means yours. Build those checks into the system that decides whom to call, before it ever reaches the API. Verify the current requirements with your own legal counsel; they change, and this page is documentation, not legal advice.
Next #
The API reference
POST /calls — parameters, responses, every error code, and the idempotency rules.
Trigger a call from your CRM
The full recipe — trigger, idempotency, webhook, and writing the outcome back.
How voice works
Transcripts, recording, statuses and outcomes — identical for outbound and inbound.
Get a number to call from
Get a phone number — the caller ID is also the agent selector.