Contact Lease for AI Agents: How It Stops Duplicate Email

A contact lease is a time-limited claim that gives one agent the right to contact one recipient while every other agent waits. It matters the day a second agent gets access to the same mailbox, list, or thread, because two agents that never see each other's drafts can still email the same person within minutes. A contact lease turns that collision into an orderly claim, with an owner, an expiry time, and a record of what happened.
Table of Contents
- What a Contact Lease Is and Why Agents Need One
- Lease Record Fields and What Each One Holds
- Lease Length, Renewal and Cooldown by Situation
- How a Send Passes the Lease Check
- Conflict Results and What the Agent Does Next
- Which Rule Wins When Leases, Suppressions and Cooldowns Clash
- Test Two-Agent Races Before You Ship
What a Contact Lease Is and Why Agents Need One
A contact lease works like a hold slip at a library desk or a reserved sign on a restaurant table. The sign holds the table for one party, for a set time, and it comes off if nobody sits down. Nobody argues about the table because the sign is visible, named, and temporary.

Say a sales agent reads a CRM note and drafts a follow-up to jane.doe@acme.com. At the same time, a support agent sees an inbound question from the same person and writes an answer. Without coordination, both messages leave, and the recipient gets two emails from one company that appear to ignore each other.
One Person, One Key
The lease only works if both agents claim the same key. Jane.Doe@Acme.com and jane.doe@acme.com must resolve to one record, built from a lowercase address and a canonical domain. Domain aliases need the same treatment, or two spellings of one person become two separate leases and the duplicate slips through.
Teams that want to turn inbound replies into qualified conversations feel this problem first, because a reply and a scheduled follow-up often land in the same hour. Coordination at the mailbox level, described in the multi-agent coordination feature overview, keeps those two intents from becoming two sends.
A lease answers who may act now, not whether the recipient should be contacted at all.
Scope stays narrow on purpose. A lease covers the window of work, such as drafting, deciding, or sending. It doesn't own the relationship, it doesn't grant consent, and it doesn't override an opt-out. That narrow scope is what makes it safe to expire, renew, and hand over.
Lease Record Fields and What Each One Holds
A single boolean flag in a database looks simpler and fails without warning. A flag says the contact is busy, but it can't say who holds the claim, when it lapses, or whether the holder crashed five minutes ago. Once an agent dies with the flag set, the contact is stuck until a person clears it.
A proper record treats the contact lease as a time-bounded distributed lock keyed by normalized recipient identity. Google's Chubby coordination design describes coarse-grained locks, sessions, lease expiration, and sequencers that let servers reject operations from stale lock holders. That model maps directly onto agent email, where a paused agent is the stale holder.
| Field | Holds | Used for |
|---|---|---|
| Recipient key | Lowercase address plus canonical domain | Finding every lease and cooldown for one person |
| Owner agent | ID of the agent holding the lease | Deciding which agent may send |
| Acquired at | Timestamp when the lease began | Measuring how long the claim has been held |
| Expires at | Timestamp when the lease lapses unless renewed | Releasing ownership without manual cleanup |
| Fencing token | Integer that rises on every new acquisition | Rejecting writes from a stale holder |
| Cooldown until | Timestamp set after a successful delivery | Blocking repeat outreach after the lease ends |
Why the Fencing Token Exists
The fencing token is a monotonically increasing integer. Every new acquisition adds one, so the store can tell a current holder from an agent that paused, lost connectivity, or sat through a long garbage-collection delay and then resumed. Without the token, a resumed agent looks identical to the owner and sends on a claim it no longer holds.
Before wiring any of this up, checking CRM data before automating is worth the hour it takes, because lease keys built from messy address data inherit every duplicate in the source system.
Acquisition in Pseudocode
acquire(recipient_key, agent)returns a new token only when no row exists orexpires_atis in the past. It setsowner_agentto the caller, adds 1 to the storedfencing_token, and setsexpires_atto now plus 60 seconds. Any later write presenting a token below the stored value is rejected at the send step.
A worked record makes the fields concrete. For jane.doe@acme.com, the row might show owner sales-02, expiry at 14:01:00, token 41, and cooldown until the following Monday. Three checks validate any record: exactly one owner, an expiry time in the future, and a token greater than the last one issued. The working pattern is a short lease for draft ownership, renewed while work is active, then a separate cooldown timestamp after successful delivery, so expiry releases ownership while cooldown stops the next agent from writing immediately.
Lease Length, Renewal and Cooldown by Situation
One fixed cooldown for every message creates two opposite failures. A flat weekly rule blocks a support reply the recipient is actively waiting for. A purely event-driven system does the reverse and fires a burst whenever three triggers land together, which the recipient reads as spam.
Frequency guidance built for human campaigns doesn't settle this, because it counts unsubscribes and complaints at campaign level rather than contact level. ConsultEvo's enterprise email analysis notes that a 2026 summary citing Gartner reports over 60% of organizations expected to deploy agents within two years, despite only 17% having deployed them at the time. More agents per mailbox makes per-contact timing a shared problem, not a per-campaign setting.
| Situation | Lease length | Renewal | Cooldown after send |
|---|---|---|---|
| Drafting or deciding one message | 60 seconds to start | Renew while the draft is open | Set by the send that follows |
| Multi-step outreach sequence | 5 minutes per step | Renew at each step | Set by policy after the final step |
| Reply to an inbound message | Held only while composing | Renew while composing | Policy decides, transactional replies may skip it |
| Emergency override | Set by a person | Needs an audit entry | Recorded in the audit log |
Reading the Starting Values
These numbers are tuning points, not rules carved into policy. A 60-second lease for drafting is long enough to compose and short enough that a crashed agent frees the contact quickly. Multi-step outreach needs a longer window per step because each step may wait on tool calls, so 5 minutes per step with renewal at each step keeps ownership honest.
Contact load is the total count of messages to one recipient across agents, channels, and teams. Each agent should read that total from one shared timeline before it renews, which is the same discipline covered in the guide to rate limiting and cooldowns for sender reputation. Renewal is a claim that work is still happening, so an agent renews only while it's actively drafting or deciding, never on a timer detached from the work.
Practical rule: if a renewal fails, stop, reacquire, then run the checks again.
An emergency override sits outside the table. A person sets it, the audit log records it, and the override skips cooldown without pretending the cooldown never existed.
How a Send Passes the Lease Check
The check runs at dispatch, not at drafting, because a draft can sit for minutes while ownership changes underneath it. Six steps run in order before any message leaves the mailbox.

