Skip to content

Developers

One REST API, for your dashboard and for you.

The dashboard uses the same /v1 API that your servers do. Agents, numbers, contacts, calls, campaigns, triggers, webhooks, calling hours and your statement all work with an API key.

A few things happen only in the dashboard, signed in as a person: recharges and the auto-recharge mandate, API keys, team members and invites, your business details, and test calls to your own verified mobile. The reference below marks them “Dashboard only”.

The OpenAPI 3 document is at https://api.call2all.in/v1/openapi.json.

01

Authentication

Base URL https://api.call2all.in/v1. Send your API key as a bearer token. Keys start with c2a_live_, are stored only as a hash, and carry scopes such as calls:write: a key without the scope an endpoint needs gets 403 scope_missing. Create keys in the dashboard; a key can never create another key.

curl https://api.call2all.in/v1/balance \
  -H "Authorization: Bearer $CALL2ALL_KEY"

02

Errors

Every error has the same shape and a stable code you can branch on; the message is written for a person. Quote the request_id (also in the x-request-id header) when you write to support.

Error body
{
  "error": {
    "code": "invalid_request",
    "message": "Some fields are not valid.",
    "details": { "fields": { "to": "Enter a valid Indian phone number, e.g. +91 98765 43210." } },
    "request_id": "req_01J…"
  }
}
400
invalid_request (details.fields names each bad field), idempotency_key_required, token_invalid, csv_invalid
401
unauthenticated, invalid_credentials
403
forbidden, scope_missing, session_required, account_suspended, opt_out_protected
404
not_found, recording_not_found
409
idempotency_conflict, idempotency_in_progress, in_debt, balance_too_low, external_ref_taken, kyc_required, number_in_use, campaign_locked
413 · 415
payload_too_large, file_too_large · unsupported_media_type, unsupported_file_type
422
a rule said no: not_india, on_dnc, marketing_not_allowed, limit_reached, agent_inactive, balance_too_low, recharge_too_small
429
rate_limited (details.retry_after_s), too_many_attempts
500 · 502
internal · telephony_unavailable, payment_gateway_error. Never a stack trace or a partner’s own text

Common codes by status. Each endpoint in the reference lists the statuses it can answer with.

03

Idempotency

POST requests that create something or spend money take an Idempotency-Key header: 1 to 100 visible ASCII characters, kept for 24 hours per account. Each endpoint in the reference says whether it takes one.

  • The same key with the same method, URL and body returns the first answer again, with the header idempotent-replayed: true.
  • The same key with a different request, or sent by another API key or user, gets 409 idempotency_conflict.
  • The same key while the first request is still running gets 409 idempotency_in_progress: retry in a moment.
  • A request that failed is forgotten, so you can retry it with the same key.
  • Renting numbers (POST /v1/numbers) and buying lines (POST /v1/lines) need a key: without one the answer is 400 idempotency_key_required.
curl https://api.call2all.in/v1/calls \
  -X POST \
  -H "Authorization: Bearer $CALL2ALL_KEY" \
  -H "Idempotency-Key: order-58213-confirm" \
  -H "Content-Type: application/json" \
  -d '{ "agent_id": "agt_…", "to": "+919876543210" }'

04

Pagination

Lists take limit (1 to 100, default 25) and cursor, and return { "data": [...], "next_cursor": "…" | null }, newest first. Keep passing next_cursor back until it is null. A few short lists, such as API keys, live calls and the catalog, come back whole.

curl "https://api.call2all.in/v1/contacts?limit=100" \
  -H "Authorization: Bearer $CALL2ALL_KEY"

# then pass next_cursor back until it is null
curl "https://api.call2all.in/v1/contacts?limit=100&cursor=<next_cursor>" \
  -H "Authorization: Bearer $CALL2ALL_KEY"

05

Rate limits

Each API key can make 300 requests a minute (a browser session: per IP address). Over that, the answer is 429 rate_limited with details.retry_after_s. Some endpoints have a lower limit of their own, such as testing a webhook endpoint, replaying a delivery or a trigger's hook URL. Request bodies are capped at 1 MB; file uploads have their own limits.

06

Resources

Every resource has a prefixed id and a created_at in ISO-8601 UTC. Where it makes sense, it also takes metadata (up to 20 keys of up to 40 characters; values are text up to 500 characters, numbers, true/false or null) and external_ref (up to 100 characters, for your own id). Money is always an object in paise with a display string. Phone numbers come back in E.164; you may send any Indian format.

