# Idempotency

The Bird API supports opt-in request deduplication via the `Idempotency-Key` header. This page is the wire contract; for the concepts — why, when, and retry strategy — see [Idempotency](/docs/guides/idempotency).

## Request header

| Header            | Constraints                                                                                                                                                       |
| ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Idempotency-Key` | Optional. Any non-empty string up to 255 characters; a UUID v4 is recommended. Honored on `POST`, `PATCH`, and `DELETE`; ignored on `GET`, `HEAD`, and `OPTIONS`. |

Omitting the header skips idempotency entirely — the request is processed normally with no deduplication. An empty key or one longer than 255 characters returns `400` with code `E01002 InvalidRequest`.

Keys are scoped to your workspace (two workspaces can use the same key string without collision) and retained for roughly **3 hours**. After the retention window, a reused key is processed as a fresh request.

```bash
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>Hi.</p>" }'
```

## Response semantics

| Scenario                                   | Response                                                                                 |
| ------------------------------------------ | ---------------------------------------------------------------------------------------- |
| First request with a key                   | Processed normally; the response (`2xx` or `4xx`) is cached against the key.             |
| Same key, identical request                | Original status and body replayed, with the `Idempotency-Replay: true` response header.  |
| Same key, **different** request            | `409` — `E01005 IdempotencyKeyReuse`. Generate a new key for the new request.            |
| Same key, original request still in flight | `409` — `E01004 RequestInProgress`. The lock expires within ~30 seconds; wait and retry. |
| Original request returned `5xx`            | Not cached — the key unlocks and the retry is processed fresh.                           |

A replayed response is byte-for-byte the original — same status code, same body — distinguished only by the extra header:

```http
HTTP/1.1 202 Accepted
Idempotency-Replay: true
```

"Identical request" covers the method, endpoint, path and query parameters, and the raw request body — any difference, including whitespace, makes it a different request and triggers `E01005`. Both `409` errors come wrapped in the standard [error envelope](/docs/api/errors).

`5xx` responses are deliberately never cached: a server error means Bird does not know whether the request took effect, and replaying it would permanently block the retry from succeeding. Retry `5xx` and timeouts with the **same** key.

## SDK behavior

The official [SDKs](/docs/sdks/concepts) attach an auto-generated UUID `Idempotency-Key` to every mutating request, generated once per logical call and reused across all retry attempts of that call. You can supply your own key per call (`idempotencyKey` in TypeScript, `option.WithIdempotencyKey` in Go, `idempotency_key` in Python) when one logical operation spans multiple SDK calls. To detect a replay, read the `Idempotency-Replay` response header through each SDK's transport-metadata accessor: `.withResponse()` in TypeScript, `option.WithResponseInto` in Go, and `with_raw_response` in Python.

## Related

- [Idempotency concepts](/docs/guides/idempotency) — retry strategy, key design, and the durable email-send guarantee
- [Errors](/docs/api/errors) — the envelope wrapping `E01004` and `E01005`
- [Email messages](/docs/api/reference/create-email-message) — the send endpoint, the most common place to use a key
- [SDK concepts](/docs/sdks/concepts) — automatic key generation and retries