Content

How to Stop an AI Agent From Sending the Same Email Twice

Most email APIs have no idempotency key to catch a repeat send, which makes a duplicate email a harder problem than a duplicate charge. Here's how Swytchcode handles the gap.

EngineeringNov 4, 2026

Key takeaways

  • -A slow or dropped response from an email API looks identical to a failed send, so a retrying agent sends the message again — and the recipient gets two identical emails.
  • -Most email-sending APIs don't offer an idempotency key the way a payments API does, so the provider itself usually can't catch a repeat send — the protection has to come from somewhere else.
  • -Swytchcode doesn't auto-retry an email write unless that specific integration's method actually carries an idempotency guarantee, which is rarer for email than for almost any other category.
  • -Every send attempt is logged locally before and after the call, so you can check whether a message already went out instead of trusting the agent's memory of what happened.
  • -The reliable pattern is recording the send attempt in your own system before calling the API, then checking that record on any retry — shifting the responsibility from the provider to your own code.

An agent sends a notification email. The API call hangs, then times out. The agent, reasonably, tries again. A moment later the recipient has two identical emails sitting in their inbox — or worse, two copies of something they really didn't want duplicated, like an invoice or a password reset link.

This is the same retry-meets-non-idempotent-write problem that causes duplicate charges, but email has a wrinkle that makes it harder to fix: the provider usually can't help you.

Why email is the hardest version of this problem

A payments provider like the ones behind most checkout flows will usually let you attach an idempotency key to a charge, and will hand back the original result instead of processing a repeat. Most email-sending APIs don't offer an equivalent mechanism at all. There's no standard "if you've already sent a message with this exact key, don't send it again" header the way there is for a lot of payment writes. Sending a message is just sending a message, every time you ask.

That matters because it means the usual fix — attach a key, let the provider catch the repeat — often isn't available. The responsibility for noticing "this already went out" has to live somewhere other than the provider's side of the connection.

How Swytchcode handles a write the provider can't dedupe

The same rule applies here as everywhere else: Swytchcode only auto-retries a write when an idempotency key is attached to that call. For most email integrations, that condition simply isn't met, which means a failed or unclear send is surfaced as an error instead of quietly retried — the runtime doesn't assume it's safe to send again just because the first attempt looked like it failed.

Every call — the request going out, and whatever comes back — is written to a local log with sensitive values redacted. That log is the thing to check before manually resending anything: did this message attempt already go out, and what did the provider say back? Guessing from memory is exactly how a "just in case" resend turns into the second copy of an invoice landing in someone's inbox.

Setting it up

swy search <email-provider>
swy get <email-provider>
swy info <email-provider>.<send-method>
swy audit network

swy info on the send method tells you plainly whether that call carries any idempotency guarantee at all — don't assume it does because a different provider you've used does. swy audit network is where you go after the fact to see exactly what was sent and when, rather than trusting anyone's recollection of what the agent did.

The pattern that actually works: record before you send

Since the provider usually can't catch a repeat, the dedupe logic has to move to your side of the call. Record the intent to send — this message, to this recipient, for this reason — in your own system before the API call goes out. On any retry, check that record first. If a send is already logged for that message and recipient, skip the call instead of making it again. This is a small amount of extra bookkeeping in exchange for never finding out about a duplicate from an annoyed customer.

A duplicate email usually isn't a sign the agent misbehaved — it's a sign the write it was asked to repeat was never made safe to repeat in the first place, and the provider offered no backstop. Treat every email send like a write that needs its own dedupe logic, because for most providers, that's exactly what it is.

FAQ

Can I build a notification agent with Swytchcode and trust it not to double-send?

Yes, but it takes one extra step compared to a payments call: pair Swytchcode's conservative default — no automatic retry on a write without a key — with your own record-before-send check, since most email providers can't catch the repeat on their own.

Can I add a policy that requires approval before an agent sends certain emails?

Yes. Any send matching a rule you define — going to a whole customer list, say, rather than one person — can be held for a person to approve before it goes out, the same policy mechanism used for high-value payments.

Does Swytchcode retry a failed email send automatically?

Only if that specific send method carries an idempotency key, which is uncommon for email. Without one, the failure is surfaced and left for you or the agent's own logic to decide on, rather than retried blindly.

What's the retry policy on an email send, specifically?

Transient errors — 429s, 503s — are still retried on a read or on a write that's genuinely safe to repeat. The distinction that matters for email is that "safe to repeat" can't be assumed the way it can for an idempotency-aware payment call, so check the specific method before leaning on automatic retries at all.

More content