REST API.
API keys, scopes, pagination, errors and rate limits, and every endpoint of the v1 REST API with examples.
The REST API lets your CRM or back office place calls for an agent, read calls and recordings, and keep contacts, callbacks, campaign leads and DNC entries in sync. It's included on the Growth and Enterprise plans.
API keys
Owners and admins create keys in Developers in the admin console: a name, the scopes the key may use, and an optional expiry date.
- Keys look like
dk_live_followed by 32 random characters. - The full key is shown once, when you create it. Store it in your secret manager; the dialer keeps only a hash.
- The key list shows each key's prefix, scopes and when it was last used. Revoke a key to stop it immediately.
Send the key as a bearer token on every request:
curl "$DIALER_URL/api/v1/me" \
-H "Authorization: Bearer $DIALER_API_KEY"{
"tenant": { "id": "0192f6c0-…", "name": "Example Support", "slug": "example" },
"key": { "id": "0192f6c1-…", "name": "CRM integration", "scopes": ["calls:read", "calls:write"] }
}In these examples, DIALER_URL is the address you use to sign in to the dialer, and DIALER_API_KEY holds your key.
Scopes
| Field | type | description |
|---|---|---|
| calls:read | Read | List and read calls, list dispositions. |
| calls:write | Write | Place calls for an agent and hang them up. |
| recordings:read | Read | Download call recordings. |
| contacts:read | Read | List and read contacts. |
| contacts:write | Write | Create, update, delete and erase contacts. |
| callbacks:read | Read | List callbacks. |
| callbacks:write | Write | Schedule callbacks. |
| campaigns:read | Read | List campaigns with lead counts. |
| campaigns:write | Write | Add leads to a campaign. |
| users:read | Read | List users with their role, status and live state. |
| dnc:write | Write | Add and remove numbers on your DNC list. |
GET /api/v1/me works with any valid key.
Conventions
- JSON only, with
snake_casefield names. - Timestamps are ISO-8601 in UTC.
- Phone numbers are returned in E.164 (
+14155550148). Where you send a number, any common format works: numbers without a country code use your workspace's country. - IDs are UUIDs.
Pagination
List endpoints that can grow return a page and a cursor:
{ "data": [ … ], "next_cursor": "eyJjcmVhdGVkQXQiOi…" }Pass ?limit= (1–100, default 50) and, for the next page, ?cursor= with the next_cursor you received. Results are newest first. next_cursor is null on the last page. Treat the cursor as opaque.
Errors
Errors use HTTP status codes and a JSON body:
{
"error": {
"code": "CALLING_HOURS",
"message": "…",
"details": { "zones": ["America/Chicago"] }
}
}details is optional, and validation errors can add fieldErrors. Branch on code, not on message.
| Field | type | description |
|---|---|---|
| VALIDATION | 422 | The request body or query is invalid. |
| UNAUTHORIZED | 401 | Missing, unknown, revoked or expired key. |
| FORBIDDEN | 403 | The key lacks the scope, or the workspace can't do this right now. |
| NOT_FOUND | 404 | No such resource in your workspace. |
| PLAN_FEATURE | 402 | Your plan doesn't include the API. |
| RATE_LIMITED | 429 | Too many requests. Wait for Retry-After seconds. |
| AGENT_NOT_READY | 409 | The agent is offline, busy, or their softphone isn't ready. |
| DNC | 409 | The number is on a do-not-call list. |
| CALLING_HOURS | 409 | Outside calling hours in one of the contact's time zones. |
| CONFLICT | 409 | For example, the workspace has no number to use as caller ID. |
| USAGE_LIMIT | 402 | The workspace used its monthly minutes or spending ceiling. |
| TRIAL_LIMIT | 402 | Not allowed during a trial without a card. |
| BILLING_PAST_DUE | 402 | Outbound calling is blocked until an overdue invoice is paid. |
| OUTBOUND_PAUSED | 403 | Outbound calling is paused for the workspace. |
| TERMS_REQUIRED | 403 | The workspace owner hasn't accepted the terms yet. |
| INVALID_NUMBER | 422 | Not a valid phone number. |
| EMERGENCY_NUMBER | 422 | Emergency numbers can't be called. |
| COUNTRY_NOT_ALLOWED | 422 | The destination country isn't allowed for your workspace. |
| PROVIDER_ERROR | 502 | The carrier refused the request. Retry later. |
Rate limits
Each key can make 600 requests a minute on Growth and 1,200 on Enterprise. Placing calls has an extra limit of 60 a minute. Over the limit, you get 429 with a Retry-After header.
A workspace past its payment grace period keeps read access, but write requests are refused until the invoice is paid.
Endpoints
All paths are under /api/v1.
Account and users
| Field | type | description |
|---|---|---|
| GET /me | any key | Your workspace (id, name, slug) and the key (id, name, scopes). |
| GET /users | users:read | All users. Filters: role, status. Returns data with User objects. |
Calls
| Field | type | description |
|---|---|---|
| GET /calls | calls:read | Paginated calls, newest first. Filters: since, until, direction, status, agent_id, campaign_id, contact_id, customer_number. |
| GET /calls/{id} | calls:read | One Call object. |
| POST /calls | calls:write | Place a call from an agent's softphone (click-to-call). 202 with the call id. |
| POST /calls/{id}/hangup | calls:write | End a call. 202. |
| GET /calls/{id}/recording | recordings:read | The recording as audio, format mp3 or wav. Supports Range requests. 404 when there is none. |
| GET /dispositions | calls:read | Your dispositions: id, code, name, category, is_success. |
Contacts
| Field | type | description |
|---|---|---|
| GET /contacts | contacts:read | Paginated contacts. Filters: q (search), phone, external_id, updated_since. |
| POST /contacts | contacts:write | Create or update a contact: matched by external_id, else by phone. 201 when created, 200 when updated. |
| GET /contacts/{id} | contacts:read | One Contact object. |
| PATCH /contacts/{id} | contacts:write | Update some fields. |
| DELETE /contacts/{id} | contacts:write | Delete the contact. 204. Add erase=1 to erase their personal data, recordings and voicemails; returns the counts. |
Callbacks
| Field | type | description |
|---|---|---|
| GET /callbacks | callbacks:read | Paginated callbacks. Filters: status, assigned_user_id, since, until. |
| POST /callbacks | callbacks:write | Schedule a callback for a contact or a phone number. 201 with the Callback object. |
Campaigns
| Field | type | description |
|---|---|---|
| GET /campaigns | campaigns:read | All campaigns: id, name, status, dial_mode, leads_total, leads_remaining. |
| POST /campaigns/{id}/leads | campaigns:write | Add up to 1,000 leads. Contacts are created or updated and put on the campaign's API list. |
DNC
| Field | type | description |
|---|---|---|
| POST /dnc | dnc:write | Add a number, with an optional reason. 201. |
| DELETE /dnc/{phone} | dnc:write | Remove a number. URL-encode the plus sign as %2B. 204. |
Place a call
POST /api/v1/calls rings the customer from an agent's softphone, exactly as if the agent had clicked the number: the agent's browser connects on its own, with no click.
| Field | type | description |
|---|---|---|
| agent_id | uuid | The agent who makes the call. Send exactly one of agent_id, agent_email or agent_username. |
| agent_email | string | Or the agent's sign-in email. |
| agent_username | string | Or the agent's username. |
| to | string, required | The number to call, in any common format. |
| contact_id | uuid | Attach the call to this contact. Otherwise the contact is matched by number. |
| caller_id | E.164 | One of your workspace numbers. Otherwise the usual caller-ID rules apply. |
| metadata | object | Up to 10 string keys with values up to 200 characters. Returned on the Call object and in webhooks. |
curl -X POST "$DIALER_URL/api/v1/calls" \
-H "Authorization: Bearer $DIALER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"agent_email": "agent@example.com",
"to": "+1 415 555 0148",
"metadata": { "crm_ticket": "T-1042" }
}'{ "id": "0192f6c2-…", "status": "initiated" }- The agent must be Available with their softphone ready; otherwise
409 AGENT_NOT_READY. - Agents are looked up only inside your workspace. An unknown email or username returns
404; none or several agent fields return422 VALIDATION. - DNC and calling hours always block over the API (
409 DNC,409 CALLING_HOURS); there's no override. - Emergency numbers return
422 EMERGENCY_NUMBER. Usage limits return402 USAGE_LIMIT; a paused workspace or missing terms return403. - If the agent's browser doesn't connect within 60 seconds, the call is canceled and the agent released.
Follow the call with GET /api/v1/calls/{id} or the call.* webhooks.
List calls
curl -G "$DIALER_URL/api/v1/calls" \
-H "Authorization: Bearer $DIALER_API_KEY" \
--data-urlencode "since=2026-09-01T00:00:00Z" \
--data-urlencode "direction=outbound" \
--data-urlencode "limit=100"The Call object
| Field | type | description |
|---|---|---|
| id | uuid | |
| direction | string | inbound, outbound or internal. |
| origin | string | inbound, manual, click_to_call, api, campaign, callback or transfer. |
| status | string | initiated, queued, ringing, in_progress, on_hold, completed, busy, no_answer, failed, canceled, abandoned, voicemail or machine. |
| from, to, customer_number | E.164 or null | Null after the contact was erased. |
| agent | object or null | id, name, email. |
| contact_id, campaign_id, queue_id | uuid or null | |
| disposition | object or null | id, code, name, category. |
| notes | string or null | The agent's notes. |
| created_at, answered_at, ended_at | timestamp | answered_at and ended_at can be null. |
| wait_sec, talk_sec, hold_sec, wrap_sec, duration_sec | integer | Seconds. |
| recording | object or null | id, duration_sec and url (the recording endpoint). |
| metadata | object | What you sent when placing the call. |
Contacts
Create or update a contact. With an external_id, the contact with that ID is updated; otherwise the phone number decides.
curl -X POST "$DIALER_URL/api/v1/contacts" \
-H "Authorization: Bearer $DIALER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"external_id": "crm-58213",
"first_name": "Alex",
"last_name": "Morgan",
"company": "Example Co",
"phone": "+14155550148",
"timezone": "America/Los_Angeles",
"tags": ["renewal"],
"custom_fields": { "plan": "Pro" }
}'The Contact object has id, first_name, last_name, company, email, phone, alt_phone, timezone, tags, custom_fields, external_id, created_at, updated_at and last_contacted_at. Creating a contact fires the contact.created webhook.
To handle a data-subject erasure request:
curl -X DELETE "$DIALER_URL/api/v1/contacts/0192f6c3-…?erase=1" \
-H "Authorization: Bearer $DIALER_API_KEY"{ "calls": 3, "recordings": 1, "voicemails": 0 }Callbacks
curl -X POST "$DIALER_URL/api/v1/callbacks" \
-H "Authorization: Bearer $DIALER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"phone": "+14155550148",
"scheduled_at": "2026-09-28T15:00:00Z",
"assigned_user_id": "0192f6c4-…",
"notes": "Wants a quote for 20 seats"
}'Send a contact_id or a phone (the contact is looked up or created). Leave out assigned_user_id and set queue_id to let the queue's agents claim it. The Callback object has id, contact_id, phone, scheduled_at, status, origin (api for these), assigned_user_id, queue_id, campaign_id, notes, created_at and completed_at. The callback.scheduled webhook fires.
Campaign leads
curl -X POST "$DIALER_URL/api/v1/campaigns/0192f6c5-…/leads" \
-H "Authorization: Bearer $DIALER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"leads": [
{ "phone": "+14155550148", "first_name": "Alex", "external_id": "crm-58213" },
{ "phone": "+13125550199", "first_name": "Riley", "timezone": "America/Chicago" }
]
}'{ "created": 1, "updated": 1, "skipped": [] }Each lead can carry phone (required), first_name, last_name, company, email, timezone, external_id and custom_fields. Contacts are created or updated and added to a list named "API leads · " plus the campaign name, created and attached the first time. The dialer turns them into leads on its next sync, applying DNC, consent and calling-hours rules as usual. Rows it can't use come back in skipped with their index and reason.
DNC
curl -X POST "$DIALER_URL/api/v1/dnc" \
-H "Authorization: Bearer $DIALER_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "phone": "+14155550148", "reason": "Asked on a call" }'
curl -X DELETE "$DIALER_URL/api/v1/dnc/%2B14155550148" \
-H "Authorization: Bearer $DIALER_API_KEY"Next: get notified as calls happen with webhooks.