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

MethodSend Idempotency-Key?
POST (create)✅ Always
PATCH (update)❌ No
GET❌ No
DELETE❌ No
⚠️

Sending an x-idempotency-key on a GET request will return a 400 Bad Request error.


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 Conflict error
  • 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

ScenarioResponse
Same key, same payload, same endpoint200 OK — returns original response
Same key, different payload409 Conflict
Same key, different endpoint409 Conflict
Missing key on a create POST400 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:

FieldPurposeScope
x-idempotency-keyPrevents duplicate API operations on retryAPI layer
referenceYour internal identifier for the transactionBusiness layer

Best practice

Always set both. Use a UUID for x-idempotency-key and a meaningful business reference (invoice ID, payout ID) for reference.


What's next

Next stepLink
Make your first API callQuickstart
See request format conventionsRequest and Response Format
Handle errors safely on retryTroubleshooting and Known Pitfalls