Track status & failures
A payout is always in one of five statuses, and your ledger should model the
same five. Read it at GET /payments/organizations/{orgId}/orders/{payoutId}
or from its payout.* webhook, and accept any forward transition from the
status you hold.
| Status | Meaning | Your balance | Terminal? |
|---|---|---|---|
pending |
Accepted, not yet sent to the payment network | Held | No |
processing |
Handed to the payment network | Committed | No |
completed |
The recipient’s bank paid them | Sent | Almost (see the bank return below) |
failed |
Not paid, or returned. See failureCode |
Credited back as a separate entry when fundsReturned is true |
Yes |
canceled |
Stopped before sending; only possible while waiting on your own funding | Never left. fundsReturned: true |
Yes |
State transitions
Section titled “State transitions”A payout can make only these transitions:
completed can still become failed when the destination bank returns the funds days later.Not every payout passes through every status. A rail that settles on acceptance
goes from pending straight to completed, and a status entered and left
between two reads is never reported. In the sandbox, every payout is
processing for about 8 seconds; suffix 0002 holds it for about 50.
The bank return (completed → failed)
Section titled “The bank return (completed → failed)”The fundsReturned flag
Section titled “The fundsReturned flag”On failed or canceled, fundsReturned: true means the money is back in your
balance (for canceled, it never left). Absent means it is not confirmed back,
so don’t re-credit anyone yet. Read the flag, not the code: a
compliance_rejected payout has no fundsReturned while the funds are held for
review. In GET /balance_transactions, a payout debit with no matching
payout_return row means the money did not come back.
Failure codes
Section titled “Failure codes”A failed payout carries a failureCode. It is a payout state, not an error
response.
failureCode |
What happened | Is the money back? | What to do |
|---|---|---|---|
returned_by_bank |
It settled, then the receiving bank returned it | Yes (fundsReturned: true) |
Tell your user. Reverse whatever you credited |
account_invalid |
The account details are wrong | Check fundsReturned |
Ask for correct details, create a new recipient |
account_ |
The account cannot accept this payment | Check fundsReturned |
Try another account or corridor |
compliance_rejected |
Refused by compliance screening | Not automatically | Contact us with the payoutId. Do not retry |
limit_exceeded |
Above a corridor or account limit | Check fundsReturned |
Split it, or check limits on the corridors call |
quote_expired |
Too long between quoting and sending | Check fundsReturned |
Re-quote and send again |
authorization_ |
An authorization step was not finished | Check fundsReturned |
Start again |
execution_failed |
It did not go through, cause not established | Check fundsReturned |
Retry only once the money is back (below) |
“Check fundsReturned” means exactly that: on some routings these codes arrive
without the flag while the funds are still unconfirmed. The money is back only
when the payout carries fundsReturned: true, or when GET /balance_transactions
shows a payout_return row for it. Until then, a retry under a new idempotency
key debits your balance a second time while the first debit may still be held.
The enum also lists insufficient_funds and unknown, which no payout carries
today: a short balance is refused up front with 400 INSUFFICIENT_BALANCE, and
a failure with no specific cause is reported as execution_failed. New codes arrive
without a major version, so treat an unrecognized one as execution_failed.
The sandbox reaches only account_invalid (0001), compliance_rejected
(0004) and returned_by_bank (0003). The rest, and stage, come from live
rails only, so write a branch for every code. Errors that refuse a request
before a payout exists are on Errors.
Batch payout status
Section titled “Batch payout status”A batch has its own statuses. They track creation, not settlement:
received → validating → (awaiting_confirmation | creating) → completedcanceled · failed (terminal)completed means every line became a payout or was refused; counts says
which. failed means the run itself broke. Each created line carries a
payoutId, and that payout follows the five statuses above.
Approvals
Section titled “Approvals”With M-of-N approval on, a POST /payouts or batch confirm above the
threshold answers 202 with an approval instead of a payout. While it is open
there is no payoutId and nothing in GET /orders. When it executes, the
payout starts at pending.
pending → approved → executing → executed (the payout now exists; payoutId on the approval)pending → rejected · expired (terminal; nothing was created)executing → execution_failed · execution_unknown (terminal; nothing was created, or resolved by hand)Read approvals at GET …/payouts/approvals and …/approvals/{approvalId}.
Every transition except into executing and execution_unknown is also a
payout_approval.* event, and payout_approval.executed carries the
payoutId. An owner or admin approves or rejects in the dashboard, and a
pending approval expires after 24 hours.
The informational stage property
Section titled “The informational stage property”Some rails add stage: awaiting_details or under_review. (The enum also
lists settling, which no payout carries today.) It answers “where is my payment?” for support; don’t drive your ledger from it.
Was this page helpful?