Skip to content

Errors

Telbox errors are machine-readable codes, not just human prose. Always branch on the code, not the HTTP status alone (several distinct conditions share a status).

Response shape

The error body always contains a detail field carrying the error code:

example
{ "detail": "ai_quota_exceeded_ask" }

Some platform errors return a richer object:

{ "code": "string", "message": "human readable", "details": { } }

Treat detail (or code) as the stable, switchable identifier; treat message as display text that may change.

Always capture X-Request-ID

Every response — success or error — includes an X-Request-ID header. Log it. When you report a problem, that id lets us find the exact request in our structured logs.

Status codes

Status Meaning Typical codes
400 Malformed request / provider rejection invalid_request, provider-specific
401 Missing / invalid / expired token missing_bearer_token, token_expired, token_invalid, device_not_registered
402 Payment required (AI budget exhausted) ai_budget_exceeded
403 Forbidden / consent required ai_processing_consent_required
404 Not found (or admin route hidden in prod)
409 Conflict (idempotent replay mismatch, state)
422 Request body failed validation validation_error (FastAPI shape)
429 Rate limited or quota exhausted otp_rate_limited, ai_quota_exceeded_*
5xx Server error

Validation errors (422)

Body-shape failures use FastAPI's validation envelope:

422 Unprocessable Entity
{
  "detail": [
    { "loc": ["body", "phone"], "msg": "field required", "type": "value_error.missing" }
  ]
}

AI errors

AI endpoints enforce three gates in order — consent, then per-feature quota, then a dollar backstop. Each maps to a distinct status so you can respond appropriately (e.g. show an upgrade prompt vs. a retry timer):

Condition Status detail code Retry-After
AI processing not consented 403 ai_processing_consent_required
Free-tier feature quota exhausted 429 ai_quota_exceeded_<feature> ✅ yes
Free-tier $ budget exhausted 402 ai_budget_exceeded

<feature> is one of voice_note, ask, thread_assistant, insights_rerun, brief — e.g. ai_quota_exceeded_ask. See Rate Limits & Quotas for the quota model and AI & Privacy for consent.

429 Too Many Requests
{ "detail": "ai_quota_exceeded_ask" }
Retry-After: 86400

Handle 402/403 from every AI call site

POST /v1/ask, POST /v1/messages/{id}/insights, POST /v1/ai/thread-assistant, and the voice variants all enforce these gates. A robust client catches 403 (route the user to consent), 429 (back off using Retry-After, or prompt to upgrade), and 402 (prompt to upgrade).

Rate-limit errors

Per-IP and per-actor limits return 429. The OTP per-phone limiter uses the code otp_rate_limited and sets Retry-After. See Rate Limits & Quotas.

import time, httpx

def call(client, method, url, **kw):
    for attempt in range(5):
        r = client.request(method, url, **kw)
        if r.status_code == 429:
            wait = int(r.headers.get("Retry-After", 2 ** attempt))
            time.sleep(wait)
            continue
        if r.status_code == 401 and r.json().get("detail") == "token_expired":
            refresh_access_token()          # then retry with new bearer
            continue
        return r
    r.raise_for_status()
  • 401 token_expired → refresh and retry once.
  • 429 → honour Retry-After; exponential backoff otherwise.
  • 402 / 403 (AI) → surface an upgrade / consent path; do not retry blindly.
  • 5xx → retry idempotent requests with backoff; include X-Request-ID if you escalate.