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:What each outcome looks like
Keys are scoped to your tenant and retained for 24 hours.First use — the request runs
First use — the request runs
The handler executes and the response is cached against the key.
Replay after completion — cached response
Replay after completion — cached response
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.
Replay while still in flight — 409
Replay while still in flight — 409
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.
Missing header — 400
Missing header — 400
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 withGET /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.