Skip to main content
Networks fail after the server has already done the work. A timeout on POST /v1/payouts tells you nothing about whether money is moving. Idempotency keys make the retry safe: the second request returns the first one’s result instead of creating a second payout. Every authenticated POST, PUT, and PATCH requires an Idempotency-Key header. It is not optional, and a request without one is rejected before any work happens. The one exception is POST /v1/banks/resolve, which is a POST purely to keep an account number out of a query string. It writes nothing, so there is nothing to make idempotent.

Choosing a key

Use a UUID v4, generated once per logical operation and persisted alongside whatever you are creating. The natural pattern is to store it on your own record before you call us:
Now a retry after any failure — timeout, crash, redeploy — reuses the same key and cannot double-pay.
Do not derive keys from something that repeats, such as the order total or the beneficiary’s phone number. Two genuinely different payouts that collide on a key will silently return the first payout’s response, and the second will never be created.

What each outcome looks like

Keys are scoped to your tenant and retained for 24 hours.
The handler executes and the response is cached against the key.
You get byte-for-byte the original response body and status code. No second payout is created. This is the case that makes retries safe.
The original request has not finished yet, so there is no result to return and running it again would be unsafe. Back off and retry.

Failed requests release the key

If a request fails, its key is cleared rather than held. Retrying with the same key runs the operation again, which is what you want — a payout rejected for a compliance reason you have since fixed should go through on retry, not replay the old failure forever. The consequence: a key only pins a response once the request has succeeded.

After 24 hours

Keys expire 24 hours after first use. Reusing one after that treats the request as new and creates a second payout. Retries should happen well inside that window; anything older should be reconciled by looking the payout up with GET /v1/payouts rather than replayed.
merchant_reference is the durable handle, not the idempotency key. Set it on every payout to something meaningful in your system — it is returned on the payout, included in webhook payloads, and searchable long after the idempotency key has expired.