API Reference
Dialer

List a campaign's sessions

GET
/v1/dialer/campaigns/{id}/sessions

The extensions in the campaign, oldest join first. Sessions that have left are hidden unless include_left=true. A supervisor (dialer.supervise, or an owner/admin) gets every session with its extension number and person. Anyone else with dialer.read gets counts (the headcount by state) and only their own sessions in data.

Authorization

bearerAuth
AuthorizationBearer <token>

Long-lived ES256 JWT minted from the dashboard (https://app.sautikit.com/developers/api-keys). Signed by the platform keyring. Carries workspace_id and scopes claims; revoked via the platform deny-list.

In: header

Path Parameters

id*string
Formatuuid

Query Parameters

include_left?boolean
Defaultfalse

Response Body

application/json

application/json

application/json

application/json

curl -X GET "https://example.com/v1/dialer/campaigns/497f6eca-6276-4993-bfeb-53cbbbba6f08/sessions"
{  "data": [    {      "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",      "campaign_id": "78614b6c-fe7c-41e2-8e25-c9b3a3c91904",      "extension_id": "string",      "user_id": "a169451c-8525-4352-b8ca-070dd449a1a5",      "state": "ready",      "reserved_contact_id": "2acb98b6-7d4a-4834-97d1-92a6097fa7cd",      "reserved_until": "2019-08-24T14:15:22Z",      "last_ready_at": "2019-08-24T14:15:22Z",      "joined_at": "2019-08-24T14:15:22Z",      "left_at": "2019-08-24T14:15:22Z",      "last_seen_at": "2019-08-24T14:15:22Z",      "pause_requested": true    }  ],  "counts": {    "total": 0,    "ready": 0,    "reserved": 0,    "talking": 0,    "wrap": 0,    "paused": 0  }}
{  "error": {    "code": "validation.bad_request",    "message": "string",    "request_id": "string",    "details": [      "string"    ],    "resolution": "string",    "reason": "invalid_characters",    "suggested_e164": "+254727524723"  }}
{  "error": {    "code": "validation.bad_request",    "message": "string",    "request_id": "string",    "details": [      "string"    ],    "resolution": "string",    "reason": "invalid_characters",    "suggested_e164": "+254727524723"  }}
{  "error": {    "code": "validation.bad_request",    "message": "string",    "request_id": "string",    "details": [      "string"    ],    "resolution": "string",    "reason": "invalid_characters",    "suggested_e164": "+254727524723"  }}

Export attempts as CSV GET

Streams every attempt of the campaign, oldest first, with a header row: `attempt_id,contact_id,phone,attempt_n,originated_at,answered_at,ended_at,outcome,amd_verdict,telco_cost_minor,amd_cost_minor,ai_cost_minor,amd_reason,amd_seconds`. Text cells that begin with `=`, `+`, `-` or `@` are prefixed with a single quote so a spreadsheet never runs them as a formula. With `?include=summary` two columns are appended: `summary,suggested_outcome`. The last two columns of every export are `caller_id`, the number that made the try (empty on a try from before campaigns could use several numbers and had none recorded), and `caller_id_label`, that number's name on the workspace (empty when it has none or the workspace no longer holds it).

Join a campaign with an extension POST

Puts an extension into the campaign as a session, `paused`. A joined extension is held out of inbound ringing only while a campaign call is reserved for it (ringing out or in the lead-in countdown) and while it is on that call; in between it takes inbound calls as normal. An extension is in one campaign at a time (`409 dialer.session_exists`). Joining is idempotent: the same person joining the same extension in the same campaign again (a reload, a retry after a lost response) gets the session they already have, with `200`. A different person on that extension is still `409 dialer.session_exists`. Call `.../sessions/{sid}/ready` to start taking calls, and `.../sessions/{sid}/heartbeat` every 30 seconds while the client is open. Call `.../sessions/{sid}/ready` to start taking calls. A person joins their own extension. A key with no person behind it joins extensions that belong to no person (a softphone it drives); an extension that belongs to a person needs `dialer.supervise` (`403 api_key.scope_denied`).