Field conventions
{
  "id": "con_…",
  "created_at": "2026-10-28T08:35:00.000Z",
  "external_ref": "client-4471",
  "metadata": { "store": "pune-2" },
  "phone": "+919876543210"
}

// money, wherever it appears
{ "paise": 45000, "display": "₹450.00" }

07

Webhooks

Each delivery is a JSON POST of { id, type, created_at, data }. v1 is the HMAC-SHA256 of <t>.<raw body> with your endpoint's secret; verify it before you parse the JSON. Any answer other than 2xx, a timeout (8 s) or a network error is retried after 10 s, 30 s, 2 min, 10 min, 30 min, 1 h, 2 h, 4 h, then every 6 h, until 24 hours after the event. Every delivery shows in the dashboard and through GET /v1/webhook-deliveries, and can be sent again.

Call2all-Signature
t=<unix>,v1=<hex>
Call2all-Event
the event type, e.g. call.ended
Call2all-Event-Id
evt_…, the same on every retry: de-duplicate on it
Call2all-Delivery
whd_…, this delivery
Verify a signature (Node)
import { createHmac, timingSafeEqual } from "node:crypto";

// header: the Call2all-Signature value, e.g. "t=1761640500,v1=5f2c…"
// rawBody: the request body exactly as received (a string, before JSON.parse)
// secret: the endpoint's whole secret, "whsec_…"
export function verifyCall2all(header, rawBody, secret, toleranceSeconds = 300) {
  const parts = Object.fromEntries(
    header.split(",").map((part) => part.trim().split("=", 2)),
  );
  const t = Number(parts.t);
  if (!Number.isInteger(t) || !parts.v1) return false;
  // Refuse old deliveries so a captured request can't be replayed later.
  if (Math.abs(Date.now() / 1000 - t) > toleranceSeconds) return false;

  const expected = createHmac("sha256", secret)
    .update(`${parts.t}.${rawBody}`)
    .digest();
  const given = Buffer.from(parts.v1, "hex");
  return given.length === expected.length && timingSafeEqual(given, expected);
}
Calls
call.started, call.ended, call.analyzed
Campaigns
campaign.started, campaign.completed
Contacts
contact.opted_out, import.completed
Numbers and KYC
number.held, number.released, kyc.updated
Money
balance.low, recharge.succeeded
Testing
webhook.test (only from the “send test” button; not subscribable)

08

Price list

GET /v1/prices needs no key. It returns the current per-minute prices, monthly charges and the Silver and Gold tiers. The pricing page on this site reads the same endpoint.

curl
curl https://api.call2all.in/v1/prices

09

Endpoint reference

Every endpoint, grouped by area, with the scope it needs. Open one to see its parameters, request body and response fields. It is generated from the API's own schemas, so it changes when the API does.

159 endpoints in 22 groups

System1

GET/v1/healthNo key needed

Catalog7

GET/v1/pricesCurrent price list and recharge tiers (public)No key needed
GET/v1/catalog/gst-statesNo key needed
GET/v1/catalog/voices/{id}/previewHear a voice: its stored preview recording, made onceAny API key
GET/v1/catalog/modelsGPT models for agents, with per-minute pricesAny API key
GET/v1/catalog/voicesVoices, optionally for one languageAny API key
GET/v1/catalog/languagesCall languagesAny API key
POST/v1/quotesPer-minute price of an unsaved agent config or message callAny API key

Auth (dashboard)10

POST/v1/auth/signupNo key needed
POST/v1/auth/loginNo key needed
POST/v1/auth/logoutDashboard only
GET/v1/auth/meDashboard only
POST/v1/auth/verify-emailNo key needed
POST/v1/auth/resend-verificationDashboard only
POST/v1/auth/forgot-passwordNo key needed
POST/v1/auth/reset-passwordNo key needed
GET/v1/auth/invites/{token}Preview an invite: account, inviter, email and role. 400 token_invalid / token_expired / token_usedNo key needed
POST/v1/auth/accept-inviteAccept an invite: creates your login in that account (email already verified) and signs you inNo key needed

Account2

GET/v1/accountScope account:read
PATCH/v1/accountUpdate the account name and billing details (dashboard; owner or admin)Dashboard only

Billing22

