Skip to main content
Sender rules control who an inbox may receive from and who it may send to. OpenMail evaluates them server-side, before delivery and before a send goes out. An agent cannot bypass them: a scoped key cannot read or change policy at all.

The model

A policy has two halves: inbound (who may write to you) and outbound (who you may write to). Each half is set independently. Policy lives at three scopes: Each direction at each scope has a mode:

Inheritance

The two halves inherit differently:
  • Modes override. Resolution walks inbox → pod → account and stops at the first scope with a mode other than inherit. If nothing is set anywhere, the mode is none.
  • Rules accumulate. The effective rule set is the union of every rule at every scope in the chain. There is no override and no exception list.
A pod-level block cannot be cancelled by an inbox-level allow. A tighter scope can narrow what it inherits, but never loosen it.

How a decision is made

For each address being evaluated:
  1. Block rules apply first, at every scope in the chain, regardless of mode. A match denies. No mode lets a blocked address through.
  2. In allowlist mode, the address must then match an allow rule from some scope in the chain. No match denies.
  3. In none mode, anything not blocked is permitted.
An address matches a rule when the rule is:
  • an exact email address: agent@partner.com matches only that address; or
  • a whole domain: partner.com matches every address at partner.com.
Matching is case-insensitive and values are stored lowercased. The domain is taken from the last @ in the address, so a quoted local part cannot get a different domain past a block.

Empty allowlists fail closed

An allowlist with no allow rules denies everything.
One compatibility exception: policies created through the console carry a legacy flag under which an empty inbound allowlist accepts all mail, and the console shows a warning instead of locking you out. Outbound is fail-closed in every case. Policies created through PUT /v1/policy are fail-closed in both directions.

What is enforced, and where

Inbound

Checked against the sender’s From address when mail arrives. Denied mail is dropped before it is threaded or stored; it never reaches your inbox, fires no webhook or WebSocket event, and doesn’t count toward storage. The sender gets no bounce or notification. The drop shows up as an inbound_rejected event in History; there is no quarantine view.

Outbound

Checked in the send path that every surface goes through, so one enforcement point covers the REST API, the CLI, the SDK, the console compose box, replies, and forwards. Every address a send can reach is checked: One denied recipient rejects the whole send. There is no partial delivery, and a rejected send does not count against your rate limits.

Bcc is not supported

OpenMail has no Bcc. A send carrying a bcc field is rejected rather than accepted with the field ignored. Accepting it would mean reporting success for a recipient that was never delivered to and never checked against your rules.

Who can change policy

A pod- or inbox-scoped key, the kind an agent runs with, gets 403 on every policy endpoint, reads included. An agent cannot change the rules that apply to it, and cannot see them either.

History

Every rule change, and every send or delivery that was refused, is recorded in your Allow/Block List history. Entries can’t be edited or deleted, not even over the API. Each event also carries its scope, the direction where one applies, and the console user who made the change. The two rejection actions have no actor; they are written by enforcement, not a person.
  • scope_deleted records the modes and rules removed when a pod or inbox is deleted, since deleting a scope also deletes its policy.
  • inbound_rejected is often the only trace of a refused delivery: the sender gets no bounce and the recipient never sees the message.
Rejection events are deduplicated to one entry per inbox and address per hour, so a retry loop against a blocked address cannot flood the history. Check it in the console under Allow/Block List → History, or over the API:

API reference

All policy endpoints require an account-wide API key. Select the scope with ?podId= or ?inboxId=; neither means the account scope.

Set up rules in one call

PUT /v1/policy sets modes and replaces the scope’s own rules for any direction you include, so re-running it converges to the state you declared instead of accumulating rules. Omitted directions are left untouched; pass [] to clear a direction’s rules. The whole body is validated before anything is applied, so a bad outbound section cannot leave a half-applied inbound section behind.
Up to 500 rules per direction per call.

Read what is in force

GET /v1/policy returns the scope’s own modes and rules plus what it inherits, so you can see what actually applies without walking the chain yourself:
rules are set on this scope; inheritedRules come from the pod and account. An inherited rule can only be removed at the scope that owns it; deleting one from a child scope returns 403 inherited_rule.

History query parameters

Worked example

One pod per customer tenant, one inbox per agent. The account blocks what nobody should reach, the pod narrows it to the tenant, the inbox narrows it to one contact.
The agent’s key can send only from its inbox, only to ap@tenant.com, and never to known-bad.example. It cannot read the policy, change it, or mint a broader key. Every attempt to reach outside that set is refused synchronously and recorded.

Managing rules in the console

Go to Allow/Block List in the Dashboard. The account owner can:
  • Add email addresses or domains to the allow or block list, per direction
  • Switch each direction’s mode between none, allowlist, and inherit
  • Remove rules owned by the scope being viewed
  • Check the History tab to see what changed and what was refused
Members see the same page read-only.

Limits

  • Domain rules do not match subdomains. example.com covers bob@example.com but not bob@mail.example.com. Add each domain you want covered.
  • No wildcards or regular expressions. A rule is an exact address or an exact domain.
  • Rules are address-based only. There is no filtering on subject, body content, or attachment type.
  • Denied inbound mail is dropped silently. No bounce, no notification, no quarantine view. The drop is recorded as an inbound_rejected event in History.
  • A value cannot be in both allow and block for the same direction at the same scope. The API rejects this with 409, since block always wins and the allow would be dead. Across different scopes it is permitted, and block still wins.
  • No plan gating. Sender rules are available on every plan.
Sender rules work alongside suppressions. Suppressions automatically block addresses that have bounced or been reported as spam. Sender rules give you manual control on top of that.