API Guide

    Errors & Response Envelope

    The success/error envelope, HTTP status meanings, and the full error_code vocabulary.

    Envelope

    Success:

    { "success": true, "data": { … } }

    Error:

    {
      "success": false,
      "error": "Human-readable message.",
      "error_code": "machine_readable_code"
    }

    Branch on error_code, never on the prose in error — messages may be reworded, codes are contractual.

    Status codes

    • 200 — success. Deletes also return 200 with a deletion receipt ({ id, object, deleted, deleted_counts }) — there is no 204 anywhere in the API.
    • 201 — created (also used by the send routes).
    • 400 — malformed request: bad parameter, unknown parameter, bad cursor, bad field list, bad JSON, missing Idempotency-Key.
    • 401 — key missing, unrecognized, or revoked; signature missing/invalid/stale; replayed request.
    • 402 — insufficient SMS credits.
    • 403 — authenticated but not permitted: missing scope, branch-scope restrictions, IP not allowed, forbidden origin.
    • 404 — no such record within your tenancy scope (includes converted — the record now lives at the other resource).
    • 405 — method not supported on this route.
    • 409 — conflict: version conflict, a same-key request still in flight, a locked estimate, dependent records blocking a delete.
    • 413 — payload too large (bodies are capped at 64 KiB).
    • 415 — writes must use Content-Type: application/json.
    • 422 — the request was well-formed but a value was rejected: validation failures, bad references, recipient restrictions, and Idempotency-Key reuse with a different payload.
    • 428 — the write requires an If-Match header and none was sent.
    • 429 — rate limited. Respect Retry-After.
    • 500 — our side. Retry with backoff.
    • 502 — write_indeterminate only; see below. Do not blindly retry.
    • 503 — a dependent service is briefly unavailable. Retry with backoff.

    Error code vocabulary

    Request-shape codes (400): invalid_id, invalid_cursor, invalid_limit, invalid_param, invalid_filter, unknown_param, invalid_fields, invalid_json, invalid_idempotency_key, idempotency_key_required, invalid_precondition.

    Auth & signing codes (401): invalid_key, signature_missing, signature_malformed, signature_stale, signature_invalid, signature_unverifiable, replay_detected.

    Permission codes (403): missing_scope, missing_read_scope, scope_mismatch, branch_scope_unsupported, branch_scope_mismatch, ip_not_allowed, origin_forbidden.

    Route codes: not_found (404), converted (404 — the record is now the other resource type; the message tells you where), method_not_allowed (405).

    Write-path codes: precondition_required (428), version_conflict (409), request_in_flight (409), estimate_locked (409), has_dependents (409), reenable_too_soon (409), idempotency_key_reuse (422), invalid_field (422), invalid_reference (422), unsupported_media_type (415), payload_too_large (413), write_indeterminate (502).

    Send-route codes: insufficient_credits (402), sms_not_provisioned (409), recipient_not_on_prospect (422), recipient_opted_out (422), recipient_suppressed (422), sms_meter_unavailable (503), suppression_check_unavailable (503), email_not_configured (503).

    Gateway/service codes: internal_error, server_misconfigured, auth_unavailable (500), replay_check_unavailable (503), rate_limited (429).

    Every code is searchable, with its status and the endpoints that document it, on the error codes page.

    write_indeterminate

    A write returned write_indeterminate (502) when the gateway could not confirm whether the mutation landed. Do not retry with the same Idempotency-Key — the indeterminate outcome is stored against that key, so a same-key retry replays the same 502. Instead: GET the resource to see whether the write landed. If it did, you're done. If it didn't, retry with a new Idempotency-Key.

    Partial success on sends

    On the rare path where an SMS or email sends but the record write fails, the response is 201 with data: null and warning: "sent_not_recorded". The message did send — do not retry.

    Retry policy

    Retry 429 (honor Retry-After), 500, and 503 with exponential backoff and jitter. Handle 502 write_indeterminate as above — read before retrying, new key if you retry. Never retry 400, 401, 403, or 404 — those need a code change, not another attempt.