GET/v1/balancePrepaid BalanceScope billing:read
GET/v1/ledgerStatement: every change to the Prepaid Balance, newest firstScope billing:read
GET/v1/lotsRecharge lots and what each has left, newest firstScope billing:read
GET/v1/recharges/termsThe non-refundable acknowledgement shown before every rechargeNo key needed
POST/v1/recharges/quoteGST and tier for a recharge value (dashboard)Dashboard only
GET/v1/rechargesScope billing:read
POST/v1/rechargesStart a recharge: creates the payment order (dashboard; owner or admin)Dashboard only
POST/v1/recharges/verifyConfirm a Checkout payment (dashboard)Dashboard only
GET/v1/recharges/{id}Scope billing:read
GET/v1/invoicesScope billing:read
GET/v1/invoices/{id}.pdfOne GST tax invoice as a PDFScope billing:read
GET/v1/invoices/{id}One GST invoice with seller and buyer detailsScope billing:read
GET/v1/billing/auto-rechargeAuto-recharge settings, mandate, the debit in progress and this month's totalScope billing:read
PUT/v1/billing/auto-rechargeChange the trigger, value or monthly cap, or switch Auto-recharge on/off (dashboard; owner or admin)Dashboard only
GET/v1/billing/auto-recharge/termsThe recharge acknowledgement and the autopay consent ticked when setting up Auto-rechargeNo key needed
POST/v1/billing/auto-recharge/mandateSet up the autopay mandate: returns Razorpay Checkout options for the approval (dashboard; owner or admin)Dashboard only
DELETE/v1/billing/auto-recharge/mandateCancel the autopay mandate here and at Razorpay, and switch Auto-recharge off (dashboard; owner or admin)Dashboard only
POST/v1/billing/auto-recharge/mandate/verifyConfirm the autopay approval after Checkout (dashboard). The mandate's state is read from RazorpayDashboard only
GET/v1/billing/alertsLow-balance alert settingsScope billing:read
PUT/v1/billing/alertsChange the low-balance alert (owner/admin, or an API key with settings:write)Scope settings:write
GET/v1/usageUsage totals by IST day, month or charge kind (list price, discount, paid)Scope billing:read
GET/v1/statement.csvThe statement as CSV: every ledger line with list, discount, amount, lot and balance afterScope billing:read

Api keys (dashboard)3

GET/v1/api-keysDashboard only
POST/v1/api-keysDashboard only
DELETE/v1/api-keys/{id}Dashboard only

Contacts15

GET/v1/contactsList contacts (search and filters; newest first)Scope contacts:read
POST/v1/contactsCreate a contact (409 contact_exists if the number already has one)Scope contacts:write
POST/v1/contacts/bulkCreate or update up to 1,000 contacts by phone (per-item results)Scope contacts:write
GET/v1/contacts/exportDownload contacts as CSV (same filters as GET /contacts)Scope contacts:read
GET/v1/contacts/by-phone/{phone}Find the contact for a phone number (any Indian format)Scope contacts:read
GET/v1/contacts/{id}One contactScope contacts:read
PATCH/v1/contacts/{id}Update a contact (attributes and metadata are merged; tags are replaced)Scope contacts:write
DELETE/v1/contacts/{id}Delete a contactScope contacts:write
GET/v1/contact-fieldsThe account's custom contact fields, in display orderScope contacts:read
POST/v1/contact-fieldsAdd a custom contact fieldScope contacts:write
GET/v1/contact-fields/undeclaredAttribute keys found on contacts that no field declares yetScope contacts:read
GET/v1/contact-fields/{id}One custom contact fieldScope contacts:read
PATCH/v1/contact-fields/{id}Change a custom contact field (its key never changes)Scope contacts:write
DELETE/v1/contact-fields/{id}Delete a custom contact field (the values on your contacts are kept)Scope contacts:write
POST/v1/contact-fields/orderSet the order the fields are shown inScope contacts:write

Lists8

GET/v1/listsList contact lists (newest first)Scope contacts:read
POST/v1/listsCreate a listScope contacts:write
GET/v1/lists/{id}One list with its member countScope contacts:read
PATCH/v1/lists/{id}Rename or describe a listScope contacts:write
DELETE/v1/lists/{id}Delete a list (its contacts stay)Scope contacts:write
GET/v1/lists/{id}/membersContacts in a list (newest first; same search as GET /contacts)Scope contacts:read
POST/v1/lists/{id}/membersAdd contacts to a list by id and/or phone (a phone with no contact gets one)Scope contacts:write
DELETE/v1/lists/{id}/membersRemove contacts from a list (the contacts stay)Scope contacts:write

Imports4

