Errors
Response shape
Every error response has the same minimal shape — both validation failures (400) and authorisation failures (401, 429). Internal errors (5xx) are never exposed with stack traces.
// Single-message variant
{ "statusCode": 401, "message": "Invalid credentials" }
// Validation variant (class-validator)
{
"statusCode": 400,
"message": [
"email must be an email",
"property foo should not exist"
]
}Status codes you can encounter
400 Bad Request— body validation failed. Inspect the returnedmessage[].401 Unauthorized— see Authentication. Always generic.402 Payment Required—{"code":"insufficient_balance", required, balance, topUpUrl}on paid campaign operations (render/audit/send) when the account balance cannot cover the FULL batch. Nothing is partially executed. See Billing & BYOC.403 Forbidden— e.g. self-signup workspace over its 50MB database quota: writes are blocked until space is freed.409 Conflict—{"code":"audit_blocked"}: the campaign exhausted its audit-iteration limit and awaits manual moderation.404 Not Found— unknown endpoint, or/api/dialogs/:idfor a non-existent dialog.410 Gone— email-confirmation link expired.413 Payload Too Large— request body exceeds the endpoint cap. Returned by nginx as plain HTML, not the JSON shape above.429 Too Many Requests— rate limit hit; see Limits & Restrictions.
What we deliberately do NOT return
- Distinct error messages for missing vs invalid vs revoked bearer (anti-enumeration).
X-RateLimit-*headers (anti-calibration).- Stack traces or internal field names from validation libraries beyond the message string.
Server: nginx/<version>—server_tokens offon all public endpoints.