A timeout on a payout call is the most common way to pay someone twice. How idempotency keys prevent it, what replays, what conflicts, and how to retry.
An idempotency key is a unique string you attach to a payout request so a retry can't create a second payout. Send the same key with the same body and the API returns the first result instead of running it again. It's the difference between "the request timed out, did it go through?" and "the request timed out, I'll just send it again."
In a payments API that difference is money. Here's how it works, and where it stops protecting you.
Not because anyone clicks twice. Because networks are unreliable in one specific way: a request can succeed on the server and the response can still get lost on the way back.
Picture it. Your server sends POST /payouts. The API creates the payout, pulls the stablecoins, and starts the Pix transfer. Then the connection drops before the response arrives. Your HTTP client throws a timeout. From your side, it looks exactly like a failure.
So your retry logic does what retry logic does. Sends it again. Without an idempotency key, that's a second payout. The supplier gets paid twice, and on-chain legs don't come back.
You generate a key before the first attempt and send the same key on every retry. The API stores the result under that key. When a retry arrives, it replays the stored response instead of running the action.
At BlindPay, you pass it as a header on any POST, PUT, PATCH, or DELETE under /v1/*:
Any string up to 255 characters works. A UUID is a good default. The example comes from the idempotency docs.
Four outcomes. Handle all of them.
| Situation | BlindPay response | What your code should do |
|---|---|---|
| Same key, same body, first request finished | Original response, plus Idempotency-Replayed: true | Treat it as success. Nothing ran twice. |
| Same key, different body | 422 idempotency_key_payload_mismatch | Bug on your side. Don't retry. Alert. |
| Same key, first request still running | 409 idempotency_key_in_flight with Retry-After | Wait the given seconds, retry with the same key and body. |
| Key over 255 characters | 400 idempotency_key_invalid | Fix the key generation. |
The replay means side effects don't repeat either. No second database write, no second webhook, no second call to a bank.
Because the check is a SHA-256 hash of the raw request body, not a semantic JSON comparison. {"a":1,"b":2} and {"b":2,"a":1} are the same object and two different hashes.
This catches people who rebuild the request on retry. If your retry path re-serializes a fresh object, key order or whitespace can change, and a harmless retry becomes a 422. The fix: serialize once, keep the exact bytes, and send those bytes on every attempt.
Provider-neutral pseudocode. The key and body are fixed before the first attempt.
Two things make this safe. The key is saved to your ledger before the first call, so a crash mid-retry doesn't lose it. And every retry sends identical bytes.
Know these limits before you rely on it.
5xx releases the key, so the retry runs as a fresh attempt. That's the right behavior (the first attempt didn't finish), but it means your ledger decides what "done" means, not the key.That's why idempotency is one layer, not the whole defense.
quote_id can only back one payout. A second payout on the same quote fails. Different mechanism, same goal.Idempotent writes and deduplicated reads are two halves of the same thing. Skip either and duplicates find a way in.
On a development instance, force each case on purpose:
Idempotency-Replayed: true.422.409 with Retry-After.quote_id on a new key. Expect the payout to fail.Development instances are free and payouts complete automatically, so this is an hour of work. For where retries sit in the whole flow, see how a stablecoin payment moves.
BlindPay is a stablecoin API for cross-border payouts and collections: USDC or USDT in, local currency out over Pix, SPEI, ACH, RTP, SEPA, and SWIFT (POBO/COBO) to 100+ countries, with no pre-funding. Every mutating endpoint accepts an Idempotency-Key header, KYC, KYB, and sanctions screening run inside the API, and virtual USD accounts turn ACH, wire, and SWIFT deposits into stablecoins.
Grep your payout code for the retry path. If it doesn't send a key generated before the first attempt, and the same bytes on every retry, fix that first. Then run the four tests above against a development instance. The idempotency reference has every edge case.
This article is for general information only and is not legal, tax, or financial advice.
AP2, ACP, and x402 each verify that an AI agent had permission to spend. What each covers, who backs it, and the reconciliation gap none close.
Seven stablecoin payment platforms compared for US fintechs in 2026: production readiness, compliance, settlement speed against ACH, and evaluation.
How to choose a stablecoin payment provider in 2026: the four provider types, a comparison of 10 options, and the questions that decide the fit.