//emails-for-saas providers ↗
guide

4 API Retry Mistakes That Cause Duplicate Transactional Emails

Transactional email APIs are designed to be reliable, but reliability breaks when retry logic is implemented incorrectly on the client side. A 200 response that arrives late, a timeout that hides a successful send, or a network hiccup between your server and the ESP can all trigger a retry that delivers a second copy of a password reset or a receipt to a customer. These four mistakes explain many duplicate-send incidents.

last updated 2026-07-12 4 sections
section 01

Retrying on Timeout Without an Idempotency Key

When a send call times out, the request may have already reached the ESP and been processed. Without an idempotency key, a retry is treated as a new send. Many transactional email APIs accept an idempotency key header or parameter. Pass a stable key derived from the message context, such as a hash of user ID plus event type plus a short timestamp window, and the API returns the original response instead of sending again.

section 02

Treating 5xx Responses as Safe to Retry Immediately

A 500 or 503 from an ESP does not always mean the request failed. Some server errors occur after the message has been queued internally. Retrying immediately on any 5xx response without checking whether the first send landed creates duplicates. Implement exponential backoff starting at two seconds, and check your ESP delivery log before retrying any send that returned a 5xx status.

section 03

Not Deduplicating at the Application Layer

Idempotency keys are one layer of protection. A second layer is tracking send attempts in your own database before making the API call. Store a record of sent transactional emails keyed to the triggering event. Before any send, query that table. If a record exists and is recent, skip the send. This approach survives network failures between your app and the ESP and works regardless of whether the ESP supports idempotency keys.

section 04

Retrying Across Multiple Worker Processes Without Coordination

In distributed systems, the same job can be picked up by two workers if a lock expires before the first worker confirms completion. If the job includes a transactional send, both workers will attempt the send. Use distributed locks or exactly-once job semantics at the queue level, not just at the API call level. Configure lock expiry to exceed your maximum ESP response time with margin, so a slow but successful response does not release the lock early.

reading this as SaaS product teams

A SaaS product sends on behalf of its customers, so the deciding factors are tenant isolation, whether suppressions can be scoped rather than global, and whether support can answer why one specific account did not receive one specific email. Lifecycle automation and transactional sending usually need to coexist.

Applied to 4 api retry mistakes that cause duplicate transactional emails, that means weighing multi-tenant support, event stream, the suppressions API, and operating track record ahead of the rest, against trial expiry notices, dunning, onboarding sequences, and per-tenant transactional mail.

related pages