GET/v1/importsYour imports (newest first)Scope contacts:read
POST/v1/importsImport contacts from CSV text (processed in the background)Scope contacts:write
GET/v1/imports/{id}One import: status, progress and totalsScope contacts:read
GET/v1/imports/{id}/errors.csvRows that were not saved (CSV: row, value, reason)Scope contacts:read

Do-not-call4

GET/v1/dncThe do-not-call list (newest first)Scope contacts:read
POST/v1/dncAdd a number to the do-not-call list (idempotent)Scope contacts:write
POST/v1/dnc/bulkAdd up to 1,000 numbers to the do-not-call listScope contacts:write
DELETE/v1/dnc/{phone}Remove a number from the do-not-call listScope contacts:write

Calling hours5

GET/v1/settings/callingCalling hours, quiet days and frequency capsScope settings:read
PATCH/v1/settings/callingChange calling hours, quiet days or frequency capsScope settings:write
GET/v1/holidaysHolidays from today (IST) onwards, earliest firstScope settings:read
POST/v1/holidaysAdd a holiday (no outbound calls, no inbound business hours, or both)Scope settings:write
DELETE/v1/holidays/{id}Remove a holidayScope settings:write

Agents7

GET/v1/agentsList agents, newest firstScope agents:read
POST/v1/agentsCreate an agentScope agents:write
GET/v1/agents/{id}Get an agentScope agents:read
PATCH/v1/agents/{id}Change an agentScope agents:write
DELETE/v1/agents/{id}Delete an agentScope agents:write
POST/v1/agents/{id}/duplicateCopy an agentScope agents:write
POST/v1/agents/{id}/test-callTest call: the agent rings a number you choose (dashboard session)Dashboard only

Http tools6

GET/v1/toolsList HTTP tools, newest firstScope agents:read
POST/v1/toolsCreate an HTTP toolScope agents:write
GET/v1/tools/{id}Get an HTTP toolScope agents:read
PATCH/v1/tools/{id}Change an HTTP toolScope agents:write
DELETE/v1/tools/{id}Delete an HTTP toolScope agents:write
POST/v1/tools/{id}/testRun an HTTP tool once with test argumentsScope agents:write

Knowledge6

GET/v1/knowledgeList knowledge sources, newest firstScope agents:read
POST/v1/knowledgeAdd reference material an agent can search during a callScope agents:write
GET/v1/knowledge/{id}One knowledge sourceScope agents:read
PATCH/v1/knowledge/{id}Rename a knowledge sourceScope agents:write
DELETE/v1/knowledge/{id}Delete a knowledge sourceScope agents:write
POST/v1/agents/{id}/knowledge-searchTry a search the way the agent would make it on a callScope agents:read

Numbers10

GET/v1/numbers/citiesCities you can search for local numbers, with their area (STD) codesScope numbers:read
GET/v1/numbers/availableSearch numbers you can rent, by city, area code and digitsScope numbers:read
GET/v1/numbersYour numbers, newest first (active and held by default)Scope numbers:read
POST/v1/numbersRent a number: the first month's rent is taken from your Prepaid Balance now (Idempotency-Key required)Scope numbers:write
GET/v1/numbers/{id}One numberScope numbers:read
PATCH/v1/numbers/{id}Change a number's settings: agent, mode, forwarding, recording, business hours, after-hours actionScope numbers:write
DELETE/v1/numbers/{id}Release and delete a number now. Permanent; rent stops; the current month is not refundedScope numbers:write
GET/v1/linesConcurrent call lines: included with numbers, plus extra linesScope numbers:read
POST/v1/linesBuy extra lines: each line's first month is taken from your Prepaid Balance now (Idempotency-Key required)Scope numbers:write
DELETE/v1/lines/{id}Remove an extra line. It stays until the end of the month already paid (a held line goes at once)Scope numbers:write

Kyc2

GET/v1/kycYour business verification (status draft until submitted)Scope numbers:read
POST/v1/kycSubmit business verification (or correct and resubmit a rejected one). Upload the two documents with PUT /v1/files firstScope numbers:write

Files2

PUT/v1/filesUpload a file as the raw request body (kyc: PDF/PNG/JPEG ≤ 5 MB; audio: MP3/WAV ≤ 10 MB)Scope numbers:write or campaigns:write or agents:write
GET/v1/files/{id}Download one of your filesScope numbers:read or campaigns:read

Webhooks10

