Idempotency & retries
Networks fail mid-flight. An idempotency key lets you retry a send without fear of delivering it twice — Drin recognizes the repeat and returns the original result instead of sending again.
Send operations are POSTs, which aren't safe to blind-retry: a request can succeed on the server even though the response never reaches you. An idempotency key closes that gap. Attach one and Drin records the outcome under it; a retry with the same key replays that outcome rather than performing a second send.
01 The Idempotency-Key header
Send a unique Idempotency-Key header on any send. It works on POST /v1/emails, POST /v1/emails/batch, and POST /v1/emails/{id}/reply. The value is yours to choose — a UUID or, better, a business identifier tied to the operation.
curl https://api.drin.run/v1/emails \
-H "Authorization: Bearer $DRIN_API_KEY" \
-H "Idempotency-Key: welcome-user-4815" \
-H "Content-Type: application/json" \
-d '{
"from": { "email": "hello@acme.com" },
"to": [{ "email": "customer@example.com" }],
"subject": "Welcome aboard",
"html": "<p>You\u2019re in.</p>"
}'idempotencyKey in the per-call options (the second argument to send, sendBatch, and reply) and the client sets the Idempotency-Key header on the request.02 How it behaves
- 1
First request with a key
The send is processed normally and the result — the messageidand status — is stored under the key. - 2
Retry with the same key and body
Drin returns the original result without sending again. Your caller sees the sameidas if the first response had arrived. - 3
Reuse the key with a different body
Drin refuses the request with a409 conflict— a reused key must describe the same operation. Use a fresh key for a different message.
{
"error": {
"type": "conflict",
"message": "Idempotency-Key was reused with a different request body"
}
}03 The 24-hour window, per project
A key is remembered for 24 hours, scoped to the sending project it was used under. Two consequences:
- Retry inside 24h and the original result is replayed. After the window expires, the same key is treated as new and the message can send again — so keep retries well within a day.
- Keys don't collide across projects. The same string used by two different projects is two independent keys. With an account-wide key, the project is the one you name via
X-Drin-Product(or the SDK'ssender) — see Authentication.
04 Choosing a key
The key must be stable across retries of the same operation and unique across different operations. Derive it from the work you're doing, not from the moment you do it — generating a new random value on each attempt defeats the purpose.
import { randomUUID } from "node:crypto";
// Derive ONE key per logical operation and reuse it across retries.
// A natural business id is ideal — here, "welcome the user who just signed up".
const key = `welcome:${userId}`;
// Or a random UUID persisted with the job so a retry sends the SAME key:
// const key = job.idempotencyKey ?? (job.idempotencyKey = randomUUID());
await drin.emails.send(payload, { idempotencyKey: key });welcome:<userId>, receipt:<orderId>, reset:<tokenId>. Two requests for the same event share a key and dedupe; two genuinely different emails never collide.// Replies accept an idempotency key too.
await drin.emails.reply(
inboundMessageId,
{ html: "<p>Thanks — we're on it.</p>" },
{ idempotencyKey: `reply:${inboundMessageId}` },
);05 How the SDK retries
The TypeScript SDK retries transient failures (HTTP 429, 5xx, and network errors) with exponential backoff up to maxRetries — 2 by default. Its retry rule is deliberately conservative:
- GET and other reads are always safe to retry, so they are.
- POST is retried only when it carries an idempotency key. A
POSTwithout one is sent exactly once — on a transient failure the SDK surfaces the error rather than risk a duplicate send.
idempotencyKey. Otherwise a single network blip turns into a failed call you have to handle yourself — and retrying it by hand without a key risks double-sending.sendBatch request. A replay returns the original results array rather than re-sending every item. See Batch & scheduled.