> ## Documentation Index
> Fetch the complete documentation index at: https://docs.topcalls.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# API Reference

> Complete REST API documentation for TopCalls. Build AI phone agents with our API.

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

<CardGroup cols={2}>
  <Card title="RESTful Design" icon="code">
    Standard HTTP methods and JSON responses. Works with any programming language.
  </Card>

  <Card title="Interactive" icon="play">
    Try API endpoints directly in this documentation. See responses in real-time.
  </Card>

  <Card title="Webhooks" icon="webhook">
    Real-time notifications for call events, tool calls, and campaign updates.
  </Card>

  <Card title="Well Documented" icon="file-lines">
    Detailed schemas, examples, and error responses for every endpoint.
  </Card>
</CardGroup>

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

```bash theme={null}
Authorization: Bearer YOUR_API_KEY
```

<Note>
  Get your API key from [topcalls.ai](https://www.topcalls.ai/auth/sign-in). Sign in and create a new key in your account settings.
</Note>

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.

| Scope                  | Grants access to                                                                                        |
| ---------------------- | ------------------------------------------------------------------------------------------------------- |
| `calls:read`           | List and read calls (details, transcripts, recordings), plus account balance, usage, and call analytics |
| `calls:write`          | Create, stop, and cancel calls                                                                          |
| `campaigns:read`       | List campaigns and read campaign details and stats                                                      |
| `campaigns:write`      | Create, update, start, pause, resume, stop, and test campaigns                                          |
| `phone_numbers:read`   | List phone numbers and carriers, and read phone number details                                          |
| `phone_numbers:write`  | Add and remove phone numbers, and manage custom carriers (BYOC)                                         |
| `leads:read`           | List and read leads and lead lists                                                                      |
| `leads:write`          | Create, update, delete, and bulk-import leads, and create lead lists                                    |
| `knowledge_base:read`  | List and read knowledge base entries                                                                    |
| `knowledge_base:write` | Create, update, and delete knowledge base entries                                                       |
| `webhooks:read`        | List and read webhook subscriptions                                                                     |
| `webhooks:write`       | Create, 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:

```json theme={null}
{
  "call_id": "564d4fd4-03bc-400a-abe0-05540fbeff88",
  "status": "queued"
}
```

Error responses follow RFC 7807 Problem+JSON format:

```json theme={null}
{
  "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

```bash theme={null}
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

```bash theme={null}
curl https://api.topcalls.ai/v1/calls/CALL_ID \
  -H "Authorization: Bearer YOUR_API_KEY"
```

### 3. List Calls

```bash theme={null}
curl https://api.topcalls.ai/v1/calls \
  -H "Authorization: Bearer YOUR_API_KEY"
```

## API Endpoints

### Calls

| Method | Endpoint                     | Description          |
| ------ | ---------------------------- | -------------------- |
| `GET`  | `/v1/calls`                  | List calls           |
| `GET`  | `/v1/calls/{call_id}`        | Get call details     |
| `POST` | `/v1/calls`                  | Create a new call    |
| `POST` | `/v1/calls/{call_id}/stop`   | Stop an active call  |
| `POST` | `/v1/calls/{call_id}/cancel` | Cancel a queued call |

### Campaigns

Create, configure, and run campaigns from the API or the TopCalls dashboard:

| Method  | Endpoint                             | Description                      |
| ------- | ------------------------------------ | -------------------------------- |
| `POST`  | `/v1/campaigns`                      | Create a campaign                |
| `GET`   | `/v1/campaigns`                      | List campaigns                   |
| `GET`   | `/v1/campaigns/{campaign_id}`        | Get campaign details             |
| `GET`   | `/v1/campaigns/{campaign_id}/stats`  | Get campaign stats               |
| `PATCH` | `/v1/campaigns/{campaign_id}`        | Update a campaign                |
| `POST`  | `/v1/campaigns/{campaign_id}/start`  | Start a campaign                 |
| `POST`  | `/v1/campaigns/{campaign_id}/pause`  | Pause a running campaign         |
| `POST`  | `/v1/campaigns/{campaign_id}/resume` | Resume a paused campaign         |
| `POST`  | `/v1/campaigns/{campaign_id}/stop`   | Stop a campaign                  |
| `POST`  | `/v1/campaigns/{campaign_id}/test`   | Place a test call for a campaign |

### Phone Numbers

| Method   | Endpoint                                  | Description                      |
| -------- | ----------------------------------------- | -------------------------------- |
| `GET`    | `/v1/phone-numbers/carriers`              | List carriers                    |
| `GET`    | `/v1/phone-numbers`                       | List phone numbers               |
| `GET`    | `/v1/phone-numbers/{phone_number_id}`     | Get phone number details         |
| `POST`   | `/v1/phone-numbers/carriers`              | Add custom carrier (BYOC)        |
| `POST`   | `/v1/phone-numbers`                       | Add 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

| Method   | Endpoint                         | Description                                   |
| -------- | -------------------------------- | --------------------------------------------- |
| `POST`   | `/v1/leads`                      | Create a lead (idempotent per phone number)   |
| `GET`    | `/v1/leads`                      | List 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/bulk`                 | Bulk import leads                             |
| `POST`   | `/v1/leads/bulk-delete`          | Bulk delete leads                             |
| `GET`    | `/v1/leads/bulk-delete/{job_id}` | Poll an async bulk-delete job                 |

### Lead Lists

| Method  | Endpoint                        | Description           |
| ------- | ------------------------------- | --------------------- |
| `GET`   | `/v1/lead-lists`                | List lead lists       |
| `GET`   | `/v1/lead-lists/{lead_list_id}` | Get lead list details |
| `POST`  | `/v1/lead-lists`                | Create a lead list    |
| `PATCH` | `/v1/lead-lists/{lead_list_id}` | Update a lead list    |

### Knowledge Bases

| Method   | Endpoint                   | Description                   |
| -------- | -------------------------- | ----------------------------- |
| `GET`    | `/v1/knowledge-bases`      | List knowledge base entries   |
| `GET`    | `/v1/knowledge-bases/{id}` | Get a knowledge base entry    |
| `POST`   | `/v1/knowledge-bases`      | Create 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

| Method   | Endpoint                    | Description                   |
| -------- | --------------------------- | ----------------------------- |
| `POST`   | `/v1/webhooks`              | Create a webhook subscription |
| `GET`    | `/v1/webhooks`              | List 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

| Method | Endpoint             | Description                                            |
| ------ | -------------------- | ------------------------------------------------------ |
| `GET`  | `/v1/models`         | List available AI models                               |
| `GET`  | `/v1/voices/builtin` | List built-in voices (with filters)                    |
| `GET`  | `/v1/voices`         | List voices for your account (including custom/cloned) |

### Account

| Method | Endpoint              | Description                   |
| ------ | --------------------- | ----------------------------- |
| `GET`  | `/v1/balance`         | Get account balance and usage |
| `GET`  | `/v1/usage/movements` | List usage ledger movements   |
| `GET`  | `/v1/analytics/calls` | Daily call analytics          |

## Webhooks

TopCalls sends webhooks to your server when calls finish. Set `webhook_url` on a call for per-call delivery:

```json theme={null}
{
  "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](/guides/webhooks) 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 theme={null}
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 Code | Description                                                                                                                                             |
| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `200`       | Success                                                                                                                                                 |
| `201`       | Created                                                                                                                                                 |
| `400`       | Bad Request - Invalid input                                                                                                                             |
| `401`       | Unauthorized - Invalid or missing API key                                                                                                               |
| `402`       | Payment Required - Insufficient quota                                                                                                                   |
| `403`       | Forbidden - Insufficient permissions                                                                                                                    |
| `404`       | Not Found - Resource doesn't exist                                                                                                                      |
| `409`       | Conflict - Idempotency key reused while the original request is in flight                                                                               |
| `413`       | Payload Too Large - Too many items in a bulk request                                                                                                    |
| `422`       | Unprocessable - Prompt failed the automated compliance review (see the `code` field, e.g. `PROMPT_SAFETY_FRAUD`), or the request could not be processed |
| `429`       | Too Many Requests - rate limit exceeded. Wait for the `Retry-After` header, then retry. See [Rate Limits](#rate-limits)                                 |
| `500`       | Internal 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

* **Documentation**: Browse our [guides](/guides/making-calls) and [concepts](/concepts/overview)
* **Contact**: Email us at [hello@topcalls.ai](mailto:hello@topcalls.ai)
* **Get Your Deployment Plan**: [Book a discovery call](https://cal.com/topcalls.ai/30min) for a free consultation

## Next Steps

<CardGroup cols={2}>
  <Card title="Quickstart" icon="rocket" href="/quickstart">
    Make your first call in 5 minutes.
  </Card>

  <Card title="Making Calls Guide" icon="phone" href="/guides/making-calls">
    Learn how to make calls with the API.
  </Card>
</CardGroup>
