Skip to main content

Dialer Dispositions

When an agent submits a disposition on an outbound dial-attempt, the platform routes the contact’s lifecycle status based on a canonical mapping. The correct route is critical: a do_not_call disposition that’s mis-routed leaves the contact in the active pool, which creates a TCPA exposure (47 U.S.C. § 227(b)(3) carries a private right of action with 500500–1,500 statutory damages per call). This page documents the disposition matrix and the POST /campaigns/:id/dispositions endpoint that agents call to submit them. Base path: /api/v1/dialer Authentication: Clerk session (Authorization: Bearer <token>) or API key (X-API-Key). Scope: dialer:write on every endpoint listed here.

Contact-status enum

dialer_list_contacts.status is the contact lifecycle. An agent disposition routes the contact to one of these statuses:

Disposition → status matrix

Disposition matching is case-insensitive and trim-stripped at the request boundary. Any disposition not listed below routes to failed (PS-STATE_MACHINE-12), not connected — unmapped dispositions are deliberately excluded from connect-rate maths so a freeform or newly-added FE disposition cannot silently inflate the connect-rate KPI or pollute the TCPA per-attempt audit. The agent’s chosen string is still persisted verbatim on dialer_call_attempts.disposition; operators adding a new disposition should extend the matrix so it routes intentionally instead of defaulting to failed.
DNC writes are platform-wide per tenant. A dnc disposition in Campaign A blocks future dials to the same phone in Campaign B, C, etc. on the same tenant — the DNC list is read by every pacing tick across every campaign in the tenant. Inserts are idempotent (UNIQUE on phone), so the first-write wins and existing entries’ source / reason are preserved.

Submit disposition

POST /api/v1/dialer/campaigns/{id}/dispositions
Scope: dialer:write.
attempt_id
string
required
The dialer_call_attempts.id returned when the dial leg was placed.
disposition
string
required
Agent-selected disposition string. Matched case-insensitively against the matrix above. 1–100 chars.
notes
string
Free-form agent notes. Max 2000 chars. Accepted but not currently persisted — the field is validated at the request boundary but is not yet written to the attempt (no storage column has landed). Treat it as a no-op until a future release adds persistence; do not rely on it surviving the request.
callback_scheduled_at
string
ISO-8601 timestamp with timezone offset. REQUIRED when disposition routes to callback_scheduled (callback_later / callback). Must be strictly in the future and ≤90 days ahead.
cURL
cURL
200 OK
Errors

List campaigns

GET /api/v1/dialer/campaigns
Scope: dialer:read. Cursor-paginated. Returns campaigns excluding soft-deleted (status = 'deleted') rows. Query parameters

Get next contact (preview mode)

GET /api/v1/dialer/next-call?campaign_id=dcmp_abc
Scope: dialer:read. Agent readiness endpoint for preview-mode campaigns. Atomically claims the next eligible contact (CTE + FOR UPDATE SKIP LOCKED) so two agents polling concurrently can never receive the same row. For progressive / predictive campaigns this endpoint returns 204 No Content — pacing is handled server-side by the scheduler. Stale-claim recovery: A preview contact stuck in dialing for >5 minutes (agent abandoned the tab) is automatically re-claimable. Without this, abandoned previews would block the contact forever. 200 OK
204 No Content — campaign is in progressive or predictive mode (pacing engine handles dial). Errors

Soft-delete a campaign

DELETE /api/v1/dialer/campaigns/{id}
Scope: dialer:write. Soft-deletes the campaign. Pending contacts flip to archived; terminal contacts (connected / failed / dnc / exhausted / wrong_number) are left untouched as TCPA-dispute audit trail. All three updates (campaign, lists, contacts) commit in a single transaction. Errors

FCC abandon-rate ceiling

max_abandon_rate is hard-capped at 0.03 (3%) per the FCC Telemarketing Sales Rule. Any POST or PATCH request that sends a value above this is rejected with 422 VALIDATION_ERROR. The scheduler also auto-flips campaigns to status: 'aborted' if the rolling 30-day abandon rate trips the ceiling — aborted is terminal and cannot be re-activated; create a new campaign after reviewing the abort reason.

See also