# Error codes

> The error codes the API returns, the send denial reasons, and what to do about each.

Error responses usually look like `{"error": "...", "detail": "..."}`. Only branch on `error`. A first `403 policy_denied` response carries `decision` instead of `detail`.

## HTTP errors

| Status | `error` | Meaning and what to do |
|---|---|---|
| `400` | `invalid_request` | The request is malformed. Fix it using `detail`. |
| `400` | `invalid_recipient` | An address can't receive mail in that form. Use plain `local@domain` addresses. |
| `400` | `idempotency_key_required` | Add an `Idempotency-Key` header. |
| `401` | `unauthorized` | The token is missing, wrong, revoked, or expired. |
| `403` | `forbidden` | The token lacks the scope, or you asked for another agent's data. |
| `403` | `policy_denied` | The policy blocked the send. Check `decision.reasons` (table below). |
| `404` | `message_not_found` · `thread_not_found` · `approval_not_found` · `attachment_not_found` · `schedule_not_found` · `audit_not_found` | Doesn't exist or can't be seen. Quarantined mail looks the same as missing mail. |
| `409` | `idempotency_key_reused` | The key was used for a different request. Use a new key. |
| `409` | `policy_changing` | The policy is being changed. Try again shortly. |
| `422` | `forward_refused_injection_risk` | Forwarding this email was refused. Write a new email in your own words. |
| `429` | `cancel_rate_limited` | Too many schedule cancellations. Tell the owner. |
| `502` | `send_failed` | Nothing went out. You can retry with a new idempotency key. |

## Errors on every path

| Status | `error` | Meaning and what to do |
|---|---|---|
| `400` | `invalid_json` | The body isn't valid JSON. |
| `400` | `invalid_request` | A query parameter was given more than once. |
| `404` | `route_not_found` | No such path. Check the path and method. |
| `405` | `method_not_allowed` | This path doesn't take that method. The `Allow` response header lists the ones it takes. |
| `413` | `payload_too_large` | Request bodies can be up to 10 MiB (10,485,760 bytes). |
| `500` | `internal_error` | The server couldn't process the request. `detail` includes a request ID. |
| `503` | `service_unavailable` | Temporarily unavailable. `detail` includes a request ID. |

After a `500`, a `503`, a timeout, or a network error, wait a little and retry with **the same** `Idempotency-Key`. Mail that already went out won't go out twice. Under heavy load you may get a `429`, whose body may not follow this shape; wait and retry with the same key too. If it keeps happening, [contact support](https://docs.atmark.ai/en/help/contact) with the request ID.

