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:

bash
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:

text
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.
bash
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.

javascript
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

bash
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.

bash
# 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

Dashboard Welcome