Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.sendmux.ai/llms.txt

Use this file to discover all available pages before exploring further.

Network blips, client timeouts, and middleware retries can cause the same request to reach Sendmux more than once. The Idempotency-Key request header lets you mark a request so a retry under the same key returns the original response instead of re-running the operation.

How it works

1

Send the header on the original request

Add Idempotency-Key: <your-key> to any mutating request. Sendmux records the key, the response, and a fingerprint of the request body.
2

Retry under the same key

If the same key arrives again within 24 hours with the same body, Sendmux returns the cached response verbatim — same status, same headers, same body. The operation does not run twice.
3

Mismatched body returns 409

If the same key arrives with a different body (or while the original request is still in flight), Sendmux returns 409 idempotency_conflict. The cached entry is left alone.

Sending the header

curl -X POST https://smtp.sendmux.ai/api/v1/emails/send \
  -H "Authorization: Bearer smx_your_key_here" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-confirmation-12345" \
  -d '{
    "from": { "email": "hello@yourdomain.com" },
    "to":   { "email": "user@example.com" },
    "subject":   "Order confirmation #12345",
    "html_body": "<p>Your order has been confirmed.</p>"
  }'
The key is opaque to Sendmux — pick whatever stably identifies the logical operation on your side.

Coverage

The header is honoured on every mutating endpoint:
APIEndpointEffect of replay
Sending APIPOST /emails/sendReturns the original message_id and status.
Sending APIPOST /emails/send/batchReturns the original per-message results array.
Management APIPOST /domainsReturns the original 201 + Location header.
Management APIPOST /mailboxesReturns the original 201 + Location header.
Management APIPOST /mailboxes/{public_id}/keysReturns the original 201 + Location header (and the secret field).
Management APIPOST /webhooksReturns the original 201 + Location header (and the secret field).
Management APIPOST /webhooks/{public_id}/rotate-secretReturns the original new secret — the secret is not rotated again.
Management APIPOST /webhooks/{public_id}/testReturns the original event_id — no second test event is published.
GET endpoints don’t accept the header (they have no side effects); PATCH, PUT, and DELETE use If-Match for optimistic concurrency instead.

Replay vs conflict

SituationHTTP statusBody
Same key, same body, original request finishedThe original status (200, 201, 422, …)The original response body, byte-identical.
Same key, original request still being processed409{ ok: false, error: { code: "idempotency_conflict", message: "This request is already being processed." } }
Same key, different body409{ ok: false, error: { code: "idempotency_conflict", message: "Idempotency-Key was reused with a different request body." } }
A 409 idempotency_conflict while the original is in flight is transient — wait briefly and retry under the same key to receive the completed response.

Key format

  • Length: 1–255 characters. Whitespace is trimmed; an empty or oversize key is silently ignored.
  • Charset: opaque to Sendmux. URL-safe identifiers travel cleanly through proxies and logs (a-zA-Z0-9_-), but no character class is rejected server-side.
  • Stability: the key must be deterministic per logical operation. Random UUIDs minted at retry time defeat the purpose — they make every retry look like a brand-new request. Derive the key from the action you’re performing: order-confirmation-{order_id}, welcome-{user_id}, invoice-{invoice_id}-{period}.

Scope

Cached entries are scoped to (team, endpoint, key):
  • Two teams may use the same key without colliding.
  • The same key reused across two different endpoints (e.g. POST /emails/send and POST /webhooks) does not collide — each endpoint maintains its own namespace.
  • Keys expire 24 hours after first use. After expiry the same key can be reused and a fresh request is processed.

Batch sends

POST /emails/send/batch honours one Idempotency-Key header for the whole batch. Replay returns the original results array verbatim — the batch is not re-processed and individual messages are not re-sent. If you need per-message idempotency inside a batch, split the batch into individual POST /emails/send calls each with their own Idempotency-Key.

Limitations

  • Best-effort. If our cache layer is briefly unavailable, the request is processed without idempotency protection (fail-open) rather than rejected. Critical retries should still be safe to send.
  • Body field is rejected. Earlier versions accepted idempotency_key inside the JSON body. That form now returns 422 validation_error — migrate to the Idempotency-Key HTTP header.