Idempotency
Networks fail at the worst moments: you POST a send, the connection drops, and now you don't know whether the email went out. Idempotency lets you retry that request safely. Send the same Idempotency-Key header again and Bird replays the original response instead of processing the request a second time.
How it works
Idempotency is opt-in. Add an Idempotency-Key header to a supported POST, PATCH, PUT, or DELETE request; requests without the header are processed normally with no deduplication. GET requests are naturally idempotent and ignore the header.
On the customer API, workspace- and organization-scoped mutations support this response replay. User-only and unauthenticated operations, streams, and operations with a separate replay contract bypass it. Sending a key to an operation that bypasses replay does not add deduplication protection.
The SDKs send a key on every mutating request already; each one also lets you set your own, which is what these tabs show.
await bird.email.send(
{
from: "hello@yourdomain.com",
to: ["delivered@messagebird.dev"],
subject: "Welcome!",
html: "<p>Thanks for signing up.</p>",
},
{ idempotencyKey: "welcome-user/usr_abc123" },
);client.email.send(
from_="hello@yourdomain.com",
to=["delivered@messagebird.dev"],
subject="Welcome!",
html="<p>Thanks for signing up.</p>",
options={"idempotency_key": "welcome-user/usr_abc123"},
)_, err := client.Email.Send(context.Background(), bird.EmailSendParams{
From: "hello@yourdomain.com",
To: []string{"delivered@messagebird.dev"},
Subject: "Welcome!",
HTML: "<p>Thanks for signing up.</p>",
}, option.WithIdempotencyKey("welcome-user/usr_abc123"))$bird->email->send(
from: 'hello@yourdomain.com',
to: ['delivered@messagebird.dev'],
subject: 'Welcome!',
html: '<p>Thanks for signing up.</p>',
options: new RequestOptions(idempotencyKey: 'welcome-user/usr_abc123'),
);curl -X POST https://us1.platform.bird.com/v1/email/messages \
-H "Authorization: Bearer $BIRD_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: welcome-user/usr_abc123" \
-d '{
"from": "hello@yourdomain.com",
"to": ["delivered@messagebird.dev"],
"subject": "Welcome!",
"html": "<p>Thanks for signing up.</p>"
}'A key is any string up to 255 characters. An empty header value skips deduplication. The recommended format is a deterministic key derived from your own entities, <event-type>/<entity-id> (for example welcome-user/usr_abc123), so retries across process restarts share a key; a random UUID per logical operation works too. The Bird SDKs generate a UUID key automatically for every mutating request and reuse it across their internal retries, so SDK users get safe retries without doing anything.
Keys are scoped to your workspace, or to your organization on organization-level endpoints. A completed response is kept for 3 hours; a retry after that window is processed as a fresh request. The window covers typical retry schedules. No deduplication record remains after it expires.
Replays
When Bird sees a key it has already completed, it returns the cached response, same status code, same body, without re-running the request. Replayed responses carry one extra header so you can tell them apart from fresh processing:
Code example
HTTP/1.1 202 Accepted
Idempotency-Replay: trueRetained responses can include 4xx rejections. Use a new key when correcting a request: if its rejection was retained, an unchanged retry replays it, and a changed request returns 409 E01005 IdempotencyKeyReuse. 5xx responses are not retained, so retry them with the same key and request.
Failure modes
| Scenario | Response |
|---|---|
| Same key, same request, original completed | Cached response replayed with Idempotency-Replay: true |
| Same key, different request body or endpoint | 409, E01005 IdempotencyKeyReuse |
| Same key, original request still in flight | 409, E01004 RequestInProgress |
| Key longer than 255 characters on an endpoint declaring the header | 422, E01001 ValidationError |
| Idempotency protection unavailable before execution | 503, E01033 IdempotencyUnavailable; this attempt is not executed |
Reusing a completed key with a different request is treated as a client bug: Bird returns 409 immediately rather than silently handing you a response that doesn't match what you sent. Generate a new key for the new request. The comparison covers the method, endpoint, path and query parameters, and the raw request body, including JSON whitespace. Multipart uploads compare part names, filenames, and contents; boundaries and part ordering do not affect replay.
RequestInProgress means a concurrent request with the same key hasn't finished yet, typically an aggressive client-side timeout retrying while the first attempt is still processing. The in-flight lock expires within 30 seconds, so wait briefly and retry. See Errors for the envelope these come wrapped in.
What's not cached
5xx responses are never cached. The key unlocks and Bird can process a retry as a new attempt. Retry 5xx responses and timeouts with backoff using the same key and request. An operation can take effect before its response is retained; if that response is lost, or the in-flight lock expires, a retry can execute the operation again.
If idempotency protection is unavailable before execution, the API returns 503 E01033 IdempotencyUnavailable without executing this attempt. Keep the key on every retry. This error does not describe the outcome of an earlier attempt with the same key.
Practical guidance
- Generate one key per logical operation and reuse it for every HTTP attempt of that operation.
- Retry on network errors, timeouts, and 5xx with exponential backoff, reusing the same key each time.
- Treat 409 IdempotencyKeyReuse as a bug in your key generation. Do not retry it.
- Skip keys on GET requests and on operations that are naturally idempotent in your domain; the mechanism is there for the cases where a duplicate would hurt.
Next steps
- Idempotency API reference: header and response-header schemas
- SDK concepts: automatic key generation and retry behavior in the SDKs
- Errors: the error envelope and code catalog
- Sending email: send and batch endpoints
Related resources
Continue with the documentation, guides and examples for this topic. Resources are in English.
Watch the guideOrder confirmation emailsExplore the capabilityOrder confirmation emailsFollow the learning pathBuild your first integration
Try the practice and get an implementation brief