Contact Lease for AI Agents: How It Stops Duplicate Email

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

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.

An illustration of a library service desk explaining a contact lease concept with agents waiting in line.

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.

FieldHoldsUsed for
Recipient keyLowercase address plus canonical domainFinding every lease and cooldown for one person
Owner agentID of the agent holding the leaseDeciding which agent may send
Acquired atTimestamp when the lease beganMeasuring how long the claim has been held
Expires atTimestamp when the lease lapses unless renewedReleasing ownership without manual cleanup
Fencing tokenInteger that rises on every new acquisitionRejecting writes from a stale holder
Cooldown untilTimestamp set after a successful deliveryBlocking 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 or expires_at is in the past. It sets owner_agent to the caller, adds 1 to the stored fencing_token, and sets expires_at to 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.

SituationLease lengthRenewalCooldown after send
Drafting or deciding one message60 seconds to startRenew while the draft is openSet by the send that follows
Multi-step outreach sequence5 minutes per stepRenew at each stepSet by policy after the final step
Reply to an inbound messageHeld only while composingRenew while composingPolicy decides, transactional replies may skip it
Emergency overrideSet by a personNeeds an audit entryRecorded 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.

A six-step infographic explaining how a send process handles a lease check for recipient addresses.

  1. Normalize the recipient address to the lease key, so casing and domain aliases resolve to one record.
  2. Build an idempotency key from thread, recipient, and campaign action, so a retry can't create a second send.
  3. Confirm the caller still holds the lease and that its fencing token matches the current token.
  4. Evaluate the suppression list, which overrides every other rule in the sequence.
  5. Check cooldown and contact history for the recipient across every agent.
  6. 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.

SituationResult returnedAgent action
Another agent holds a live leaseConflict: contact owned by another agentStop, log the conflict and wait for release or hand off
Recipient is inside cooldownBlocked: cooldown active until timestampDrop the send or schedule it after the cooldown ends
Lease expired before the sendRejected: lease expiredReacquire the lease, then run the checks again
Token older than the current tokenRejected: stale fencing tokenDiscard the draft; never resend with the old token
Recipient on suppression listBlocked: suppressedStop; suppression applies to every agent
Timeout after send with unknown resultAmbiguous: send status unknownLook 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.

MechanismScopeLifetimeWho changes it
Contact leaseOne agent, one recipientMinutes, renewed while work is activeThe owning agent, released on send, discard or expiry
CooldownEvery agent, one recipientStarts after a delivery, length set by policyMailbox policy
SuppressionGlobal, one addressUntil an opt-out or a person clears itRecipient opt-out or a person
Idempotency keyOne logical sendKept with the send recordThe 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.

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.

A six-step infographic detailing how to test two-agent races for lease management systems before shipping software.

  1. 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.
  2. An agent pauses past lease expiry, resumes, and sends. The send is rejected on token, because a newer acquisition has raised the stored value.
  3. A timeout occurs after a send. A retry with the same idempotency key returns the original message ID and sends nothing new.
  4. A recipient joins the suppression list during an active lease. The next send is blocked, even though the lease itself is still valid.
  5. 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.
  6. 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.

The Molted team