Most teams wiring an agent into email give it the same mailbox the humans use. It works until two agents poll the same folder and one of them marks a customer reply as read.
The fix is boring and structural: give every agent its own address.
The part that actually breaks
IMAP was designed for a human client that opens a folder, reads a window of messages, and leaves the rest alone. An agent does not do that. It calls a list endpoint, gets the newest N messages, processes them, and then has to remember which ones it already handled. If the process restarts between the fetch and the write, the same inbound email is answered twice.
The reliable pattern is to let the server own the read state. A dedicated address per agent means 'unread for this agent' is a server-side fact, not an in-memory flag. The agent asks for unread messages, the server hands them over and marks them, and a crash mid-processing leaves at most one message in a retryable state instead of an unbounded duplicate window.
What a per-agent address buys you
- Isolation: one agent's retry loop cannot consume another agent's queue.
- Attribution: every reply leaves from the address the customer wrote to, so threading survives and the customer sees a consistent identity.
- Cleanup: deleting an agent deletes its mailbox, not a slice of a shared one.
The concrete detail worth stealing
Store the message id, not the sequence number. Sequence numbers shift when a message is expunged; a stable id lets the agent deduplicate across restarts and across two workers running the same job. If you only keep the last seen sequence, a single delete in the mailbox silently replays old mail.
That is the whole design: one address per agent, server-owned read state, stable ids for dedupe. Everything else is plumbing.
If you want to see the shape of it in an API, Volanea implements exactly this: inbound mail parsed to JSON, per-agent inboxes, and reply threading handled server-side.
Top comments (0)