Developers
Errors
One error envelope for everything. Code first (so machines can branch), message second (so humans get a clue), details third (so debugging doesn't require a tail of the server log).
Envelope
{
"error": {
"code": "invalid_request",
"message": "channels[0].id must be a uuid",
"details": { "field": "channels[0].id" }
}
}Status codes
| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_request | Malformed JSON, missing required field, wrong type. |
| 401 | unauthorized | Missing, invalid, revoked, or expired API key. |
| 403 | forbidden | Key valid but lacks the required scope. |
| 404 | not_found | Resource missing, or not owned by your organization. |
| 409 | idempotency_conflict | Same Idempotency-Key, different body. |
| 413 | payload_too_large | Media upload exceeded the 1 GB limit. |
| 422 | unprocessable | Semantically invalid, e.g. channel id belongs to another org. |
| 429 | rate_limited | Slow down. See Retry-After. |
| 5xx | internal_error | Our fault. Retry with a fresh request after a brief backoff. |
Retry semantics
- 4xx errors will not change on retry unless you change the request. Fix and re-send.
- 5xx errors are usually transient. Re-send the request with the same
Idempotency-Key. - For 429, honor
Retry-After; for 5xx, exponential backoff (1s → 2s → 4s, capped at 30s) is a sane default.