On this page
Errors
Error envelope
Every error response:
{
"error": {
"code": "<code>",
"message": "<user-safe string>",
"details": {}
}
}
Always also: response header X-Request-Id.
Branch on error.code, not only HTTP status.
Success responses are bare JSON (no { data: … } wrapper). Unknown body keys → 400 validation_error.
Example
{
"error": {
"code": "unauthorized",
"message": "Invalid API key.",
"details": {}
}
}
Header: X-Request-Id: <uuid>. On 429, also Retry-After.
Error codes
| Code | Typical HTTP | When |
|---|---|---|
unauthorized | 401 | Missing or invalid key |
plan_required | 403 | Workspace not entitled for Public API |
validation_error | 400 (or 413 if body too large) | Bad body/query; unknown fields |
not_found | 404 | Campaign, lead, template, etc. |
conflict | 409 | Idempotency mismatch / in-progress; other conflicts |
insufficient_credits | 403 | Not enough credits (never HTTP 402) |
account_disconnected | 403 | Gmail account missing or disconnected |
rate_limited | 429 | Too many requests — see Retry-After |
send_failed | 502 | Gmail send failed upstream |
upstream_unavailable | 503 | Temporary failure; safe to retry |
HTTP status table
| Status | Typical codes |
|---|---|
| 400 | validation_error |
| 401 | unauthorized |
| 403 | plan_required, insufficient_credits, account_disconnected |
| 404 | not_found |
| 409 | conflict |
| 413 | validation_error (body too large) |
| 429 | rate_limited |
| 502 | send_failed |
| 503 | upstream_unavailable |
See also Credits & rate limits for RPM and idempotency details.