Errors and limits
Error shape
Failed requests return JSON:
{
"code": "content_blocked",
"error": "message blocked by the content gate",
"hint": "Remove promotional phrasing or send on the broadcast route after DOI."
}
hint is safe to show in a UI. Do not parse error for control flow — use code.
HTTP status
| Status | Typical meaning |
|---|---|
| 202 | Send accepted (queued) |
| 400 | Validation, wrong route/kind, missing fields |
| 401 | Missing or invalid bearer |
| 403 | Authenticated but not allowed (role / plan / paused) |
| 404 | Unknown id (or hidden cross-tenant id) |
| 409 | Conflict (duplicate, already in that state) |
| 410 | Retired endpoint (POST /api/public/unsubscribe) |
| 413 | Body larger than the message cap (~48 MiB) |
| 422 | Content gate hard-block or compliance gate |
| 429 | Rate limit or monthly cap |
Common codes
| Code | What to do |
|---|---|
content_blocked |
Read content_reasons; use POST …/send/score |
suppressed |
Recipient is on a team/project/route list |
paused |
Reputation or abuse pause — fix the list, then resume |
plan_cap |
Monthly volume reached |
warm_up |
Wait or stay on transactional |
impressum_incomplete |
Fill street, city, privacy URL |
dkim_unverified |
Publish DKIM and re-check DNS |
route_kind_mismatch |
Newsletter → broadcast; termin → outgoing |
safe_mode |
Recipient not on the allowlist / simulator |
attachment_rejected |
Type, size, or double-extension |
Exact strings can grow; treat unknown codes as generic failures and show hint.
Idempotency
Idempotency-Key (or X-Idempotency-Key) on single send, and idempotency_key on batch items / bulk, replay the original status and body for the same workspace + key. Keys are scoped per tenant.
Rate limits
Werkmail applies a per-tenant client limiter in addition to the outbound platform. Bursting thousands of un-idempotent POSTs will 429. Prefer batch/bulk and backoff on 429.
HTTP bodies are capped at MaxMessageRequestBytes (tens of MiB) so a single request cannot exhaust the process.