Reference
Idempotency
Retry email sends safely with the Idempotency-Key header. Which endpoints support it, key format and scope, the 24-hour replay window and its errors.
Network errors and timeouts leave you unsure whether a send went through. An idempotency key makes the send safe to retry: if Emailit already accepted a request with the same key, it returns the original response instead of sending the email again. This page is the reference for the Idempotency-Key header. For a walkthrough, see Idempotent sends.
Supported endpoints
| Endpoint | |
|---|---|
POST /emails |
Send an email |
POST /emails/{id}/forward |
Forward an email |
Other endpoints ignore the header. Creating a domain, API key, audience or contact is already safe to retry: a second request with the same name or email returns 409 and the existing object instead of creating a duplicate.
Send a key
Add an Idempotency-Key header with a value that’s unique to the email you’re sending, such as a UUID or an ID from your own system:
curl https://api.emailit.com/v2/emails \
-H "Authorization: Bearer $EMAILIT_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: order-1042-confirmation" \
-d '{
"from": "Acme <orders@acme.com>",
"to": "ada@example.com",
"subject": "Your order #1042",
"html": "<p>Thanks for your order.</p>"
}'const response = await fetch('https://api.emailit.com/v2/emails', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.EMAILIT_API_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': `order-${order.id}-confirmation`,
},
body: JSON.stringify({
from: 'Acme <orders@acme.com>',
to: order.email,
subject: `Your order #${order.id}`,
html: '<p>Thanks for your order.</p>',
}),
});import os
import requests
response = requests.post(
"https://api.emailit.com/v2/emails",
headers={
"Authorization": f"Bearer {os.environ['EMAILIT_API_KEY']}",
"Idempotency-Key": f"order-{order['id']}-confirmation",
},
json={
"from": "Acme <orders@acme.com>",
"to": order["email"],
"subject": f"Your order #{order['id']}",
"html": "<p>Thanks for your order.</p>",
},
timeout=30,
)Generate the key once per email, before the first attempt, and send the same key on every retry of that email.
How keys work
| Rule | Details |
|---|---|
| Format | 1 to 256 characters. Letters, digits, hyphens (-) and underscores (_) only. |
| Scope | Per workspace. Keys from different workspaces never collide, but both endpoints share one namespace, so don’t reuse a send key for a forward. |
| Lifetime | The response to a successful request is kept for 24 hours after it completes. |
| Replays | A request with a stored key returns the stored response with status 200. No email is sent, no credits are used, and the sending limits aren’t counted. |
| Matching | Only the key is compared, not the request body. A different request with a used key returns the first response. |
| Failures | If a request fails with any error, nothing is stored and the key is released, so you can fix the request and retry with the same key. |
| Concurrency | While a request with a key is still running, another request with the same key returns 409. The lock is released when the first request finishes, and it expires after 15 minutes at most. |
A replayed request still goes through authentication and the per-second and daily sending limits check, so it can return 401 or 429. Forwards also count against the hourly forward limit, even when the response is replayed.
Errors
{
"error": "Invalid Idempotency-Key"
}{
"error": "Idempotency key in progress",
"message": "A request with this Idempotency-Key is already being processed. Retry shortly with the same key."
}{
"error": "Idempotency unavailable",
"message": "Unable to process Idempotency-Key right now. Retry the request with the same key."
}| Status | error |
Cause | What to do |
|---|---|---|---|
400 |
Invalid Idempotency-Key |
The key is empty, longer than 256 characters, or has other characters than letters, digits, - and _. |
Use a UUID or another safe value. |
409 |
Idempotency key in progress |
A request with the same key is still being processed. | Wait a second and retry with the same key. You’ll get the stored response once the first request finishes. |
503 |
Idempotency unavailable |
Emailit can’t check the key right now. The request wasn’t processed. | Retry with the same key after a short delay. |
Emailit never sends a request without its idempotency check: if the key can’t be checked, the request fails with 503 rather than risk a duplicate.
Choose good keys
- Derive the key from the thing you’re notifying about, such as
order-1042-confirmationorpassword-reset-<token-id>, so a retry from another worker or after a restart uses the same key. - Use a fresh UUID only when the email has no natural ID, and store it with the job before the first attempt.
- Don’t reuse a key for a different email within 24 hours. You’d get the first email’s response and the second email wouldn’t be sent.