← Blog

14 July 2026

Watching an AI agent misdirect an email — and the server refuse it

The misdirected email is the unglamorous failure every ops team actually fears: the right document, the wrong counterparty. Autocomplete does it to humans daily. An agent that drafts and sends its own mail can do it at machine speed. This post walks through what happens when an agent on a governed mailbox tries — every request and response below is lifted from a Playwright test that runs in our CI on every push.

The setup: a mailbox that knows who it may never email

The mailbox belongs to an ops team handling cargo documentation. Its policy does two things: allowlists the senders the agent may act on, and — the part this post is about — blocklists the counterparties this mailbox may never send to, no matter what the agent thinks it's doing:

{
  "defaultAction": "bounce",
  "senders": [{ "match": { "address": "ops@your-team.example" }, "capabilities": [] }],
  "blocklist": ["stranger@rival-broker.example", "rival-broker.example"],
  "mode": "live",
  "auditLog": { "retentionDays": 90, "includeBodyHash": false }
}

That policy lives on the server, set by the mailbox owner over PUT /api/v1/mailboxes/{id}/policy. The agent can read it. It cannot change it.

The mistake

The agent has a Certificate of Analysis to send. It picks the wrong recipient — the counterparty on the other side of the deal:

POST /api/v1/mcp/send
{
  "inbox_id": 42,
  "to": ["stranger@rival-broker.example"],
  "subject": "Certificate of Analysis — cargo MT-4471",
  "text": "Attached: the final COA for cargo MT-4471."
}

It doesn't matter why the agent picked that address — a hallucinated contact, a prompt-injected instruction buried in an inbound email, or plain old wrong-row-in-the-CRM. The send is evaluated server-side, after the model has done its reasoning and can no longer be argued with.

The refusal

HTTP 200
{
  "status": "blocked",
  "policy": { "decision": "blocked", "matched_rule": "blocklist" }
}

Two things worth noticing.

It's a 200, not an error. A policy block is a successful, well-formed outcome. The agent gets structured data it can reason about — "this recipient is blocklisted" — instead of an exception it might be tempted to retry its way around. Blocked and held-for-approval sends are results, not failures; agent code branches on status, not on error handling.

The rule is named. matched_rule: "blocklist" tells the agent (and the audit trail, and the owner) exactly which line of policy fired. No opaque 403, no guessing.

The evidence

The refusal is recorded twice, for two different readers.

For the agent and its operator, the tamper-evident audit log — each row hash-chained to the previous one, denials included, because a blocked send is security signal, not noise:

{ "tool": "mailbuttons_send_email", "decision": "blocked", "reason": "blocklist" }

For the mailbox owner, the dashboard's Blocked sends view names the counterparty the agent was refused from emailing — the recipient itself is deliberately kept out of the hash-chained log (only a digest goes there) and shown only to the owner.

Why we test this in public

This whole sequence — provision a governed mailbox, set the policy, attempt the misdirected send, assert the block, assert the audit row, assert the owner's view — is a Playwright test in our repository's CI. It runs on every push. The demo video on our governance page is a recording of that test passing; if the journey ever breaks, the test fails and the video comes down. We think that's the right standard for security claims: if it isn't continuously tested, it's marketing.

The same enforcement applies to every send path — the agent tool surface, the REST send endpoint, and threaded replies — along with per-account send caps, so a compromised or confused agent can't spray mail even at permitted recipients.

Want this for your own agent? An inbox takes about 60 seconds: quickstart — or hand your agent the signup guide and let it do the work. mbag.ai runs on the platform behind Mailbuttons, where the same governance carries SSO, custom domains, and audit exports for regulated teams.

Like this? Subscribe to Herald, the Mailbuttons news agent — your email joins a live policy allowlist on our own platform.