Place an outbound call
This guide covers placing a single call with per-call data, then scaling the same agent to thousands of recipients.
A single call
You need a phone agent and a destination number in E.164 format:
curl -X POST "https://api.rabbitt.ai/api/v1/calls" \
-H "Authorization: Bearer $RABBITT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"agentId": "507f1f77bcf86cd799439011",
"phoneNumber": "+919876543210",
"contactName": "Asha Menon",
"externalRef": "booking-8842"
}'The response comes back as soon as the carrier accepts the request, with
status: "created". The phone rings a moment later.
Personalising each call
Hard-coding a name into an agent means one agent per person. Instead, declare
{{variables}} in the agent's instructions and supply values per call:
You are calling {{customerName}} about invoice {{invoiceNumber}},
which is {{daysOverdue}} days overdue for {{amount}} rupees.
Ask when they can pay. Be polite and never threaten.
If they dispute the amount, say a colleague will call back.curl -X POST "https://api.rabbitt.ai/api/v1/calls" \
-H "Authorization: Bearer $RABBITT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"agentId": "507f1f77bcf86cd799439011",
"phoneNumber": "+919876543210",
"variables": {
"customerName": "Asha Menon",
"invoiceNumber": "INV-2291",
"daysOverdue": "14",
"amount": "4200"
}
}'One agent, any number of customers.
Waiting for the outcome
Poll the call until it completes, then poll the report. Poll every few seconds, not every few hundred milliseconds — see rate limits.
async function waitForReport(callId, { timeoutMs = 15 * 60_000 } = {}) {
const headers = { Authorization: `Bearer ${process.env.RABBITT_API_KEY}` };
const deadline = Date.now() + timeoutMs;
while (Date.now() < deadline) {
const response = await fetch(
`https://api.rabbitt.ai/api/v1/calls/${callId}/report`,
{ headers },
);
// 202 means the call has not finished, or the analysis is still running.
if (response.status === 200) return response.json();
if (response.status !== 202) {
const { error } = await response.json();
throw new Error(`${error.code}: ${error.message}`);
}
await new Promise((resolve) => setTimeout(resolve, 5000));
}
throw new Error(`Report for ${callId} did not arrive in time`);
}Ending a call early
curl -X POST "https://api.rabbitt.ai/api/v1/calls/CALL_ID/hangup" \
-H "Authorization: Bearer $RABBITT_API_KEY"Safe to call more than once — on an already-finished call it returns the call unchanged.
Scaling to thousands
Do not loop over POST /v1/calls. Your organization has a maxConcurrentCalls limit, and
a loop will simply queue against it while burning your request budget and handling retries
yourself.
Use a campaign instead. It takes the whole recipient list, paces the calls within your concurrency limit, and retries failures on your schedule.
# 1. Create the campaign
curl -X POST "https://api.rabbitt.ai/api/v1/campaigns" \
-H "Authorization: Bearer $RABBITT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "January overdue invoices",
"agentId": "507f1f77bcf86cd799439011",
"timezone": "Asia/Kolkata",
"settings": { "retryLimit": 3, "retryDelayMinutes": 60, "concurrency": 5 }
}'
# 2. Import recipients, then launch
curl -X POST "https://api.rabbitt.ai/api/v1/campaigns/CAMPAIGN_ID/launch" \
-H "Authorization: Bearer $RABBITT_API_KEY"Track progress with GET /v1/campaigns/{id}, which returns live stats for total,
pending, completed, and failed recipients.
Next
- Calls — lifecycle, transcripts, and billing
- Reports — reading the outcome
- Campaigns reference