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

# Idempotency

> Retry a money-moving request safely. The same Idempotency-Key returns the original result instead of paying twice.

Networks fail in the worst possible way: your request arrives, Numero moves the money, and the response is lost on the way back. You cannot tell that apart from the request never arriving — and retrying blind pays the recipient twice.

An **idempotency key** removes the ambiguity. You generate a unique value per attempt and send it as the `Idempotency-Key` header. If Numero has already processed that key, it returns the **original result** instead of doing the work again. Retrying is then always safe.

## When you must send it

Every endpoint that moves value requires the header. The API reference lists `Idempotency-Key` in its Headers table wherever it is mandatory:

* Transfers — `/business/single`, `/business/bulk`, `/business/usd/transfers`
* Payouts — `/business/payout/initiate`
* VAS purchases — airtime, data, electricity, betting funding, cable TV (including add-ons)
* Wallet money movement — `/wallets/{walletReference}` credit, debit, transfer and payout

Omitting it on one of those endpoints returns **HTTP 400**. That is deliberate: without a key, a client-side network retry has no way to be safe, so we would rather fail your first attempt than risk double-sending on your second.

On other write endpoints the header is **optional but honoured** — send one and you get the same protection.

## Generating a key

Any unique string up to **100 characters**. A UUID is the obvious choice.

```bash theme={null}
curl -X POST "https://api.usenumero.com/numeroaccount/api/v1/business/single" \
  -H "Content-Type: application/json" \
  -H "X-Numero-Api-Key: your_api_key" \
  -H "X-Numero-Signature: $SIGNATURE" \
  -H "X-Numero-Signature-Version: v2" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d "$BODY"
```

Generate the key **once per logical operation** and reuse it across every retry of that operation. A fresh key on each retry defeats the entire mechanism — that is the most common way this goes wrong.

Store the key alongside your own record of the payment before you send the request. If your process dies mid-flight, you can then resume with the same key and discover what happened rather than guessing.

## What Numero does with it

| Situation                                   | Result                                                                        |
| ------------------------------------------- | ----------------------------------------------------------------------------- |
| Key never seen before                       | Request is processed normally and the response is cached                      |
| Same key, **same** request body             | The original response is replayed — no second transfer                        |
| Same key, **different** request body        | **HTTP 409 Conflict** — the key is already committed to a different operation |
| Key missing on an endpoint that requires it | **HTTP 400**                                                                  |
| Key longer than 100 characters              | **HTTP 400**                                                                  |

The 409 is a safety net, not something to work around. It means your code reused a key for a different payment, which almost always indicates a bug in how keys are generated.

## Scope and lifetime

Keys are scoped **per merchant**, so you only need uniqueness within your own account — another merchant using the same string does not collide with you.

Cached responses are retained for **24 hours** by default. After that the key is forgotten and reusing it starts a new operation, so retries should happen well inside that window. Anything older should be resolved by querying transfer status rather than replaying.

## Idempotency is not a substitute for checking status

If you are unsure whether a transfer went through and you no longer hold the key, do not send a fresh request to find out. Query [transfer status](/api-reference/transfers-status) with the reference you were given. Idempotency protects a retry of the *same* attempt; it cannot protect a new attempt you have chosen to make.
