Idempotency
Use Idempotency-Key when a failed connection leaves you unsure whether your request succeeded. The guarantee is exactly-once acceptance: retries with the same key and identical request must resolve to one accepted operation within the 3-hour retention window, even if you lost the original response.
How it works
Idempotency is opt-in. Add an Idempotency-Key header to a supported POST, PATCH, PUT, or DELETE request. Requests without it are processed normally with no deduplication. GET requests ignore the header.
On the customer API, workspace- and organization-scoped mutations support the response replay described below. User-only and unscoped unauthenticated operations and streams bypass it. Operations with a separate replay contract define their behavior in their reference page. For example, Create a voice call retains the original acceptance snapshot for matching retries when you supply a key.
The SDKs generate a key for each mutating call and reuse it for automatic retries, including call creation. You do not need to supply one for automatic SDK retries. Supply your own when one intended operation spans separate SDK calls, such as a retry after restarting your application. These examples show that case.
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 nonempty 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.
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 can be processed as a fresh request. Keep the original operation unresolved if its outcome is unknown after expiry, and reconcile it before sending again.
Replays
A matching retry within three hours must resolve to the original operation. When its response is retained, the API returns the same status code and body without accepting another operation. Replayed responses carry one extra header so you can tell them apart from fresh processing:
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 as completed responses. The exactly-once acceptance guarantee also covers retries after these errors.
Failure modes
| Scenario | Response |
|---|---|
| Same key, same request, original response retained within retention | 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. Wait briefly and retry with the same key and request. See Errors for the envelope these come wrapped in.
What's not cached
5xx responses are not retained as completed responses. Retry within three hours with bounded backoff, the same key, and the identical request. The exactly-once acceptance guarantee applies even when the original response was lost.
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 network errors, timeouts, and
5xxwithin three hours with bounded exponential backoff, preserving the key and identical request. - Keep an unknown outcome unresolved when retries are exhausted or retention expires. Reconcile before submitting another operation with either the old key or a new one.
- For message sends, check the message status to confirm delivery after acceptance.
- Treat
409 IdempotencyKeyReuseas a bug in your key generation. Do not retry it. - Keys are optional on mutations. Use one when you need retry protection; omit it on
GETrequests.
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.