# Error codes

Every API error is a JSON body with `code`, `error`, `hint` and `retryable`. Codes are stable; a client may switch on them. This page lists the ones an integration is most likely to meet.

## Request and auth

- `bad_json` - the body is not valid JSON. Fix the body.
- `validation` - a field is missing or invalid. The `hint` names the field.
- `bad_id` - a path or body identifier is not a UUID.
- `token_expired` - the API key passed its `expires_at`. Create a new key.
- `forbidden` - the key or member lacks the scope named in `hint`.
- `not_found` - the resource does not exist in this workspace.

## Sending

- `suppressed` - the recipient is on the suppression list. Remove it only with the recipient's consent.
- `newsletter_consent_missing` - a newsletter send needs a newsletter channel grant. Import the address as active or complete double opt-in.
- `consent_withdrawn` - the recipient opted out. Do not send again.
- `impressum_incomplete` - a marketing send needs the legal sender fields (street and city).
- `route_kind_mismatch` - a newsletter must use the `broadcast` route, transactional mail uses `outgoing`.
- `safe_mode` - the workspace or project is in the sandbox and the recipient is not a simulator or allowlisted address.
- `content_blocked` - the content gate refused the message. The response carries the reasons.
- `scanner_unavailable` - the spam filter is unreachable, so marketing is refused; retry later. Transactional mail keeps running.
- `send_failed` - the provider did not accept the message; the hint says whether a retry helps.

## Agent keys

- `recipient_not_allowed` - the recipient is outside the key's allowlist.
- `approval_required` - the recipient count is above the key's threshold. A human must approve, and the `approval.required` event fires.
- `token_daily_cap_reached` - the key reached its daily cap. It resets at midnight UTC.

## Idempotency

- `idempotency_key_reuse` - the same key was used with a different body. Use a new key for a new request.
- `idempotency_in_progress` - the first request with this key is still running. Retry after `Retry-After`.

## Inbound and forwarding

- `attachments_not_supported` - attachments are not accepted; the sender gets a notice.
- `inbound_message_truncated` - reserved for setups with raw storage; the message was larger than the receiving path can read.

`retryable: true` means the same request may succeed later. `retryable: false` means retrying unchanged will fail again.
