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 any POST, PATCH, or DELETE request; requests without the header are processed normally with no deduplication. GET requests are naturally idempotent and ignore the header.
Codevoorbeeld
curl -X POST https://us1.platform.bird.com/v1/email/messages \
-H "Authorization: Bearer bk_us1_..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000" \
-d '{
"from": "hello@example.com",
"to": ["user@example.com"],
"subject": "Welcome!",
"html": "<p>Thanks for signing up.</p>"
}'A key is any non-empty string up to 255 characters. A UUID v4 is the recommended format; a deterministic key derived from your own entities (e.g. welcome-user/usr_abc123) also works well when you want retries across process restarts to share a key. The Bird SDKs generate a UUID key automatically for every mutating request, so SDK users get safe retries without doing anything.
Keys are scoped to your workspace — two workspaces can use the same key string without colliding — and retained for roughly 3 hours. A retry after the retention window is processed as a fresh request, so the window comfortably covers typical retry schedules (seconds to an hour) but is not a permanent dedupe log.
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. Both 2xx and 4xx responses are cached and replayed: if your first attempt failed validation, retrying the identical request returns the identical error rather than burning another attempt.
Replayed responses carry one extra header so you can tell them apart from fresh processing:
Codevoorbeeld
HTTP/1.1 202 Accepted
Idempotency-Replay: trueFailure modes
| Scenario | Response |
|---|---|
| Same key, same request | 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 |
| Empty key, or key longer than 255 characters | 400 — E01002 InvalidRequest |
Reusing a 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, so even a whitespace change in the body counts as a different request.
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 automatically within about 30 seconds, so wait briefly and retry. See Errors for the error envelope these come wrapped in.
What's not cached
5xx responses are never cached. A server error means Bird doesn't know whether the request took effect, and caching it would permanently block the retry from ever succeeding. Instead the key unlocks, and your retry is processed fresh. This is exactly the case idempotency exists for: retry 5xx responses and timeouts with the same key, and you'll either get the original successful response (if the first attempt actually completed) or a clean new attempt.
Email sends get an extra durable guarantee. For POST /v1/email/messages and the batch endpoint, acceptance is additionally deduplicated in durable storage, independent of the response cache. Even if the cache misses — a lock expired, a response was too large to cache — a retry with the same key replays the same accepted email IDs rather than accepting the send twice. Your retries never produce duplicate emails. See Sending email.
Practical guidance
- Generate one key per logical operation, not per HTTP attempt — the whole point is that retries of the same operation share the key.
- 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, not something to retry.
- Don't bother with keys on GET requests or on operations that are naturally idempotent in your domain — the mechanism is there for the cases where a duplicate would hurt.
Related
- 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