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

# Idempotency

> Retry POST requests safely with an Idempotency-Key header.

Networks fail. If a `POST` times out you can't tell whether it ran. Send an `Idempotency-Key` header and you can retry it without starting a second top-up or paying an invoice twice.

```bash theme={null}
curl https://partners.chargeexpressltd.com/api/public/v1/wallet/fundings \
  -H "Authorization: Bearer $CHARGEEXPRESS_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 0f8b6c1e-4d2a-4a8e-9b7f-3c5d1e2f4a6b" \
  -d '{ "amount": 500 }'
```

The key is any string up to 255 characters that is unique to one logical operation. A UUID v4 works well, as does an id from your own system, such as `topup-2026-10-week-42`.

## Which requests use it

| Endpoint | `Idempotency-Key` |
| - | - |
| `POST /wallet/fundings` | **Required** |
| `POST /invoices/usage/pay` | **Required** |
| `POST /drivers`, `POST /vehicles` | Optional |
| `POST /webhook_endpoints`, `POST /webhook_endpoints/{endpointId}/rotate_secret` | Optional |
| `POST /events/{eventId}/redeliver` | Optional |
| `POST /drivers/{driverId}/photo`, `POST /vehicles/{vehicleId}/image`, `POST /webhook_endpoints/{endpointId}/test` | Ignored |
| `GET`, `PATCH`, `PUT`, `DELETE` | Ignored |

Without the header, a required endpoint returns `400 idempotency_key_required`. `PUT` replaces the whole set it targets, so repeating it is already safe.

## How it works

Keys are scoped to your API key: two different API keys can use the same `Idempotency-Key` without colliding.

1. **First request.** ChargeExpress records the key with a hash of the method, path and JSON body, runs the request, and stores the response.
2. **Same key, same request.** You get the stored status code and body again, with the header `Idempotent-Replayed: true`. Nothing runs a second time. The `X-Request-Id` header is new on each call.
3. **Same key, different request.** If the method, path or body differ, you get `422 idempotency_key_reused`. Use a new key.
4. **Same key while the first is still running.** You get `409 idempotency_key_in_progress`. Wait a moment and retry with the same key.

### What is stored, and for how long

* Responses with a status **below 500** are stored for **24 hours**, apart from `429`. That includes `4xx` errors: retrying a request that failed validation with the same key returns the same error. Fix the request and use a new key.
* `5xx` and `429` responses are **not** stored. The key is released, so retrying with the same key runs the request again.
* If the connection drops before a response is produced, the key is also released.
* After 24 hours the key can be used again as if it were new.

## Top-ups and the idempotency key

For `POST /wallet/fundings` and `POST /invoices/usage/pay`, the `Idempotency-Key` also determines the funding's `id` (`wallet-…`). This protects against double payment even after the 24-hour window:

* Sending the same key again for a funding that is still `pending` returns that same funding and its `checkout_url`.
* Sending the same key again for a funding that has completed or failed returns `409 conflict`.
* Sending the same key with a different amount returns `422 idempotency_key_reused` within 24 hours, and `409 conflict` after that.

Use a fresh key for every new top-up.

## Recommendations

* Generate the key **before** the first attempt and persist it with the operation, so a restarted worker retries with the same key.
* Retry network errors, `409 idempotency_key_in_progress`, `429` and `5xx` with exponential backoff, keeping the same key.
* Don't reuse keys across different operations.


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