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

# Idempotency

> Retry requests safely after timeouts and network errors without creating duplicate payouts.

The API supports idempotency for safely retrying requests without performing the same operation twice. When a request that moves money times out, you can't tell whether Infinity received it; an idempotency key lets you send it again and get the original result back instead of a second payout.

```mermaid theme={null}
sequenceDiagram
  participant You as Your server
  participant API as Infinity API
  You->>API: POST /payouts (Idempotency-Key: inv-1042)
  Note over API: Batch and payout created
  API--xYou: Response lost (timeout)
  You->>API: POST /payouts (same key, same body)
  API-->>You: 200, the same batch (nothing new created)
```

## Sending a key

Pass the key in the `Idempotency-Key` header:

```bash theme={null}
curl "$BASE/payouts" \
  -H "Authorization: Bearer $INFINITY_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: inv-1042" \
  -d '{
    "wallet_id": "wallet_7t4m9k2qx8d3hn5r",
    "items": [ { "payee_id": "payee_c8pnahcrxqzd3z8d", "amount": "500.00", "currency": "eur" } ]
  }'
```

| Endpoint | `Idempotency-Key` |
| - | - |
| `POST /payouts`, `POST /test/deposits` | Required. Requests without it fail with `400 missing_parameter`. |
| `POST /payees`, `POST /payees/{id}/addresses`, `POST /webhook-endpoints` | Optional, and recommended if your code retries automatically. |
| Updates, cancels, quotes, `GET` and `DELETE` | Not used. These are either idempotent by definition or create nothing. |

Keys are up to 255 characters and scoped to your account, so two accounts can use the same key without colliding.

## How Infinity processes a key

When a request with a new key arrives, Infinity records the key with a fingerprint of the request body, performs the work, and saves the response's status code and body against the key for 24 hours. What happens to a later request with the same key depends on its state:

| The key was used… | Result |
| - | - |
| With the same body, and the first request has finished | The saved response is returned, with its original status code. Nothing is created. |
| With a **different** body | `409 idempotency_key_reused`. Nothing is created. |
| While the first request is still being processed | `409 idempotency_key_in_use`. Retry after a short delay to receive the saved response. |
| More than 24 hours ago | The key has expired and the request is processed as new. |

Responses are saved for client errors as well as successes. A request that failed with `400 insufficient_funds` returns the same `400` on retry, even after you've funded the wallet, because the retry is a replay, not a new attempt. Server errors are the exception: if the first attempt fails with a `5xx`, its record is discarded so that a retry with the same key runs the work again. A `5xx` attempt never leaves partial state behind.

## Choosing keys

Generate the key once per intended operation, before the first attempt, and reuse it for every retry of that operation.

* **A business identifier** works well when one exists: `inv-1042` for paying invoice 1042, `payroll-2026-10` for October's payroll run. It survives process restarts, so a retry from a different worker still deduplicates.
* **A V4 UUID** works when there's no natural identifier. Persist it with the pending operation so retries reuse it.
* Don't put personal data, such as email addresses, in keys.

Use a new key only when you deliberately start a new attempt after a definitive error, for example `inv-1042-attempt-2` after fixing the request that returned `400`.

> [!WARNING]
>
> Never generate a new key to retry a timeout or network error. If the first request succeeded, the new key creates a second set of payouts.

## Retry strategy

1. Retry on timeouts, connection errors, `429`, `5xx` and `409 idempotency_key_in_use`, with the same key and the same body.
2. Back off exponentially with jitter (for example 1 s, 2 s, 4 s, 8 s), honouring `Retry-After` when it's present.
3. Stop on any `2xx`, and on any other `4xx`; those are final answers.
4. If you exhaust your retries, look the payout up with `GET /payouts` before you consider sending it again under a new key.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.