GET/v1/webhook-endpointsList webhook endpoints (at most 10 per account), with the event types you can subscribe toScope webhooks:read
POST/v1/webhook-endpointsAdd a webhook endpoint. The response holds the signing secret; it is never shown againScope webhooks:write
GET/v1/webhook-endpoints/{id}One webhook endpointScope webhooks:read
PATCH/v1/webhook-endpoints/{id}Change a webhook endpoint (URL, events, numbers, description, enabled, metadata, external_ref)Scope webhooks:write
DELETE/v1/webhook-endpoints/{id}Delete a webhook endpoint. Its pending deliveries stop; the delivery log is keptScope webhooks:write
POST/v1/webhook-endpoints/{id}/rotate-secretReplace the signing secret. The new secret is shown only in this response and signs every attempt from now onScope webhooks:write
POST/v1/webhook-endpoints/{id}/testSend a webhook.test event to this endpoint only, now, and return the delivery with the attempt's resultScope webhooks:write
GET/v1/webhook-deliveriesThe delivery log, newest firstScope webhooks:read
GET/v1/webhook-deliveries/{id}One delivery: the exact body sent and every attempt (time, status code, error, response excerpt, duration)Scope webhooks:read
POST/v1/webhook-deliveries/{id}/replaySend this delivery again now (also when it failed or already succeeded) and return the resultScope webhooks:write

Team (dashboard)6

GET/v1/teamTeam members and pending invitesDashboard only
POST/v1/team/invitesInvite a person by email (an owner may invite admins; an admin may invite members and viewers)Dashboard only
POST/v1/team/invites/{id}/resendSend the invite again with a new link (the old link stops working) and 7 more daysDashboard only
DELETE/v1/team/invites/{id}Cancel a pending inviteDashboard only
PATCH/v1/team/members/{userId}Change a member's roleDashboard only
DELETE/v1/team/members/{userId}Remove a member: their login is disabled and every session endsDashboard only

Calls7

GET/v1/callsList calls, newest firstScope calls:read
POST/v1/callsQueue one outbound callScope calls:write
GET/v1/calls/liveCalls on the line now (dialing, ringing or in progress) and the account's linesScope calls:read
GET/v1/calls/statsCall KPIs for today and this month (IST): calls, answered, minutes billed, spend; plus the last 14 IST daysScope calls:read
GET/v1/calls/{id}One call: transcript, analysis, recording, exact cost and timelineScope calls:read
DELETE/v1/calls/{id}Cancel a call that is still waiting to be placedScope calls:write
GET/v1/calls/{id}/recordingA short-lived link to the call's recordingScope calls:read

Campaigns13

GET/v1/campaigns/attestationThe service/transactional attestation every campaign stores (show it, then send its version)Scope campaigns:read
GET/v1/campaignsList campaigns (newest first) with live countersScope campaigns:read
POST/v1/campaignsCreate a campaign (draft)Scope campaigns:write
POST/v1/campaigns/previewWho would be called and what it may cost (nothing is saved)Scope campaigns:read
GET/v1/campaigns/{id}Get a campaign with live countersScope campaigns:read
PATCH/v1/campaigns/{id}Change a campaignScope campaigns:write
POST/v1/campaigns/{id}/startStart a draft campaignScope campaigns:write
POST/v1/campaigns/{id}/pausePause a running or scheduled campaignScope campaigns:write
POST/v1/campaigns/{id}/resumeResume a paused campaignScope campaigns:write
POST/v1/campaigns/{id}/cancelCancel a campaignScope campaigns:write
GET/v1/campaigns/{id}/historyWhat happened to this campaign, newest first (including what the system did on its own)Scope campaigns:read
GET/v1/campaigns/{id}/recipientsThe campaign's people, in dialling orderScope campaigns:read
GET/v1/campaigns/{id}/export.csvDownload the campaign's results as CSVScope campaigns:read

Triggers9

GET/v1/triggersList triggers (newest first) with today's firesScope triggers:read
POST/v1/triggersCreate a triggerScope triggers:write
POST/v1/triggers/previewWho a date-field trigger would call in the next 24 hours (nothing is saved)Scope triggers:read
GET/v1/triggers/{id}Get a triggerScope triggers:read
PATCH/v1/triggers/{id}Change a triggerScope triggers:write
DELETE/v1/triggers/{id}Delete a triggerScope triggers:write
POST/v1/triggers/{id}/rotate-tokenReplace a webhook trigger's token (the old URL stops at once)Scope triggers:write
GET/v1/triggers/{id}/firesWhat the trigger did, newest first: the call it queued, or why it didn'tScope triggers:read
POST/v1/hooks/{token}The incoming URL of a webhook trigger (no API key: the token in the URL is the credential)No key needed