Skip to main content
MailCub Logo Image
Guidelines

Preventing Duplicate Emails with Idempotency Keys

By MailCub TeamFeb 24, 202610 min read

Introduction

Duplicate transactional emails are usually a side effect of normal failures. A request may time out after the provider already accepted it, a worker may crash after sending but before recording the result, or retries may run while an earlier attempt is still in flight. Users do not care why it happened. They only see duplicate password resets, repeated receipts, or multiple welcome emails.

This guide is for SaaS and development teams, and anyone running app-driven email, who want a production-ready way to reduce duplicates using idempotency keys. It explains what an idempotency key is, how to design one around a stable business event (not a job attempt), where to store it with uniqueness guarantees, and how to reuse it safely across retries.

It also covers visibility. You need to correlate one business event to one provider-accepted send using logs and outcomes. The provided content notes that MailCub documents an API-key-based send flow and response codes that help you design safer retry and dedupe behavior. Start with MailCub Documentation and confirm your first send appears in logs.

Quick Answer

  • Idempotency keys represent one logical email event, which makes retries safe.
  • Build keys from stable identifiers such as event ID, token ID, or order ID, not timestamps.
  • Store keys with a unique constraint and save the provider response or message reference.
  • On retries, reuse the same key and return the stored result instead of sending again.
  • Add TTL by email type (short for OTP, longer for receipts and invoices).
  • Use logs and analytics to verify the event was accepted once and to debug outcomes.

Why It Matters

Duplicates hurt trust. Multiple password reset emails can look like an account attack. Duplicate receipts and invoices create billing confusion and increase support work.

Duplicates also make incidents worse. When providers throttle you, duplicate sends increase traffic at the exact moment you should be slowing down. The provided content notes that MailCub Documentation calls out rate and quota limits and a 429 response code, which is a strong reason to keep retries and dedupe tightly controlled.

The Core Idea Behind Idempotency Keys

An idempotency key is a unique identifier for one logical email event in your product. If the same event is processed again because of retries, replays, or timeouts, your system detects the existing key and avoids sending another email.

This is stronger than basic job dedupe. Jobs can be duplicated in different places. Idempotency works because it is tied to a stable business fact, such as “send reset email for reset_token_id=abc.”

Step-by-Step Implementation

1) Define what “one email event” means in your system

List your transactional events, such as reset requested, invoice finalized, or receipt issued. Make sure each event has a stable ID that you can reference later.

If you do not already have a stable event ID, generate one when the event is created in your database.

2) Create a stable key format (specific, scoped, repeatable)

A good idempotency key should be:

  • Stable across retries
  • Specific enough to avoid blocking legitimate emails
  • Scoped correctly for multi-tenant systems

Recommended patterns from the provided content:

  • email:{tenant}:{event_type}:{event_id}
  • email:{tenant}:reset:{reset_token_id}
  • email:{tenant}:receipt:{order_id}

Avoid keys based only on timestamps or attempt numbers.

3) Store the key with uniqueness and status

Create a record that includes:

  • idempotency_key (unique)
  • status (pending, accepted, sent, failed)
  • provider_message_id (optional)
  • created_at and updated_at
  • expires_at (TTL)

If the insert fails because the key already exists, return the stored outcome and do not send again.

4) Wire it into the send path in the safe order

Use a safe sequence to avoid the crash window:

  • Compute the key
  • Insert record as pending
  • Call provider send
  • Persist provider response
  • Mark record accepted or sent
  • Acknowledge the job (if queued)

The provided content notes that MailCub materials show the send endpoint and auth header format, which helps keep sending centralized and consistent. Use MailCub Documentation to confirm implementation details, and review the Transactional Email page for integration context.

5) Reuse the same key across retries (never regenerate)

Retries must reuse the same key. If you generate a new key on retry, you will send duplicates.

Carry the key in the job payload, or carry the stable identifiers required to recompute the exact same key. This is especially important when handling throttling (429). The provided content notes that MailCub documents 429 for rate limit or quota exceeded and recommends monitoring rate behavior.

6) Add TTL rules by email type

Use TTLs that match how the email is used in the real world:

  • OTP or password reset: 10 to 60 minutes
  • Verification: 6 to 24 hours
  • Receipts or invoices: 30 to 90 days (or longer if your policy requires it)

TTL keeps storage bounded and allows legitimate future sends to happen.

Idempotency Key Recipes

Email type Key format TTL Why
Password reset email:{tenant}:reset:{reset_token_id} 10–60 min Prevents duplicates during retries
OTP email:{tenant}:otp:{user_id}:{purpose}:{time_bucket} 10–30 min Allows new OTP while deduping repeats
Verification email:{tenant}:verify:{verification_id} 6–24 hr Blocks duplicate onboarding sends
Receipt email:{tenant}:receipt:{order_id} 30–90 days Typically sent once per order
Invoice email:{tenant}:invoice:{invoice_id}:{version} 90+ days Version supports regenerations

Common Mistakes

  • Using timestamps as the key source, so retries generate new keys
  • Keying only on user_id and accidentally blocking legitimate emails
  • Creating the key after sending, which does not protect crash windows
  • Not enforcing a unique index, allowing duplicates to slip through
  • Having multiple sending services generate different key formats for the same event

Troubleshooting

Duplicates still happen

Check these first:

  • Are retries reusing the exact same key?
  • Is a unique constraint actually enforced?
  • Do multiple senders generate different key formats?

Legitimate emails are blocked

Your key is likely too broad. Add event IDs or token IDs, and use TTL so future legitimate sends can still proceed.

Storage is growing too fast

Add TTL, archive old rows, and store only references or hashes instead of full payloads where possible.

FAQ

What are idempotency keys and how do they help in preventing duplicate emails?

An idempotency key uniquely represents one logical email event. If the same event is processed again due to retries, timeouts, or replays, your system detects the existing key and avoids sending another email.

Should I store idempotency keys in a database or Redis?

A database is better when you need strong durability and uniqueness guarantees. Redis can work for short TTL dedupe, but you must accept risk if it is not persisted. A queue alone is not enough for idempotency.

What TTL should I use for OTPs vs receipts/invoices?

Use short TTLs for OTP and password reset emails (minutes to an hour) and longer TTLs for receipts and invoices (weeks to months), based on how strictly you need to enforce “send once per event.”

Do idempotency keys replace retries and backoff?

No. Idempotency prevents duplicate sends for the same event. Retries and backoff handle transient failures. In production systems, you usually need both.

How do I design keys for multi-tenant SaaS to avoid collisions?

Include tenant or account identifiers, the event type, and a stable event ID such as a token, order, or invoice ID. Avoid timestamps alone because they change across retries.

What should I log to prove an email was sent once?

Log the idempotency key, timestamps, final status, and the provider response or message reference. This gives you one clear trail from business event to provider-accepted send.

Conclusion

Preventing duplicate emails is mostly about making normal failures safe. With stable idempotency keys, uniqueness enforcement, and key reuse across retries, duplicates become rare and easier to diagnose instead of becoming a repeated incident.

Pair idempotency with sensible retry backoff and strong log visibility so your team can answer “Was it accepted?” and “What happened next?” without guesswork. Use MailCub Documentation to guide implementation, review the Transactional Email page for sending and event-tracking context, and check MailCub Pricing if you are planning rollout capacity or environment upgrades.

Tags:
preventing duplicate emailsidempotency keystransactional email retriesmessage deduplicationretry backoffemail logswebhook eventsoutbox patterndead letter queuetransactional email

You Might Also Like