Idempotency
Idempotency ensures that retrying a failed request does not result in duplicate operations — particularly critical for financial transactions where double-execution can cause real harm.
How idempotency works in NadaPay
When you send a POST request to create a resource, include a unique Idempotency-Key header. If the request fails or times out and you retry it with the same key, NadaPay will return the original response instead of creating a duplicate.
curl --request POST \
--url https://sandbox.api.nadapay.com/v1/transactions \
--header 'x-api-key: YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--header 'x-idempotency-key: a1b2c3d4-e5f6-7890-abcd-ef1234567890' \
--data '{
"wallet_id": "wlt_01HXXXXXXXXXXXXXXXXXX",
"beneficiary_account_id": "bac_01HXXXXXXXXXXXXXXXXXX",
"quote_id": "qte_01HXXXXXXXXXXXXXXXXXX",
"reference": "payout-invoice-0042"
}'If you send this exact request twice with the same Idempotency-Key, NadaPay returns the result of the first request on the second call — no duplicate transaction is created.
When to send an x-idempotency-key
| Method | Send Idempotency-Key? |
|---|---|
POST (create) | ✅ Always |
PATCH (update) | ❌ No |
GET | ❌ No |
DELETE | ❌ No |
Sending anx-idempotency-keyon aGETrequest will return a400 Bad Requesterror.
How to generate an x-idempotency-key
Use a UUID (v4) generated fresh for each logical action:
// Node.js
const { randomUUID } = require('crypto');
const idempotencyKey = randomUUID();
// → 'a1b2c3d4-e5f6-7890-abcd-ef1234567890'# Python
import uuid
idempotency_key = str(uuid.uuid4())
# → 'a1b2c3d4-e5f6-7890-abcd-ef1234567890'Storing and reusing keys for retries
Generate your idempotency key before making the request, and persist it alongside the logical action in your own system. This way, if the request fails at any point — timeout, network error, server error — you can safely retry with the same key.
// Recommended pattern
const idempotencyKey = randomUUID();
await db.savePayoutAttempt({ payoutId, idempotencyKey });
const response = await nadapayClient.executeTransaction({
walletId,
beneficiaryAccountId,
quoteId,
idempotencyKey
});Rules for x-idempotency-key usage
- One key per logical action — generate a new key for each distinct operation
- Never reuse a key with a different payload — this will return a
409 Conflicterror - Never reuse a key with a different endpoint — keys are scoped to the URL they were first used with
- Persist the key before sending — so you can retry safely after failures
- Do not use sequential or predictable values — use UUIDs
What happens when idempotency is violated
| Scenario | Response |
|---|---|
| Same key, same payload, same endpoint | 200 OK — returns original response |
| Same key, different payload | 409 Conflict |
| Same key, different endpoint | 409 Conflict |
| Missing key on a create POST | 400 Bad Request |
Idempotency and the reference field
The reference field on transactions is a separate, user-defined identifier for your own reconciliation — it is not the same as the Idempotency-Key. Both serve different purposes:
| Field | Purpose | Scope |
|---|---|---|
x-idempotency-key | Prevents duplicate API operations on retry | API layer |
reference | Your internal identifier for the transaction | Business layer |
Best practiceAlways set both. Use a UUID for
x-idempotency-keyand a meaningful business reference (invoice ID, payout ID) forreference.
What's next
| Next step | Link |
|---|---|
| Make your first API call | Quickstart |
| See request format conventions | Request and Response Format |
| Handle errors safely on retry | Troubleshooting and Known Pitfalls |
Updated about 2 hours ago