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

# Idempotency & Retries

> How to retry requests safely without double-moving money

Network failures happen: a timeout after you called [Withdraw Funds](/api-reference/endpoint/wallet/withdraw) leaves you not knowing whether the payout was created. This page covers the tools the API gives you to retry safely.

<Warning>
  The API does **not** support a global `Idempotency-Key` header. Blindly retrying a money-moving `POST` can create a second transaction. Use the patterns below instead.
</Warning>

## Payouts: check before you retry

Give every withdrawal an `externalReference` — a unique ID from **your** system. `description` is required alongside it:

```bash theme={null}
curl -X POST "https://api.rolla.xyz/api/v1/external/wallet/withdraw" \
  -H "X-API-Key: your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 10000,
    "currency": "USD",
    "beneficiaryId": "c3d4e5f6-a7b8-9012-cdef-123456789012",
    "description": "Invoice 4021",
    "externalReference": "payout-2026-08-30-0042"
  }'
```

If the request times out or errors ambiguously, **look the reference up before retrying**. [Get Transaction](/api-reference/endpoint/wallet/get-transaction) accepts your `externalReference` in place of a transaction ID:

```bash theme={null}
curl "https://api.rolla.xyz/api/v1/external/wallet/transaction/payout-2026-08-30-0042" \
  -H "X-API-Key: your_api_key_here"
```

* **Found** → the payout was created; don't resend.
* **`404`** → it never landed; safe to retry with the same reference.

<Warning>
  **This pattern does not work for NGN or mobile money payouts.** Those rails must carry a
  provider-formatted reference, so Rolla generates one (`NG-XXXXXXXX` / `MM-XXXXXXXX`) and your
  `externalReference` is not stored on the transaction — looking it up afterwards always returns
  `404`, whether or not the payout was created. Send `metadata` with your own id instead, and
  recover by listing [`GET /wallet/transactions`](/api-reference/endpoint/wallet/transactions) over
  the window in question and matching on the `metadata` you attached before you resend anything.
</Warning>

## Naturally idempotent endpoints

Some creation endpoints are keyed on an identifier you supply and can be retried freely:

| Endpoint                                                                                               | Idempotency key       | Behaviour on repeat                                               |
| ------------------------------------------------------------------------------------------------------ | --------------------- | ----------------------------------------------------------------- |
| [Generate Customer Virtual Account](/api-reference/endpoint/wallet/virtual-accounts-generate-customer) | `customer_identifier` | Returns the existing account with `200` (a new one returns `201`) |
| [Issue Bank Account](/api-reference/endpoint/accounts/issue-bank-account) (USD)                        | `reference`           | The same reference always returns the same account                |

## Webhooks: deduplicate on `event_id`

Webhook deliveries may arrive more than once (retries, redelivery). Every event carries a **deterministic** `event_id` — the same logical event always has the same ID — so store processed IDs and skip duplicates. See [Delivery & Retries](/api-reference/webhooks/delivery).

## Retry etiquette

* Retry `5xx` and network errors with exponential backoff; treat `4xx` (other than `429`) as terminal — fix the request instead.
* For money-moving calls, always run the lookup-then-retry pattern above rather than resending on a timer.
