# Idempotency

Source: <https://docs.gjold.com/v2/idempotency>

A request that moves money must happen once, even when the connection breaks before you have the answer. Send it with an `Idempotency-Key`, and send it again with the same key: we do it once, and answer each time with the first result.

When to retry, with which key, and what to do with a request that stays in doubt is in the note [Retries and idempotency](https://docs.gjold.com/notes/retries).

## Which requests

| Request | Takes a key |
|---|---|
| [Capture](https://docs.gjold.com/v2/transactions/capture), [refund](https://docs.gjold.com/v2/transactions/refund), [void](https://docs.gjold.com/v2/transactions/void) | Yes |
| [Charge subscriber](https://docs.gjold.com/v2/subscribers/charge) | Yes |
| [Create payment link](https://docs.gjold.com/v2/payment-links/create), [create](https://docs.gjold.com/v2/subscribers/create) and [renew](https://docs.gjold.com/v2/subscribers/renew) subscriber | No: a second link does no harm, as no one has its URL. |
| [Delete payment link](https://docs.gjold.com/v2/payment-links/delete), [delete subscriber](https://docs.gjold.com/v2/subscribers/delete) | No: deleting twice does no harm. |
| [Seq](https://docs.gjold.com/v2/synchronization/seq) | No: it changes nothing. |

## The key

```
Idempotency-Key: xM3h1STdPREZVWXVNeTBqNzkwUTZUaHdCRWp4ZldGWHdKWjBX
```

The key is any string of printable ASCII, which you make up for one request, and which no other request of yours has. A random value of 128 bits or more, in hex or base64, is a good key, as is a UUID. Spaces at its ends are trimmed. The whole request line and headers must fit in 4 KiB, which bounds its length.

Make the key when you decide to make the request, and store it with what the request is for, such as the order and the capture of it, before you send it. Then a retry, even one from another process after a crash, sends the same key.

## What we do with it

1. The first request with a key runs, and we store its whole answer, status, headers, and body, whether it succeeded or failed.
2. A request with the same key, sent while the first still runs, waits for it to finish.
3. A request with the same key, sent later, is not run: we answer with the stored answer, and the header `Idempotency-Status: OK`, which says that it is a replay.

We keep a key for 24 hours after its first request. A request with the key after that runs anew.

A failure is stored like a success. A key protects a request whose answer you never got; once you have an answer, even an error, the key has done its job, and a retry with it gets that answer again. To try a failed request again, make a new key.

We don't compare a replay with the first request: a key reused for another request gets the first request's answer, and the second request is never run. Use each key for one request only.

## Errors

| Error | Meaning |
|---|---|
| `400 invalid idempotency key` | The header is empty, or there is more than one `Idempotency-Key` header. |
| `500 idempotency error: {why}` | We could not store or read the answer for the key, and did not run the request. This answer, unlike others, is not stored under the key: try again, with the same key. |

## Without a key

A request without a key runs every time you send it. Two charges of a subscriber sent without a key, where one was meant, both go through. For a capture, a refund, or a void, the [`"index"`](https://docs.gjold.com/v2/transactions#index) stops the second, which fails with `409 index mismatch`, but then you can't tell from it whether the first went through, and have to synchronize to know. Always send a key.
