# Control who can email your agent (/core/inbound-control)

<!-- agent-signals: reading_time_min: 7 · est_tokens: 2917 · updated: 2026-09-06 -->
Related: [Receive email](/core/receive.md), [Send and reply](/core/send.md), [Manage conversations](/core/conversations.md), [Connect external mail tools: IMAP and SMTP](/core/imap-smtp.md)



# Control who can email an inbox

AgentMail classifies every inbound message, then filters senders through allow and block lists kept per direction at inbox, pod, and organization scope. Use these endpoints to narrow who reaches an agent's inbox, audit what was filtered, and rescue false positives.

## Do this

Block one sender on an inbox, confirm the entry, and audit what the lists and spam screening caught.

```bash
# 1. Add a block entry for new inbound mail
curl -X POST "https://api.agentmail.to/v0/inboxes/example@agentmail.to/lists/receive/block" \
  -H "Authorization: Bearer $AGENTMAIL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "entry": "blocked-sender@example.com", "reason": "unwanted newsletter" }'

# 2. Confirm the entry and how it matches (entry_type: email or domain)
curl "https://api.agentmail.to/v0/inboxes/example@agentmail.to/lists/receive/block" \
  -H "Authorization: Bearer $AGENTMAIL_API_KEY"

# 3. Audit filtered mail, which listings hide by default
curl "https://api.agentmail.to/v0/inboxes/example@agentmail.to/messages?include_blocked=true&include_spam=true&include_unauthenticated=true" \
  -H "Authorization: Bearer $AGENTMAIL_API_KEY"

# 4. Remove the entry
curl -X DELETE "https://api.agentmail.to/v0/inboxes/example@agentmail.to/lists/receive/block/blocked-sender@example.com" \
  -H "Authorization: Bearer $AGENTMAIL_API_KEY"
```

## SDK

Install: `npm install agentmail` (TypeScript), `pip install agentmail` (Python), `npm install -g agentmail-cli` (CLI).

* Create an entry: TS `client.inboxes.lists.create("example@agentmail.to", "receive", "block", { entry, reason })`, Python `client.inboxes.lists.create(inbox_id="example@agentmail.to", direction="receive", type="block", entry="...", reason="...")`, CLI `agentmail inboxes:lists create --inbox-id "example@agentmail.to" --direction receive --type block --entry "..." --reason "..."`
* List entries: TS `client.inboxes.lists.list("example@agentmail.to", "receive", "block")`, Python `client.inboxes.lists.list(inbox_id="example@agentmail.to", direction="receive", type="block")`, CLI `agentmail inboxes:lists list --inbox-id "example@agentmail.to" --direction receive --type block`
* Delete an entry: TS `client.inboxes.lists.delete("example@agentmail.to", "receive", "block", "blocked-sender@example.com")`, Python `client.inboxes.lists.delete(inbox_id="example@agentmail.to", direction="receive", type="block", entry="blocked-sender@example.com")`, CLI `agentmail inboxes:lists delete --inbox-id "example@agentmail.to" --direction receive --type block --entry "blocked-sender@example.com"`
* List filtered messages: TS `client.inboxes.messages.list("example@agentmail.to", { includeBlocked: true, includeSpam: true, includeUnauthenticated: true })`, Python `client.inboxes.messages.list(inbox_id="example@agentmail.to", include_blocked=True, include_spam=True, include_unauthenticated=True)`, CLI `agentmail inboxes:messages list --inbox-id "example@agentmail.to" --include-blocked=true --include-spam=true --include-unauthenticated=true`

Full reference: `/integrations/sdks-and-cli`.

## Facts

