All docs

payout.failed — what it signals for cross-border platforms

A failed payout on a multi-currency platform is often the visible symptom of an FX or balance problem that has been quietly costing you money for months.

What the event means

payout.failed fires when a payout to a bank account could not be completed. failure_code carries the reason — account_closed, insufficient_funds, could_not_process, and others.

The cross-border angle

On a platform that settles in one currency and pays out in another, a failed payout is worth treating as a prompt to inspect recent conversions. Currency conversion carries a spread on every transfer, and it never announces itself.

Each individual conversion looks unremarkable. Across thousands of cross-border payouts the aggregate is material, and because no API call ever fails, nothing in a platform's own monitoring will mention it.

Measuring slippage

Stripe exposes the realised rate on the balance transaction behind each transfer, as exchange_rate. Comparing each transfer's realised rate against the rate the same currency pair achieved on comparable transfers that day isolates the outliers.

That relative comparison is the actionable signal. Measuring against a third-party mid-market rate would mostly restate the fact that Stripe charges a spread — a pricing negotiation, not a reconciliation problem.

Reading the failure code

failure_code on the payout tells you which of several unrelated problems you have. They need different responses and only some relate to reconciliation.

insufficient_funds — the balance moved between the payout being scheduled and executed, usually because a refund or dispute landed in between. On a Connect platform this is often the visible symptom of unreversed refunds draining the platform balance.

account_closed, no_account, invalid_account_number — banking details are wrong or stale. An onboarding data problem, not a money problem.

could_not_process, declined — the receiving bank rejected it. Usually transient and worth retrying.

debit_not_authorized — the account holder has not authorised debits, which blocks Stripe from recovering negative balances. Relevant to clawback: reversals against this account may not be recoverable.

Why a failed payout is a useful reconciliation trigger

A payout failing for insufficient funds on a platform that believes it is profitable is worth investigating rather than retrying.

The most common underlying cause on Connect platforms is refund leakage: the platform has been refunding buyers from its own balance without reversing transfers, so the balance depletes faster than revenue suggests it should. Each individual refund is too small to notice; the payout failure is the first aggregate signal.

For cross-border platforms there is a second cause — conversion spreads compounding across many transfers, leaving less in the settlement currency than projected.

Neither shows up as an error anywhere else. The failed payout is often the only alarm that ever sounds.

The cross-border angle

When a platform settles in one currency and pays out in another, Stripe converts at the rate in effect at transfer creation, and that rate carries a spread.

On any single transfer the spread is unremarkable. Across thousands of cross-border payouts it becomes material — and because no API call fails and no webhook reports it, nothing in a platform's own monitoring will ever mention it.

Stripe exposes the realised rate on the balance transaction behind each transfer, as exchange_rate. Comparing each transfer's realised rate against the rate the same currency pair achieved on comparable transfers that day isolates the outliers worth investigating.

const balanceTx = await stripe.balanceTransactions.retrieve(
  transfer.balance_transaction as string,
);

// Populated only when the transfer crossed currencies
const realisedRate = balanceTx.exchange_rate;

What an outlier usually means

A deviation several times your normal spread rarely indicates one bad conversion. It usually indicates a misconfigured settlement currency — and that misconfiguration keeps costing money on every future transfer until someone changes a setting.

That is why FX findings are worth surfacing despite being unrecoverable: the individual loss is sunk, but the cause is frequently still live and still charging you.

What FeeGuard does with it

FeeGuard uses payout.failed as one of two triggers for a sweep of recent cross-border transfers, comparing each realised rate against that day's baseline for the same currency pair.

The failed payout itself is recorded in the finding's metadata for context, but it is not the finding. The finding is whatever the sweep turns up.

Alert on this one

A failed payout is one of the few Stripe events that almost always warrants a human seeing it promptly. It is low-volume by nature, so alerting on every occurrence will not create fatigue, and the cost of missing one compounds daily.

Route it to whoever owns finance operations rather than to an engineering channel. The most common causes — wrong banking details, insufficient balance — are not engineering problems, and an alert that lands with people who cannot act on it is worse than none.

Stripe does not retry automatically

A failed payout stays failed. Stripe does not reschedule it, and the funds return to the available balance rather than being held for another attempt.

For platforms on automatic payouts this usually self-corrects — the next scheduled payout picks up the balance including the failed amount. For platforms on manual payouts, nothing happens until someone creates a new one, and the money simply sits there.

The dangerous middle case is a persistent cause. If banking details are wrong, every subsequent payout fails the same way, and a platform with no alerting on this event can accumulate weeks of unpaid balance before anyone notices. The balance looks healthy; the bank account is empty.

Connected account payouts fail differently

A payout.failed on a connected account is a different problem from one on the platform account, and the distinction matters for recovery.

When a connected account cannot receive payouts, their Stripe balance accumulates rather than draining. Counter-intuitively that is *good* for clawback: the funds you would reverse against are still sitting there.

A seller with failing payouts and outstanding disputes is therefore the easiest recovery you will ever do — and the window closes the moment they fix their banking details. Worth prioritising if your findings queue surfaces both signals.