Skip to content

Errors & compliance

The error envelope

Every error, on every endpoint, has the same shape:

json
{
  "error": {
    "code": "conflict",
    "message": "campaign cannot transition from completed to running"
  }
}

code is stable and machine-readable — branch on it. message is for humans and may be reworded at any time — never parse it.

HTTPerror.codeMeaning
400bad_requestMalformed JSON, bad query params, wrong field types.
400invalidA field violates a domain rule (validation error).
401unauthorizedMissing, unknown or revoked API key — see Authentication.
403forbiddenAccount suspended.
404not_foundUnknown, malformed or another account's id — existence is never leaked.
409conflictUniqueness or state-machine violation (duplicate name, invalid lifecycle transition, busy seat).
415unsupported_media_typeBody-carrying request without a JSON content type.
422unprocessableShape is fine, semantics are not (a broken domain rule).
422compliance_blockedThe compliance engine refused the operation — see below.
429throttled / sms_spend_cap_exceededRate and spend brakes — see Rate limits & caps.
500internalOur fault. Retry with backoff and quote the X-Request-ID to support.
502provider_error / dial_error / sbc_errorAn upstream (SMS provider, switch, SIP edge) rejected the operation.

Endpoint-specific codes (e.g. sms_provider_unconfigured) are documented per response in the endpoint reference.

422 compliance_blocked — the platform that refuses to break the law

Dialer Digital has exactly one path to a consumer's phone: a gated engine that checks consent, do-not-call status, quiet hours, contact-frequency rules (Reg F 7-in-7 and post-contact), state-level limits, reassigned-number data and your own account policies — before any provider is contacted. Your API calls go through the same gates as our campaigns and our dashboard. There is no bypass parameter, and none can be added by mistake: the engine fails closed.

When a gate refuses, you get:

json
{
  "error": {
    "code": "compliance_blocked",
    "message": "the compliance engine refused this send (dnc_listed)",
    "gate": "dnc_listed",
    "decision_id": "2c1d0e9f-8a7b-6c5d-4e3f-2a1b0c9d8e7f"
  }
}
  • gate — which rule refused: tcpa_no_consent, quiet_hours, regf_7in7, regf_post_contact, state_limit, dnc_listed, consent_revoked, cease_and_desist, number_reassigned, or tenant_policy.
  • decision_id — a citation into the append-only audit chain. The decision that blocked the operation is durable evidence: if a consumer or regulator ever asks "did you try to contact this person?", the answer is a verifiable record, in your favor.

A block is evidence, not an error

Nothing happened — zero provider calls, zero charges. Do not retry unchanged: the input that was refused will be refused again, and hammering a gate only adds noise to your own audit trail. Fix the underlying state (obtain consent, wait out quiet hours, honor the revocation) or move on to the next account. This is the feature you are paying for — every block is a violation that never reached a consumer, packaged for your defense at GET /v1/reports/violations-prevented.

Where blocks show up

SurfaceHow a block is reported
One-off SMS (POST /v1/sms/messages)422 compliance_blocked with gate + decision_id.
Manual/preview dial (POST /v1/campaigns/{id}/dial)200 with {"status": "blocked", "reason": "<gate>"} — a valid outcome the agent should see, not a server error.
Campaign auto-dialingBlocks never reach the API caller; they are recorded and aggregated at GET /v1/stats/blocked.

Turning blocks into paperwork

  • GET /v1/stats/blocked — by-gate counts and a daily series, computed from the append-only evidence table.
  • GET /v1/reports/violations-prevented — the packaged report (JSON or CSV) you can hand to a client, auditor or court.
  • GET /v1/debts/{id}/defense-packet — the sealed, per-account litigation bundle; every decision_id you ever received is citable against it.

404 and multi-tenancy

Cross-account ids, malformed UUIDs and genuinely unknown ids are all the same 404 not_found. This is enforced by row-level security in the database — the API cannot tell you whether someone else's resource exists, by design.

Nothing in these docs is legal advice — always confirm compliance posture with your own counsel.