---
title: "Stablecoin payout statuses explained: processing, on hold, completed, failed, and refunded"
seoTitle: "Stablecoin payout statuses explained"
description: "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."
date: "2026-09-03"
category: "payments"
author: "BlindPay Team"
faq:
  - q: "What are the statuses of a stablecoin payout?"
    a: "At BlindPay, a payout starts as processing, can pass through on_hold for review, and ends in one of three terminal states: completed (fiat landed in the recipient's bank account), failed (the payout did not complete), or refunded (the stablecoins went back to the funding source instead of being converted)."
  - q: "Why is my stablecoin payout on hold?"
    a: "Usually compliance review. At BlindPay, every SWIFT payout starts on_hold, and USD ACH, wire, and RTP payouts pass through on_hold as a standard step after the stablecoins are collected. Transaction monitoring can also hold a payout for review or a request for information. A hold can take up to 30 days to resolve."
  - q: "Is a payout complete when the blockchain transaction confirms?"
    a: "No. The on-chain confirmation means the stablecoins moved from the funding source. The payout is complete when the fiat lands in the recipient's bank account, which is instant on Pix or SPEI and about 5 business days on SWIFT."
  - q: "Does a failed payout refund automatically?"
    a: "Not at BlindPay. A payout that reaches failed or sits in review does not automatically refund the sender. If a payout looks stuck, contact support rather than assuming it will resolve. A refunded payout is different: the stablecoins have already returned to the funding source."
  - q: "How do I test failed and refunded payouts?"
    a: "On a BlindPay development instance, payouts complete automatically. Set the payout quote's request_amount to 66600 ($666.00) to force failed or 77700 ($777.00) to force refunded. Every other amount completes."
---

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](/docs/payouts). Other providers use different names for the same ideas; map yours before you build.

| Status | What it means | Terminal? | What to tell the user |
| --- | --- | --- | --- |
| `processing` | Stablecoins are being pulled from the funding source and the fiat transfer is in flight | No | "Sending." |
| `on_hold` | Held for review | No | "Under review. No action needed unless we ask." |
| `completed` | Fiat landed in the recipient's bank account | Yes | "Paid." |
| `failed` | The payout did not complete, for example a rejected compliance check or a review timeout | Yes | "Didn't go through." Plus next step. |
| `refunded` | The stablecoins returned to the funding source instead of being converted | Yes | "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](/resources/more/stablecoin-payout-settlement-times), and the difference between an API accepting a request and money becoming final is in [stablecoin API SLAs and settlement finality](/resources/more/stablecoin-api-sla-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](/docs/kb/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.

```
TERMINAL = {completed, failed, refunded}

on_event(payout_id, new_status):
    current = ledger.status(payout_id)

    if current in TERMINAL:
        return                        # never leave a terminal state

    if new_status == current:
        return                        # duplicate event, ignore

    ledger.set_status(payout_id, new_status)

    match new_status:
        processing -> show("Sending")
        on_hold    -> show("Under review"); start_review_timer(30 days)
        completed  -> mark_paid(); notify_recipient()
        failed     -> open_ticket(); notify_sender()
        refunded   -> credit_back(); notify_sender()
```

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](/resources/more/are-stablecoin-payments-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.

| Scenario | How to trigger it | What should happen |
| --- | --- | --- |
| Happy path | Any normal amount | `completed`, recipient notified |
| Failed payout | Quote `request_amount` of `66600` ($666.00) | `failed`, ticket opened, no auto-credit |
| Refunded payout | Quote `request_amount` of `77700` ($777.00) | `refunded`, balance credited back |
| Expired quote | Wait past `expires_at`, then execute | Rejected, new quote fetched |
| Reused quote | Execute a second payout on the same `quote_id` | Rejected, no second payout |
| Duplicate event | Replay a webhook from the dashboard | State unchanged |
| Out-of-order event | Send an old `processing` after `completed` | State stays `completed` |
| Insufficient funds | Execute with less balance than `sender_amount` | Handled either way: rejected at create, or created and then `failed` at collection |

The first three rows come straight from BlindPay's [sandbox behavior](/docs/learn/sandbox-vs-production). The rest are your code's job. What a sandbox can and can't simulate is covered in [stablecoin API sandbox vs production](/resources/more/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](/global-payments) 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](/resources/more/stablecoin-api-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](/docs/payouts) has the full lifecycle, and the [payout quickstart](/docs/quickstart-payout) gets you to your first test payout.

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