Stablecoin payout statuses explained: processing, on hold, completed, failed, and refunded

What each stablecoin payout status means, which are final, why an on-chain confirmation isn't a completed payout, and how to test every failure.

A stablecoin payout moves through a small set of statuses. At BlindPay it starts as processing, can pause at on_hold for review, and ends in exactly one terminal state: completed, failed, or refunded. Your product needs a clear answer for each, because each one means something different to the person waiting for money.

Most payout bugs aren't in the happy path. They're in the states nobody designed a screen for.

What does each payout status mean?

These are BlindPay's statuses, from the payouts reference. Other providers use different names for the same ideas; map yours before you build.

StatusWhat it meansTerminal?What to tell the user
processingStablecoins are being pulled from the funding source and the fiat transfer is in flightNo"Sending."
on_holdHeld for reviewNo"Under review. No action needed unless we ask."
completedFiat landed in the recipient's bank accountYes"Paid."
failedThe payout did not complete, for example a rejected compliance check or a review timeoutYes"Didn't go through." Plus next step.
refundedThe stablecoins returned to the funding source instead of being convertedYes"Returned to your balance."

Three terminal states, and they're not interchangeable. failed and refunded both mean the recipient wasn't paid, but only refunded tells you where the money is.

Why is "confirmed on-chain" not the same as "completed"?

Because a payout has two legs, and the blockchain only sees the first one.

The on-chain leg pulls stablecoins from the funding source. That confirms in seconds and is final. The fiat leg sends local currency to a bank account over Pix, SPEI, ACH, RTP, SEPA, or SWIFT. That takes minutes to days depending on the rail, and it's the only leg the recipient cares about.

So if your UI flips to "paid" when you see a transaction hash, you're early. Sometimes by five business days. Use completed for "paid." Timing by rail is in how long a stablecoin payout takes, and the difference between an API accepting a request and money becoming final is in stablecoin API SLAs and settlement finality.

Why do payouts land on hold?

Compliance. on_hold isn't an error. It's a review step, and for some rails it's routine:

  • SWIFT. Every SWIFT payout starts on_hold. When the recipient isn't the sender's own account, it waits there until supporting documents are submitted and approved.
  • USD ACH, wire, and RTP. These pass through on_hold as a standard step, for compliance review after the stablecoins are collected.
  • Transaction monitoring. Any payout flagged as unusual (first-time withdrawals, large amounts against a customer's history, a screening match) can be held. Compliance may send a request for information. If it goes unanswered for 24 hours, the transaction may be refunded to the sender.

A hold can take up to 30 days to resolve. Approval resumes the flow. A timeout without a decision fails the payout. Design the on_hold screen for days, not seconds. More in on-hold transactions.

What are the tracking objects for?

The top-level status tells you where the payout is. The tracking_* objects tell you which stage it's in. A BlindPay payout carries tracking_transaction, tracking_payment, tracking_complete, tracking_liquidity, and tracking_partner_fee, each with a step of processing, on_hold, pending_review, or completed.

Use them for a detailed status view: "stablecoins collected, bank transfer in progress." Don't drive business logic from them. Your ledger should move on status, and only on terminal values.

What does a payout state machine look like?

Provider-neutral pseudocode. Every transition is driven by an event you received, never by a timer guessing.

text

The first check is the important one. Events can arrive late or out of order. A stale processing event must not undo a completed.

What happens after a failure or refund?

They go different directions.

Refunded means the stablecoins came back to the funding source instead of being converted. Common causes: wrong beneficiary details, a closed or restricted account, or the receiving bank rejecting the transfer. Stablecoin refunds process right away. Credit the balance back in your ledger and ask the sender to check the recipient's details.

Failed means the payout didn't complete, and at BlindPay it does not automatically refund. If a payout reaches failed or sits past its review window, contact support rather than assuming it resolves on its own. In your product, a failed payout should open an internal ticket, not just show a red badge.

Either way, once a payout is terminal, it stays terminal. To try again, create a new quote and a new payout. On a stablecoin rail, a confirmed transfer can't be clawed back, which is why whether stablecoin payments are reversible is really a question about the fiat edges.

What should you test before launch?

Every row in this matrix, on a development instance, before your first real payout.

ScenarioHow to trigger itWhat should happen
Happy pathAny normal amountcompleted, recipient notified
Failed payoutQuote request_amount of 66600 ($666.00)failed, ticket opened, no auto-credit
Refunded payoutQuote request_amount of 77700 ($777.00)refunded, balance credited back
Expired quoteWait past expires_at, then executeRejected, new quote fetched
Reused quoteExecute a second payout on the same quote_idRejected, no second payout
Duplicate eventReplay a webhook from the dashboardState unchanged
Out-of-order eventSend an old processing after completedState stays completed
Insufficient fundsExecute with less balance than sender_amountHandled either way: rejected at create, or created and then failed at collection

The first three rows come straight from BlindPay's sandbox behavior. The rest are your code's job. What a sandbox can and can't simulate is covered in stablecoin API sandbox vs production.

Check the funding balance against the quote's sender_amount before you execute. It's cheaper than finding out from a failure.

Where does BlindPay fit?

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 payout reports a single top-level status plus per-stage tracking, and signed webhooks fire at each transition. Retries stay safe with idempotency keys.

What to do next

Open your payout UI and list every status you show today. If on_hold, failed, and refunded don't each have their own screen and their own ledger action, add them. Then run the test matrix above on a free development instance. The payouts reference has the full lifecycle, and the payout quickstart gets you to your first test payout.

This article is for general information only and is not legal, tax, or financial advice.

FAQ