Skip to content

Idempotency and safe retries

Networks fail. A request may reach us while its answer never reaches you. Idempotency makes the retry safe: the same request again answers the result of the first, and does nothing twice.

Send Idempotency-Key — 1 to 200 characters, a UUID is fine — on every command that creates or changes money:

Command Key Second key
POST /v1/payments required merchant_order_id
POST /v1/payments/{id}/cancel required —
POST /v1/payments/{id}/amount-correction required —
POST /v1/payments/{id}/refunds required —
POST /v1/payouts required merchant_payout_id
POST /v1/payouts/{id}/cancel required —

Reads and the webhook settings take none.

  • Same key, same body → the first answer again: the same object, Idempotent-Replayed: true on payments and payouts.
  • Same key, another body → 409 idempotency_conflict. Use a new key for a new request.
  • Same merchant_order_id or merchant_payout_id under a new key, same body → the object made first, as a repeat. With another body → 409 idempotency_conflict. A double click that sends two keys still makes one payment.
You got Do
A timeout or a dropped connection Retry with the same key and body.
503 payment_unavailable, payout_unavailable The outcome is unknown. Retry with the same key, or read the object.
409 payment_busy Wait Retry-After (1 s) and retry with the same key.
429 rate_limited Wait Retry-After (1 s) and retry with the same key.
400, 404, 409 idempotency_conflict, 422 Do not retry: fix the request.

Every retry is a new signature: a new nonce and timestamp, the same Idempotency-Key. Back off — 1, 2, 4, 8 seconds — and stop after a few attempts; then reconcile by reading.

Generate the key when you decide to make the request — when the order is placed — and store it with the order, so a retry after your own process restarts reuses it.