Skip to main content

Documentation Index

Fetch the complete documentation index at: https://orbit-docs.devotel.io/llms.txt

Use this file to discover all available pages before exploring further.

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:
StatusMeaning
pendingFresh contact, eligible for the next pacing claim once next_attempt_at <= now().
dialingCurrently being dialed (or claimed by an agent in preview mode). Atomic flip via FOR UPDATE SKIP LOCKED.
connectedTalked, voicemail dropped, no answer, AMD-detected, busy, or general “outcome captured”. Terminal for the current attempt; the contact may be re-queued if attempts < max_attempts.
callback_scheduledAgent scheduled a callback. next_attempt_at is set to the requested ISO timestamp; pacing claim re-picks the contact at that moment.
wrong_numberNumber was reached but the called party is not the intended contact. Terminal — but not added to the DNC list (the rightful owner may still be contactable on reassignment).
dncDo-not-call. Terminal. The phone is INSERTed into the tenant dnc_list so any future pacing tick across any campaign blocks at the compliance gate (checkDialerCompliancecheckDnc).
exhaustedMax attempts consumed without a connected outcome. Terminal.
archivedSoft-deleted via DELETE /campaigns/:id or DELETE /campaigns/:id/lists/:listId. Audit-trail only.
failedCarrier-level failure (invalid number, network error). Terminal for the attempt.

Disposition → status matrix

Disposition matching is case-insensitive and trim-stripped at the request boundary. Any disposition not listed below falls through to connected (preserving pre-fix semantics for campaign-script-specific freeform dispositions; the agent’s chosen string is still persisted on dialer_call_attempts.disposition).
Disposition stringRoutes to statusSide effects
callback_latercallback_scheduledRequires callback_scheduled_at in body. Stamps next_attempt_at on the contact.
callbackcallback_scheduledSame as callback_later.
wrong_numberwrong_numberTerminal — no DNC write.
dncdncINSERT INTO tenant dnc_list (phone, source=dialer_disposition, reason=disposition). Idempotent on phone.
do_not_calldncSame as dnc.
saleconnectedNone beyond the status flip.
no_saleconnectedNone.
voicemailconnectedNone. The voicemail leg is already captured on dialer_call_attempts.outcome.
interestedconnectedNone.
not_interestedconnectedNone.
any other stringconnectedNone. The literal disposition string is still persisted for analytics.
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. Persisted with the attempt. Max 2000 chars.
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
# Schedule a callback
curl -X POST "https://api.orbit.devotel.io/api/v1/dialer/campaigns/dcmp_abc/dispositions" \
  -H "X-API-Key: dv_live_sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "attempt_id": "dcall_xyz",
    "disposition": "callback_later",
    "notes": "Customer asked for tomorrow morning EST",
    "callback_scheduled_at": "2026-05-25T13:00:00-04:00"
  }'
cURL
# Mark as DNC
curl -X POST "https://api.orbit.devotel.io/api/v1/dialer/campaigns/dcmp_abc/dispositions" \
  -H "X-API-Key: dv_live_sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "attempt_id": "dcall_xyz",
    "disposition": "do_not_call",
    "notes": "Customer requested removal from all outreach"
  }'
200 OK
{
  "data": {
    "attempt_id": "dcall_xyz",
    "disposition": "callback_later",
    "status": "callback_scheduled",
    "callback_scheduled_at": "2026-05-25T13:00:00-04:00"
  },
  "meta": { "request_id": "req_abc123", "timestamp": "2026-05-24T12:00:00Z" }
}
Errors
Statuserror.codeCause
404NOT_FOUNDattempt_id not found in this campaign (or cross-tenant).
422VALIDATION_ERRORcallback_scheduled_at missing on callback disposition, in the past, or >90 days ahead.
503TENANT_SCHEMA_INCOMPLETEThe tenant schema was provisioned before migration 280 ran. Contact support.

List campaigns

GET /api/v1/dialer/campaigns
Scope: dialer:read. Cursor-paginated. Returns campaigns excluding soft-deleted (status = 'deleted') rows. Query parameters
NameTypeNotes
limitinteger (1–100)Default 20.
cursorstringOpaque base64url cursor returned in the previous response’s next_cursor.
{
  "data": {
    "items": [
      {
        "id": "dcmp_abc",
        "name": "Q2 outbound — US East",
        "mode": "predictive",
        "status": "active",
        "caller_id_e164": "+12025551234",
        "pacing_ratio": 1.2,
        "max_abandon_rate": 0.03,
        "abandon_rate_30d": 0.018,
        "recording_enabled": true,
        "recording_consent_acknowledged_at": "2026-05-22T10:30:00Z",
        "created_at": "2026-05-22T10:30:00Z"
      }
    ],
    "next_cursor": "MjAyNi0wNS0yMlQxMDozMDowMC4wMDBa",
    "has_more": false
  },
  "meta": { "request_id": "req_abc123", "timestamp": "2026-05-24T12:00:00Z" }
}

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
{
  "data": {
    "contact": {
      "id": "dlc_xyz",
      "phone_e164": "+14155552671",
      "display_name": "Jane Doe",
      "contact_id": "con_abc",
      "attempts": 0
    }
  },
  "meta": { "request_id": "req_abc123", "timestamp": "2026-05-24T12:00:00Z" }
}
204 No Content — campaign is in progressive or predictive mode (pacing engine handles dial). Errors
Statuserror.codeCause
400BAD_REQUESTcampaign_id query parameter missing.
404NOT_FOUNDCampaign not found or not in active status.

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
Statuserror.codeCause
404NOT_FOUNDCampaign id not found or already soft-deleted.
409DIALER_CAMPAIGN_IN_FLIGHTAt least one contact is currently in status dialing. Pause the campaign and wait for live legs to terminate, then retry. The details.in_flight_contacts count is returned.

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