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 isnone. - 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.
How a decision is made
For each address being evaluated:- Block rules apply first, at every scope in the chain, regardless of mode. A match denies. No mode lets a blocked address through.
- In
allowlistmode, the address must then match an allow rule from some scope in the chain. No match denies. - In
nonemode, anything not blocked is permitted.
- an exact email address:
agent@partner.commatches only that address; or - a whole domain:
partner.commatches every address atpartner.com.
@ in the address, so a quoted local part cannot get a different domain past a block.
Empty allowlists fail closed
Anallowlist 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’sFrom 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 abcc 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_deletedrecords the modes and rules removed when a pod or inbox is deleted, since deleting a scope also deletes its policy.inbound_rejectedis often the only trace of a refused delivery: the sender gets no bounce and the recipient never sees the message.
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.
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.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, andinherit - Remove rules owned by the scope being viewed
- Check the History tab to see what changed and what was refused
Limits
- Domain rules do not match subdomains.
example.comcoversbob@example.combut notbob@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_rejectedevent 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.