> ## Documentation Index
> Fetch the complete documentation index at: https://docs.reap.global/llms.txt
> Use this file to discover all available pages before exploring further.

# Idempotency

> Safely retry POST requests with the Idempotency-Key header.

Send a unique `Idempotency-Key` header on any POST request and you can safely retry on failure. The server caches the first response (status code and body) and replays it on retries. Keys expire after 24 hours.

```bash theme={null}
curl -X POST https://api.reap.global/cards \
  -H "Authorization: Bearer $REAP_API_KEY" \
  -H "Reap-Version: 2026-01-01" \
  -H "Idempotency-Key: 7e7f1a90-3e0e-4a7e-bd2c-9b3a3c2d8e1f" \
  -H "Content-Type: application/json" \
  -d '{ "userId": "...", "accountId": "...", "type": "VIRTUAL" }'
```

Generate one key per logical operation. Use a UUIDv4 or any high-entropy string up to 255 characters. Keep the same key across every retry - never generate a new key on each attempt.

***

## Behavior

| Situation                         | Response                                                                           |
| --------------------------------- | ---------------------------------------------------------------------------------- |
| First request                     | Executes and caches the response.                                                  |
| Retry, same key, same body        | Returns the cached response. Includes `Idempotent-Replayed: true` response header. |
| Retry, same key, different body   | `400 IDEMPOTENT_PARAMETER_MISMATCH` - use a new key for a different request.       |
| Two concurrent requests, same key | `409 IDEMPOTENCY_REQUEST_IN_PROGRESS` - retry with backoff.                        |
| Key reused after 24 hours         | Treated as a fresh request.                                                        |

All outcomes are cached, including `4xx` and `5xx`. If the first attempt returned a business error, retries replay that error. Fix the input and use a new key to try again.

`401`, `422`, and `429` responses are not cached and are safe to retry with the same key.

***

## Retry pattern

```ts theme={null}
const idempotencyKey = crypto.randomUUID(); // generate once, reuse on retries

async function createCardWithRetry(body) {
  for (let attempt = 0; attempt < 5; attempt++) {
    try {
      const res = await fetch('https://prod.api.reap.global/cards', {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${apiKey}`,
          'Reap-Version': '2026-01-01',
          'Idempotency-Key': idempotencyKey,
          'Content-Type': 'application/json',
        },
        body: JSON.stringify(body),
      });
      if (res.status === 429 || res.status >= 500) {
        await sleep(2 ** attempt * 1000);
        continue;
      }
      return await res.json();
    } catch {
      await sleep(2 ** attempt * 1000); // network error: safe to retry same key
    }
  }
}
```

***

## Recommendations

* **Retry on network errors and `429`** with the same key. These are the only cases where the original request may not have reached the server.
* **Use a new key after a `4xx` business error.** The cached error replays on retries; only a new key triggers a fresh attempt.
* **Persist the key before sending** if you need guaranteed retry safety after a client crash.
