Developers

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:

Shell
curl "$DIALER_URL/api/v1/me" \
  -H "Authorization: Bearer $DIALER_API_KEY"
Data
{
  "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

Fieldtypedescription
calls:readReadList and read calls, list dispositions.
calls:writeWritePlace calls for an agent and hang them up.
recordings:readReadDownload call recordings.
contacts:readReadList and read contacts.
contacts:writeWriteCreate, update, delete and erase contacts.
callbacks:readReadList callbacks.
callbacks:writeWriteSchedule callbacks.
campaigns:readReadList campaigns with lead counts.
campaigns:writeWriteAdd leads to a campaign.
users:readReadList users with their role, status and live state.
dnc:writeWriteAdd and remove numbers on your DNC list.

GET /api/v1/me works with any valid key.

Conventions

  • JSON only, with snake_case field 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
{ "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:

Data
{
  "error": {
    "code": "CALLING_HOURS",
    "message": "…",
    "details": { "zones": ["America/Chicago"] }
  }
}

details is optional, and validation errors can add fieldErrors. Branch on code, not on message.

Fieldtypedescription
VALIDATION422The request body or query is invalid.
UNAUTHORIZED401Missing, unknown, revoked or expired key.
FORBIDDEN403The key lacks the scope, or the workspace can't do this right now.
NOT_FOUND404No such resource in your workspace.
PLAN_FEATURE402Your plan doesn't include the API.
RATE_LIMITED429Too many requests. Wait for Retry-After seconds.
AGENT_NOT_READY409The agent is offline, busy, or their softphone isn't ready.
DNC409The number is on a do-not-call list.
CALLING_HOURS409Outside calling hours in one of the contact's time zones.
CONFLICT409For example, the workspace has no number to use as caller ID.
USAGE_LIMIT402The workspace used its monthly minutes or spending ceiling.
TRIAL_LIMIT402Not allowed during a trial without a card.
BILLING_PAST_DUE402Outbound calling is blocked until an overdue invoice is paid.
OUTBOUND_PAUSED403Outbound calling is paused for the workspace.
TERMS_REQUIRED403The workspace owner hasn't accepted the terms yet.
INVALID_NUMBER422Not a valid phone number.
EMERGENCY_NUMBER422Emergency numbers can't be called.
COUNTRY_NOT_ALLOWED422The destination country isn't allowed for your workspace.
PROVIDER_ERROR502The 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

Fieldtypedescription
GET /meany keyYour workspace (id, name, slug) and the key (id, name, scopes).
GET /usersusers:readAll users. Filters: role, status. Returns data with User objects.

Calls

Fieldtypedescription
GET /callscalls:readPaginated calls, newest first. Filters: since, until, direction, status, agent_id, campaign_id, contact_id, customer_number.
GET /calls/{id}calls:readOne Call object.
POST /callscalls:writePlace a call from an agent's softphone (click-to-call). 202 with the call id.
POST /calls/{id}/hangupcalls:writeEnd a call. 202.
GET /calls/{id}/recordingrecordings:readThe recording as audio, format mp3 or wav. Supports Range requests. 404 when there is none.
GET /dispositionscalls:readYour dispositions: id, code, name, category, is_success.

Contacts

Fieldtypedescription
GET /contactscontacts:readPaginated contacts. Filters: q (search), phone, external_id, updated_since.
POST /contactscontacts:writeCreate or update a contact: matched by external_id, else by phone. 201 when created, 200 when updated.
GET /contacts/{id}contacts:readOne Contact object.
PATCH /contacts/{id}contacts:writeUpdate some fields.
DELETE /contacts/{id}contacts:writeDelete the contact. 204. Add erase=1 to erase their personal data, recordings and voicemails; returns the counts.

Callbacks

Fieldtypedescription
GET /callbackscallbacks:readPaginated callbacks. Filters: status, assigned_user_id, since, until.
POST /callbackscallbacks:writeSchedule a callback for a contact or a phone number. 201 with the Callback object.

Campaigns

Fieldtypedescription
GET /campaignscampaigns:readAll campaigns: id, name, status, dial_mode, leads_total, leads_remaining.
POST /campaigns/{id}/leadscampaigns:writeAdd up to 1,000 leads. Contacts are created or updated and put on the campaign's API list.

DNC

Fieldtypedescription
POST /dncdnc:writeAdd a number, with an optional reason. 201.
DELETE /dnc/{phone}dnc:writeRemove 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.

Fieldtypedescription
agent_iduuidThe agent who makes the call. Send exactly one of agent_id, agent_email or agent_username.
agent_emailstringOr the agent's sign-in email.
agent_usernamestringOr the agent's username.
tostring, requiredThe number to call, in any common format.
contact_iduuidAttach the call to this contact. Otherwise the contact is matched by number.
caller_idE.164One of your workspace numbers. Otherwise the usual caller-ID rules apply.
metadataobjectUp to 10 string keys with values up to 200 characters. Returned on the Call object and in webhooks.
Shell
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" }
  }'
Data
{ "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 return 422 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 return 402 USAGE_LIMIT; a paused workspace or missing terms return 403.
  • 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

Shell
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

Fieldtypedescription
iduuid
directionstringinbound, outbound or internal.
originstringinbound, manual, click_to_call, api, campaign, callback or transfer.
statusstringinitiated, queued, ringing, in_progress, on_hold, completed, busy, no_answer, failed, canceled, abandoned, voicemail or machine.
from, to, customer_numberE.164 or nullNull after the contact was erased.
agentobject or nullid, name, email.
contact_id, campaign_id, queue_iduuid or null
dispositionobject or nullid, code, name, category.
notesstring or nullThe agent's notes.
created_at, answered_at, ended_attimestampanswered_at and ended_at can be null.
wait_sec, talk_sec, hold_sec, wrap_sec, duration_secintegerSeconds.
recordingobject or nullid, duration_sec and url (the recording endpoint).
metadataobjectWhat 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.

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

Shell
curl -X DELETE "$DIALER_URL/api/v1/contacts/0192f6c3-…?erase=1" \
  -H "Authorization: Bearer $DIALER_API_KEY"
Data
{ "calls": 3, "recordings": 1, "voicemails": 0 }

Callbacks

Shell
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

Shell
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" }
    ]
  }'
Data
{ "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

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