- Normalize the recipient address to the lease key, so casing and domain aliases resolve to one record.
- Build an idempotency key from thread, recipient, and campaign action, so a retry can't create a second send.
- Confirm the caller still holds the lease and that its fencing token matches the current token.
- Evaluate the suppression list, which overrides every other rule in the sequence.
- Check cooldown and contact history for the recipient across every agent.
- Write the send record and the authorization in one ordered step, so a stale worker can't pass after a newer owner takes over.
Why the Order Matters
The Chubby paper summary from System Experts explains that the sequencer model protects against delayed messages and race conditions by carrying lock identity and generation information to the server. Step 3 is that idea applied to email. The token check and the send write have to be atomic, or a gap opens between checking and sending where a second agent can slip in.
The race plays out like this. Agent A drafts with token 41 and pauses. Its lease expires, Agent B acquires token 42 and sends, and then Agent A resumes and presents token 41. Step 3 rejects it, because 41 is no longer the stored value. No duplicate leaves, and the rejection carries a reason Agent A can log.
Several agents can share one mailbox, and an agent can take a lease on a contact so no other agent emails that person at the same time. In a self-built setup, the lease store and these checks live in the send code, directly in front of the call that hands the message to the mailbox.
Conflict Results and What the Agent Does Next
A lease check can fail with nobody at fault. Three ordinary conditions cause most failures: an agent pauses past its expiry, a process hits a long garbage-collection pause or dropped connection, and two hosts run with clocks that disagree. Treating these as bugs sends developers hunting for errors that don't exist.
Clock skew shrinks once expiry is judged by the lease store's clock rather than the agent's clock. The agent's local time becomes a hint for when to renew, never the authority on whether the lease is alive. A host running two minutes fast can't resurrect an expired claim, because the store compares its own timestamps.
| Situation | Result returned | Agent action |
|---|---|---|
| Another agent holds a live lease | Conflict: contact owned by another agent | Stop, log the conflict and wait for release or hand off |
| Recipient is inside cooldown | Blocked: cooldown active until timestamp | Drop the send or schedule it after the cooldown ends |
| Lease expired before the send | Rejected: lease expired | Reacquire the lease, then run the checks again |
| Token older than the current token | Rejected: stale fencing token | Discard the draft; never resend with the old token |
| Recipient on suppression list | Blocked: suppressed | Stop; suppression applies to every agent |
| Timeout after send with unknown result | Ambiguous: send status unknown | Look up the idempotency key; retry only if no message ID exists |
A Conflict Is Information, Not an Error
Each result names the next action, and none of them is "retry immediately." Another agent owns the contact, the recipient is in cooldown, or the lease must be reacquired. Silent retries turn a clean conflict into a loop that burns sends against the same wall.
The ambiguous timeout deserves care. The agent sent, the response never arrived, and the message may or may not exist. The correct move is to look up the idempotency key first and retry only if no message ID is recorded. Guessing here is how duplicates happen in systems that got everything else right.
Every decision belongs in an audit log with the lease owner, token, decision, and resulting message ID, so a duplicate can be traced to the exact policy state that allowed it.
Repeated conflicts on the same thread signal a coordination problem a person should see. The escalation rule is simple: if the same conflict repeats, pass the thread to a person rather than looping. A person can spot two agents running the same playbook in seconds, while the agents will contest the lease indefinitely.
Which Rule Wins When Leases, Suppressions and Cooldowns Clash
Four mechanisms decide whether a send is allowed, and they look interchangeable until they conflict. They differ in scope, lifetime, and who controls them, and those differences set the priority order.
| Mechanism | Scope | Lifetime | Who changes it |
|---|---|---|---|
| Contact lease | One agent, one recipient | Minutes, renewed while work is active | The owning agent, released on send, discard or expiry |
| Cooldown | Every agent, one recipient | Starts after a delivery, length set by policy | Mailbox policy |
| Suppression | Global, one address | Until an opt-out or a person clears it | Recipient opt-out or a person |
| Idempotency key | One logical send | Kept with the send record | The send path, not the agent |
A lease grants temporary responsibility and ends on release or expiry. A cooldown is a timestamp that binds every agent after a delivery. A suppression is a global record for an address, and a lease never clears it. An idempotency key doesn't permit or forbid anything; it identifies one logical send so retries collapse into it.
The Legal Floor Under Suppression
Suppression wins every clash because law sits under it. The FTC CAN-SPAM compliance guide states that a commercial message must provide a clear and conspicuous opt-out method that remains able to process requests for at least 30 days after the message is sent, and the sender has 10 business days to honor a request. The sender also can't require more than a reply email or a visit to one web page, charge a fee, or demand additional personally identifying information.
The clash case runs like this. Agent A holds a lease on a prospect, and the prospect replies with an unsubscribe request. Inbound screening writes a suppression, Agent A releases its lease, and Agent B's next send is blocked by the suppression even though no lease exists. Release freed the contact for coordination purposes and changed nothing about permission.
Practical ordering follows from that example. Opt-outs go to the shared suppression list before any lease is released, which is the same discipline set out in the guide to suppression lists and bounce handling for AI senders. One shared list, written first, means no agent can squeeze a send into the gap.
Test Two-Agent Races Before You Ship
Lease logic fails in timing, not in logic diagrams, so it needs race tests in staging with two real agent processes. Six cases cover the failures that reach production most often. Each one has a single expected result, which makes the suite easy to read when something breaks at 2 a.m.

