> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.awardspring.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.awardspring.com/_mcp/server.

# Idempotency

Any write accepts an `Idempotency-Key` header. Send one and a retry cannot create a duplicate.

```bash
curl -X POST https://api.awardspring.com/api/v1/gifts \
  -H "X-Spring-API-Key: YOUR_KEY" \
  -H "Idempotency-Key: 8f3b2a10-6c2e-4c9a-9f0d-2b1c3d4e5f60" \
  -H "Content-Type: application/json" \
  -d '{"donor_id":"4MZ8RT2PW","amount":250.00,"subject":"Annual fund","gift_date":"2026-04-03"}'
```

Generate a fresh key per logical operation. A UUID is ideal. The key must be printable ASCII, 1 to 255 characters.

## What happens on a retry

| Situation                                      | Result                                                     |
| ---------------------------------------------- | ---------------------------------------------------------- |
| Same key, same body, within 24 hours           | The original response is replayed. Nothing new is created. |
| Same key, **different** body                   | `422 idempotency_key_request_mismatch`                     |
| Same key while the first call is still running | `409 idempotency_key_in_flight`                            |
| Same key after 24 hours                        | Treated as a new request.                                  |

Only **2xx** responses are cached. A `4xx` or `5xx` leaves the key free, so you can correct the payload and retry with the same key.

## Why the mismatch error exists

`422 idempotency_key_request_mismatch` means you reused a key with a changed body. That is almost always a bug on the client — a retry loop that regenerates the payload, or a key derived from something less unique than the operation.

It is deliberately an error rather than a silent overwrite: the alternative is a caller believing they had updated a gift when the API had replayed an older response.

## When you need it most

The case that bites hardest is **creating a donor without an email address**. Email is optional, and uniqueness is what would otherwise catch a duplicate — so an email-less create has no natural protection. A network timeout on that call, followed by a naive retry, produces two donor records that are hard to tell apart afterwards.

Send an `Idempotency-Key` on every donor create, and treat it as mandatory when `email` is omitted.

## Interaction with dry runs

A [dry run](/dry-runs) never consumes a key. You can validate a request and then send it for real using the same `Idempotency-Key`.