Skip to main content

Welcome to the TopCalls API

The TopCalls API is a RESTful API that lets you build AI-powered phone agents. Make calls, manage phone numbers, check available models and voices, and integrate with your systems via webhooks.

RESTful Design

Standard HTTP methods and JSON responses. Works with any programming language.

Interactive

Try API endpoints directly in this documentation. See responses in real-time.

Webhooks

Real-time notifications for call events, tool calls, and campaign updates.

Well Documented

Detailed schemas, examples, and error responses for every endpoint.

Base URL

All API requests should be made to:
https://api.topcalls.ai

Authentication

All API requests require authentication. Include your API key in the Authorization header:
Authorization: Bearer YOUR_API_KEY
Get your API key from topcalls.ai. Sign in and create a new key in your account settings.
API keys carry scopes — each scope grants access to one group of endpoints. When you create a key in the dashboard, grant only the scopes it needs. A request to an endpoint outside the key’s scopes returns 403 Forbidden, and the response names the scope that was missing.
ScopeGrants access to
calls:readList and read calls (details, transcripts, recordings), plus account balance, usage, and call analytics
calls:writeCreate, stop, and cancel calls
campaigns:readList campaigns and read campaign details and stats
campaigns:writeCreate, update, start, pause, resume, stop, and test campaigns
phone_numbers:readList phone numbers and carriers, and read phone number details
phone_numbers:writeAdd and remove phone numbers, and manage custom carriers (BYOC)
leads:readList and read leads and lead lists
leads:writeCreate, update, delete, and bulk-import leads, and create lead lists
knowledge_base:readList and read knowledge base entries
knowledge_base:writeCreate, update, and delete knowledge base entries
webhooks:readList and read webhook subscriptions
webhooks:writeCreate, update, and delete webhook subscriptions
A read scope never grants writes: a calls:read key can list calls but cannot place one. The model and voice catalog endpoints (/v1/models, /v1/voices) require no scope — any valid key can read them.

API Versioning

The current API version is v1. All endpoints are prefixed with /v1:
https://api.topcalls.ai/v1/calls

Response Format

All responses are JSON. Success responses include the requested data:
{
  "call_id": "564d4fd4-03bc-400a-abe0-05540fbeff88",
  "status": "queued"
}
Error responses follow RFC 7807 Problem+JSON format:
{
  "status": 400,
  "title": "Invalid request body",
  "detail": "Phone number must be in E.164 format",
  "errors": [
    {
      "path": "phone_number",
      "message": "Phone number must be in E.164 format (e.g., +14155551234)"
    }
  ]
}

Quick Start

1. Make Your First Call

curl -X POST https://api.topcalls.ai/v1/calls \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "phone_number": "+14155551234",
    "task": "Call to confirm appointment...",
    "voice": "alloy"
  }'

2. Check Call Status

curl https://api.topcalls.ai/v1/calls/CALL_ID \
  -H "Authorization: Bearer YOUR_API_KEY"

3. List Calls

curl https://api.topcalls.ai/v1/calls \
  -H "Authorization: Bearer YOUR_API_KEY"

API Endpoints

Calls

MethodEndpointDescription
GET/v1/callsList calls
GET/v1/calls/{call_id}Get call details
POST/v1/callsCreate a new call
POST/v1/calls/{call_id}/stopStop an active call
POST/v1/calls/{call_id}/cancelCancel a queued call

Campaigns

Create, configure, and run campaigns from the API or the TopCalls dashboard:
MethodEndpointDescription
POST/v1/campaignsCreate a campaign
GET/v1/campaignsList campaigns
GET/v1/campaigns/{campaign_id}Get campaign details
GET/v1/campaigns/{campaign_id}/statsGet campaign stats
PATCH/v1/campaigns/{campaign_id}Update a campaign
POST/v1/campaigns/{campaign_id}/startStart a campaign
POST/v1/campaigns/{campaign_id}/pausePause a running campaign
POST/v1/campaigns/{campaign_id}/resumeResume a paused campaign
POST/v1/campaigns/{campaign_id}/stopStop a campaign
POST/v1/campaigns/{campaign_id}/testPlace a test call for a campaign

Phone Numbers

MethodEndpointDescription
GET/v1/phone-numbers/carriersList carriers
GET/v1/phone-numbersList phone numbers
GET/v1/phone-numbers/{phone_number_id}Get phone number details
POST/v1/phone-numbers/carriersAdd custom carrier (BYOC)
POST/v1/phone-numbersAdd phone number
PATCH/v1/phone-numbers/{phone_number_id}Update phone number label/status
DELETE/v1/phone-numbers/carriers/{carrier_id}Delete custom carrier
DELETE/v1/phone-numbers/{phone_number_id}Delete phone number

Leads

