Skip to content

Idempotency and retries

POST /v2/payins and POST /v2/payouts require the Idempotency-Key header (8 to 128 characters). Sending the same request again with the same key returns the same operation, in whatever state it is at that moment, and asks nothing new of the bank or of your user. The same key with a different body is 409 idempotency_key_reused.

Derive the key from your order identifier (order-4821), not from the time or a random value: that way a retry from a brand-new process sends the same key. If you use an SDK and don’t pass a key, the SDK generates one, reuses it on every retry and returns it to you with the created operation: store it next to your order.

What retries on its own, and what doesn’t

Section titled “What retries on its own, and what doesn’t”
Situation What to do
Network error before any response, 429, 5xx on a GET Retry with growing backoff. Nothing moved.
The same on a POST with Idempotency-Key Retry with the same key. It is safe by definition.
POST .../confirm, .../resend-code, .../cancel, /v2/webhook-endpoints Do not retry blindly. Look the operation up with GET and decide.
503 temporarily_unavailable on .../cancel The payout is still pending. Look it up; the system resolves it on its own.
4xx (validation, scope, not found) Fix the request. Retrying the same thing gives the same result.

The SDKs apply exactly this table: three attempts by default, exponential backoff with jitter, and never a retry that could move money twice.

If an operation stays pending with pending_reason: processing, the API keeps asking the provider until the final answer and notifies you by webhook. You don’t need to poll in a loop; if you do, GET /v2/transactions/{id} costs you nothing and asks nothing of the bank.