> ## Documentation Index
> Fetch the complete documentation index at: https://jetemail.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Idempotency Keys

> Make SMTP submission retries safe with X-Idempotency-Key

# Idempotency Keys (SMTP)

Network glitches, timeouts, and connection drops during DATA can leave your sender unsure whether a message was actually accepted. Adding an `X-Idempotency-Key` MIME header lets you retry safely: a repeat with the same key and body is recognized as a duplicate, the original `250` reply is replayed, and **no second message is queued or relayed**.

<Warning>
  Idempotency is currently scoped **per region**. A retry that lands on a different region than the original submission will not see the prior key and may produce a duplicate send. We are working on a global key store that will apply idempotency across all regions at once.
</Warning>

## How it works

1. Pick a unique key per logical send (a UUID v4 is a safe default).
2. Add it as an `X-Idempotency-Key` MIME header inside the DATA payload.
3. If the connection breaks before you read the reply, retry the SMTP transaction with the **same key and same body**. The server replays the original `250` response without sending a second email.
4. If the same key is reused with a **different body**, the submission is rejected.

The `X-Idempotency-Key` header is **stripped before delivery**, so it never reaches recipients or appears on the relay leg.

## Header reference

| Header              | Required | Notes                                   |
| ------------------- | -------- | --------------------------------------- |
| `X-Idempotency-Key` | No       | Opaque string, **1 to 256 characters**. |

Keys are scoped per user. Pick something globally unique per logical send.

## Example SMTP transaction

```
MAIL FROM:<alice@example.com>
RCPT TO:<bob@example.org>
DATA
From: alice@example.com
To: bob@example.org
Subject: Hi
X-Idempotency-Key: 4f8a5d-customer-order-12345
Message-ID: <abc123@example.com>

(body)
.
```

## SMTP reply codes

| Outcome                   | Reply                                                       | Notes                                                             |
| ------------------------- | ----------------------------------------------------------- | ----------------------------------------------------------------- |
| First time, accepted      | `250 OK Message queued as <id>`                             | Cached for 24 hours.                                              |
| Retry, same body          | `250 OK Message already queued as <id> (idempotent replay)` | Same `<id>` as the original. No new message is queued or relayed. |
| Retry, different body     | `554 5.5.0 X-Idempotency-Key reused with different body`    | Use a new key.                                                    |
| Concurrent duplicate      | `451 4.5.0 Idempotency in flight; retry`                    | The original is still processing. Retry shortly.                  |
| Key longer than 256 chars | `554 5.5.0 Invalid X-Idempotency-Key (length)`              | Permanent.                                                        |
| Empty or missing header   | Runs normally                                               | No caching applied.                                               |

A replay keeps the `250` status code so standard SMTP clients still treat the response as success, with the original queue id in the same position as on the first reply.

## Retry over the same transport

JetEmail also accepts an `Idempotency-Key` header on the HTTP API. Always retry over the **same transport** you originally submitted on. A cross-transport retry (HTTP after SMTP, or vice versa) will not match and will produce a new send.

## Best practices

* **Generate the key before the first attempt**, not on retry, so retries can reuse it.
* **Persist the key** alongside the work item that triggered the send (e.g. an order or job row) so the same retry path always reuses the same key.
* **One key per logical send.** Do not reuse a key for unrelated emails.
* **Pair with [scheduled sends](/outbound/scheduled-sends)** when the SMTP submission that schedules a message may itself be retried.