* Two checks reject inbound mail outright: a virus scan, and a DMARC failure when the sender's policy is `quarantine` or `reject`. Rejected mail is never stored and no bounce goes back, so the sender's mail server may still report the message as delivered.
* Before an enforcing DMARC failure drops a message, AgentMail validates the message's ARC chain. When a trusted forwarder (google.com, microsoft.com, outlook.com, or icloud.com) sealed it, the original sender's authentication results replace the failing ones. This is what lets mail auto-forwarded from a Gmail or Outlook account pass.
* Everything not rejected is delivered. Mail that passes every check carries just the `received` label. The remaining checks add labels instead of rejecting: `unauthenticated` (the sender could not be verified), `spam` (failed spam screening), `blocked` (a list rejects the sender). One message can collect several.
* Messages labeled `spam`, `unauthenticated`, or `blocked` are hidden from message and thread listings by default.
* A message that starts a fresh conversation is checked against the inbox's `receive` lists. A message in a conversation the inbox has already sent into is checked against its `reply` lists. Empty `reply` lists accept every reply.
* An inbox has six lists: an `allow` and a `block` list for each of `receive` (new inbound mail), `reply` (replies), and `send` (outbound recipients). An entry lives on the allow or the block list of a direction, never both.
* An entry containing `@` matches that one address and stores `entry_type` `email`. Anything else is a domain, matches every address on it, and stores `entry_type` `domain`.
* A `block` entry rejects matching senders and changes nothing else. Once an allow list has any entry, only matching senders get through and everyone else is blocked. With no allow entries, an inbox accepts mail from anyone.
* Entries can be created for a whole pod or organization, and they apply to every inbox inside. When several entries match one sender, the most specific wins: an inbox entry beats a pod entry, which beats an organization entry, and within one scope an exact address entry beats a domain entry.
* The allow default applies across scopes: a single organization-wide `receive` allow entry blocks unmatched new senders for every inbox in the organization. An inbox-level allow for one address carves an exception into an organization-wide block on its domain.
* Entries affect mail that arrives after they exist. Messages already in the inbox keep their labels. Removing an entry also only affects mail that arrives afterward.
* Create: `POST /v0/inboxes/{inbox_id}/lists/{direction}/{type}` with required `entry` and optional `reason` (up to 1,024 characters, stored on the entry and returned whenever it is read).
* The create response's `scope_key` names the exact list the entry landed on, scope, id, direction, and type in one string, like `inbox#example@agentmail.to#receive#block`. Use it when auditing entries across inbox, pod, and organization scopes.
* Read: `GET /v0/inboxes/{inbox_id}/lists/{direction}/{type}` with optional `limit` and `page_token`. Entries return sorted alphabetically by `entry`, with `count` and a `next_page_token` when more remain.
* Delete: `DELETE /v0/inboxes/{inbox_id}/lists/{direction}/{type}/{entry}` returns `204` with an empty body. Pass `entry`, `direction`, and `type` exactly as the create returned them, at the scope that owns the entry.
* AgentMail keeps read-only suppression entries on the organization's `send` block list, added when a recipient hard-bounces, complains of spam, or unsubscribes. They show `read_only: true` when listed.
* CLI sign-up seeds the organization's `send` allow list with the sign-up email address, so an unverified organization can email only that address. A send to anyone else fails with `403` `message_rejected`. Completing verification deletes the entry.
* Filtered mail is stored. Message and thread listings return it with `include_blocked`, `include_spam`, `include_unauthenticated`, and `include_trash` (mail moved to trash with the `trash` label).
* Each include flag needs the matching read permission on the API key, like `label_blocked_read` for `include_blocked`. Keys created without a permissions restriction hold them all.

## Not supported

* Rejected mail (a virus, or an enforcing DMARC failure with no trusted ARC rescue) is never stored. It appears in no listing even with every include flag set, and no bounce goes back to the sender.
* An SPF or DKIM failure alone does not reject a message. An unverifiable sender is delivered with the `unauthenticated` label.
* Creating an entry does not relabel or remove messages already delivered. Entries filter only mail that arrives after they exist.
* An entry cannot sit on both the allow and the block list of the same scope and direction. The create fails with `409` `conflict`.
* Read-only suppression entries cannot be removed through the API. Only [support@agentmail.cc](mailto:support@agentmail.cc) can remove them.
* A pod or organization entry cannot be removed through the inbox path. The delete finds nothing there. Remove the entry at the scope that owns it.

## Errors

| Error                | Status | Cause                                                                                                                                                                                                                  | Fix                                                                     |
| -------------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------- |
| `validation_error`   | 400    | The entry parses as neither an email address nor a domain.                                                                                                                                                             | Send one address or one bare domain as `entry`.                         |
| `missing_permission` | 403    | The credential cannot create entries, usually an organization from CLI sign-up that has not completed verification. Every key in the organization is gated until verification completes.                               | Complete verification.                                                  |
| `conflict`           | 409    | The entry already sits on the opposite list of this scope and direction.                                                                                                                                               | Remove the entry named in the error's `fix` field first.                |
| `not_found`          | 404    | On read: the inbox is not visible to the credential (mistyped `inbox_id`, or a key scoped to a different inbox). On delete: no such entry on this list (wrong direction or type, or the entry lives at another scope). | Check `inbox_id`, key scope, `direction`, `type`, and the owning scope. |
| `cannot_delete`      | 409    | The entry is one of AgentMail's read-only suppression entries.                                                                                                                                                         | Contact [support@agentmail.cc](mailto:support@agentmail.cc).            |
| `message_rejected`   | 403    | An unverified organization sent to an address other than its sign-up email.                                                                                                                                            | Complete verification.                                                  |

## Verify

```bash
curl -s -w "\n%{http_code}\n" "https://api.agentmail.to/v0/inboxes/example@agentmail.to/lists/receive/block" \
  -H "Authorization: Bearer $AGENTMAIL_API_KEY"
```

A `200` with `count` and an `entries` array holding the new entry confirms the edit. Check the entry's `entry_type`: `email` matches one address, `domain` matches every address on the domain. A missing `@` turns an intended address entry into a domain entry.

## Related

* `/core/imap-smtp` - point mail clients, libraries, and warm-up platforms at the same inbox
* `/advanced/deliverability` - bounce and complaint practices after a rejected or suppressed send
* `/core/conversations` - list and read the threads these lists protect, with the same include flags