- Two agents claim the same address with different casing. One uses Jane.Doe@Acme.com, the other uses the lowercase form. Exactly one lease succeeds, because both normalize to one key.
- An agent pauses past lease expiry, resumes, and sends. The send is rejected on token, because a newer acquisition has raised the stored value.
- A timeout occurs after a send. A retry with the same idempotency key returns the original message ID and sends nothing new.
- A recipient joins the suppression list during an active lease. The next send is blocked, even though the lease itself is still valid.
- A delivery sets a cooldown and the first agent releases. A second agent is blocked by cooldown after the release, proving the two mechanisms are independent.
- A host with a skewed clock attempts a renewal. The lease store's clock decides the outcome, and the skewed host gets the same answer any other host would.
Running Order and Setup
Tests 1 and 2 run first. They exercise the casing path and the stale-token path, which are the two routes a duplicate takes in production. If either one fails, the remaining four tests measure a system that already leaks.
Tests 3 through 6 then confirm the safety properties around the lease rather than the lease itself. Idempotent retry, suppression during an active claim, cooldown after release, and clock authority each protect a different edge. A suite that passes all six gives a team grounds to add a third or fourth agent to the same mailbox.
Leases stop two agents from emailing the same person at once, and every agent shares one set of checks. Molted is a mailbox for AI agents, for agents what Gmail is for humans, so the sends in these tests can run from a mailbox without a separate email provider account. It works with any agent that speaks MCP, including Claude Code, Cursor, and Codex, through a local setup or the hosted server at mcp.molted.email, and new mailboxes run on their own with approvals as an optional dial.
Add agents one at a time, and rerun the six tests after each addition. Parallelism without atomic claims produces duplicates faster, not throughput.
The practical next step is small. Stand up two agents against one staging mailbox, point both at the same test recipient, and run test 1 today. Start free at molted.email (free for people, paid plans from $19 a month).
Running the six race tests takes one mailbox, two agent processes, and an afternoon, and Molted Email provides the mailbox with contact leases, cooldowns, and suppression checks built into the send path. Connect an agent through MCP, CLI, or REST, claim a contact, and watch the second agent get a clean conflict instead of sending a duplicate.