Codes specific to schedules, verification codes, and webhooks are on their pages: [Scheduled email](https://docs.atmark.ai/en/api/scheduled#cancel), [Verification codes](https://docs.atmark.ai/en/api/verification-code), [Webhooks](https://docs.atmark.ai/en/api/webhooks).

## Common denial reasons

These are the codes you'll see most often in `decision.reasons` of `policy_denied` and in MCP `denied` results. While the cause remains, retrying gives the same result. Retrying with the same idempotency key returns the same denial even after the cause is fixed, so after fixing it, or waiting `retry_after_seconds`, send with a **new** idempotency key.

| Reason | Meaning | What to do |
|---|---|---|
| `recipient_not_allowlisted` | The recipient isn't on the outbound allowlist. | The owner adds them. |
| `recipient_blocklisted` | The recipient is on the outbound blocklist. | The owner removes them. |
| `outbound_disabled` | The outbound mode is Block all. | The owner changes the mode. |
| `recipient_is_self` | The agent tried to email its own address. | It can't email itself. |
| `suppression_list` | The address has a history of bounces or spam reports. | Check the address. |
| `rate_limit_per_minute` · `rate_limit_per_hour` · `rate_limit_per_day` | A sending limit was reached. | Wait, then send. |
| `recipients_per_message` · `plan_recipients_per_message` | Too many recipients in one message. | Split it up. |
| `daily_send_limit` | The organization's daily sending limit was reached. For new organizations this limit rises over the first weeks after the first send. | Wait and send later. Upgrading doesn't lift it. |
| `plan_daily_cap` | The plan's daily recipient limit for the organization was reached. | Send tomorrow. Upgrading lifts it, but payments aren't open during the beta. |
| `org_pending_review` | The organization is under operator review. | Wait until review is done. |
| `org_review_rejected` | Sign-up wasn't approved. | Contact support. |
| `org_owner_paused` | Emergency stop is on for the whole organization. | The owner resumes it. |
| `org_abuse_freeze` | Operators froze sending for the organization. | Contact support. |
| `org_billing_blocked` | Sending is blocked by a billing problem. | The owner checks **Settings › Plan**. |
| `agent_not_active` | The agent is stopped or not active. | The owner checks. |
| `from_forgery` | `from` isn't this agent's address. | Use the agent's own address. |
| `evaluation_error` | The send couldn't be evaluated, so it was blocked to be safe. | Try again later with a new idempotency key. If it keeps happening, contact support. |

## All reason codes

These are the codes the public API currently returns in `decision.reasons` and in audit `reasons`. Older audit records may show earlier names. Allowed sends carry reasons too. "Old mode" means an old outbound mode (Approval required, Replies only) that Atmark operators set. See [Approvals](https://docs.atmark.ai/en/email/approvals).

### Denied

| Code | Meaning |
|---|---|
| `org_pending_review` | The organization is under operator review. |
| `org_review_rejected` | Sign-up wasn't approved. |
| `org_owner_paused` | Emergency stop is on for the whole organization. |
| `org_abuse_freeze` | Operators froze sending for the organization. |
| `org_billing_blocked` | Sending is blocked by a billing problem. |
| `agent_not_active` | The agent is stopped or not active. |
| `outbound_disabled` | The outbound mode is Block all. |
| `from_forgery` | `from` isn't this agent's address. |
| `recipient_is_self` | The agent tried to email its own address. |
| `suppression_list` | The address has a history of bounces or spam reports. |
| `recipient_blocklisted` | A recipient is on the outbound blocklist. |
| `recipient_not_allowlisted` | A recipient isn't on the outbound allowlist. |
| `rate_limit_per_minute` · `rate_limit_per_hour` · `rate_limit_per_day` | The agent's per-minute, hourly, or daily sending limit was reached. |
| `recipients_per_message` | Too many recipients for the agent's policy limit. |
| `plan_recipients_per_message` | Too many recipients for the plan's limit. |
| `recipient_ceiling_exceeded` | Too many recipients for the hard cap no plan can exceed. |
| `daily_send_limit` | The organization's daily sending limit was reached. For new organizations it rises over the first weeks after the first send. |
| `plan_daily_cap` | The plan's daily recipient limit for the organization was reached. |
| `budget_exhausted` | The agent's sending budget was used up. Contact support. |
| `evaluation_error` | The send couldn't be evaluated, so it was blocked to be safe. |
| `release_window_expired` | It was approved but couldn't be sent within the allowed time, so it was closed (old mode). |
| `reply_only_no_inbound_thread` | It tried to send outside a received thread (old mode "Replies only"). |
| `reply_only_inbound_quarantined` | The received email being replied to is quarantined (old mode "Replies only"). |

### Allowed

| Code | Meaning |
|---|---|
| `allowlist_match` | Every recipient is on the outbound allowlist. |
| `unrestricted_mode` | The outbound mode is All. |
| `not_blocklisted` | In Blocklist mode, no recipient is on the blocklist. |
| `approval_granted` | A person approved it. |
| `standing_approval` | A recipient (or domain) approved before (old mode). |
| `seed_relationship` | A recipient the owner listed when creating the agent (old mode). |
| `reply_within_thread` · `auto_approve_reply_within_thread` | Allowed as a reply within the same thread (old mode). |
| `auto_approve_verification_sender` | Allowed as a reply to a verification-mail sender (old mode). |
| `auto_approve_domain` | A domain the owner allowed automatically (old mode). |

### Awaiting approval

| Code | Meaning |
|---|---|
| `new_recipient` | A first-time recipient needs a person's approval (old mode "Approval required"). |
| `bootstrap_grace` | Marks a decision made during the starting period of the old mode "Replies only". |

---

Source: https://docs.atmark.ai/en/api/errors · Last updated 2026-09-27