MethodEndpointDescription
POST/v1/leadsCreate a lead (idempotent per phone number)
GET/v1/leadsList leads with cursor pagination and filters
GET/v1/leads/{id}Get lead details
PATCH/v1/leads/{id}Update a lead
DELETE/v1/leads/{id}Delete a lead
POST/v1/leads/bulkBulk import leads
POST/v1/leads/bulk-deleteBulk delete leads
GET/v1/leads/bulk-delete/{job_id}Poll an async bulk-delete job

Lead Lists

MethodEndpointDescription
GET/v1/lead-listsList lead lists
GET/v1/lead-lists/{lead_list_id}Get lead list details
POST/v1/lead-listsCreate a lead list
PATCH/v1/lead-lists/{lead_list_id}Update a lead list

Knowledge Bases

MethodEndpointDescription
GET/v1/knowledge-basesList knowledge base entries
GET/v1/knowledge-bases/{id}Get a knowledge base entry
POST/v1/knowledge-basesCreate a knowledge base entry
PATCH/v1/knowledge-bases/{id}Update a knowledge base entry
DELETE/v1/knowledge-bases/{id}Delete a knowledge base entry

Webhooks

MethodEndpointDescription
POST/v1/webhooksCreate a webhook subscription
GET/v1/webhooksList webhook subscriptions
GET/v1/webhooks/{webhook_id}Get a webhook subscription
PATCH/v1/webhooks/{webhook_id}Update a webhook subscription
DELETE/v1/webhooks/{webhook_id}Delete a webhook subscription

Configuration

MethodEndpointDescription
GET/v1/modelsList available AI models
GET/v1/voices/builtinList built-in voices (with filters)
GET/v1/voicesList voices for your account (including custom/cloned)

Account

MethodEndpointDescription
GET/v1/balanceGet account balance and usage
GET/v1/usage/movementsList usage ledger movements
GET/v1/analytics/callsDaily call analytics

Webhooks

TopCalls sends webhooks to your server when calls finish. Set webhook_url on a call for per-call delivery:
{
  "webhook_url": "https://your-app.com/webhooks/call-complete"
}
Or create an account-level subscription with POST /v1/webhooks, including disposition-suffixed events like call.completed.booked_callback. See the Webhooks Guide for complete documentation.

Idempotency

Mutating endpoints accept an optional Idempotency-Key header (8-255 characters, [A-Za-z0-9_-]). The gateway caches the response for 24 hours and returns the same response on retries with the same key. A key reused while the first request is still running returns 409.

Rate Limits

Two limits apply, both counted per account. Every API key on the same account shares them.
  • All endpoints: 120 requests per minute.
  • POST /v1/calls: also limited to your account’s max_calls_per_minute setting (default 20) per minute.
When you go over a limit, the API returns 429 Too Many Requests with a Retry-After header and a Problem+JSON body:
HTTP/1.1 429 Too Many Requests
Retry-After: 60
Content-Type: application/problem+json

{
  "status": 429,
  "title": "Too Many Requests",
  "detail": "Your account has reached its per-minute call rate limit (max_calls_per_minute). Wait for the number of seconds in the Retry-After header, then retry.",
  "code": "RATE_LIMITED"
}
What to do when you get a 429
  1. Read the Retry-After header. Its value is the number of seconds to wait before trying again.
  2. Wait that long, then send the same request again.
  3. If you keep getting 429, you are sending faster than your limit allows. Lower your request rate, or spread the work out over time.
  4. In automated clients, check for status 429 (or the code field equal to RATE_LIMITED) and back off: wait the Retry-After seconds on the first 429, then double the wait on each repeat until the request succeeds.
The limits are per account, so adding more API keys or servers against the same account does not raise the ceiling. For steady high call volume, run a campaign instead of sending individual POST /v1/calls requests, or contact us to raise your max_calls_per_minute.

Error Codes

Status CodeDescription
200Success
201Created
400Bad Request - Invalid input
401Unauthorized - Invalid or missing API key
402Payment Required - Insufficient quota
403Forbidden - Insufficient permissions
404Not Found - Resource doesn’t exist
409Conflict - Idempotency key reused while the original request is in flight
413Payload Too Large - Too many items in a bulk request
422Unprocessable - Prompt failed the automated compliance review (see the code field, e.g. PROMPT_SAFETY_FRAUD), or the request could not be processed
429Too Many Requests - rate limit exceeded. Wait for the Retry-After header, then retry. See Rate Limits
500Internal Server Error

SDKs & Libraries

Official SDKs coming soon. For now, use any HTTP client library:
  • JavaScript/TypeScript: fetch, axios
  • Python: requests
  • Ruby: httparty
  • Go: net/http
  • PHP: guzzle

Support

Next Steps

Quickstart

Make your first call in 5 minutes.

Making Calls Guide

Learn how to make calls with the API.