A dispute lost three weeks after payout
The chargeback arrives after the seller has been paid and the funds have left. One lost dispute, followed from the sale to the write-off.
Refunds are the well-known version of this problem. Disputes are the same structure with worse timing and an extra fee, and they are the version that catches platforms who have already fixed their refund path.
What follows is a fabricated marketplace and one transaction, in the order it happened. The amounts are round for legibility; the dispute fee is illustrative, because it varies by account and region.
Day 0 — the charge
A buyer pays $860 for a piece of equipment. It is a destination charge: the money lands in the platform's balance, the platform takes a 15% application fee of $129, and $731 is destined for the seller.
await stripe.charges.create({
amount: 86000,
currency: 'usd',
source: token,
application_fee_amount: 12900,
transfer_data: { destination: 'acct_EXAMPLE' },
});
Platform position: $129 of revenue. Nothing unusual.
Day 1 — the transfer
The transfer of $731 settles into the connected account's Stripe balance. The seller can see it. So could a reversal, at this point — the funds are sitting right there.
Day 3 — the payout
The seller is on a daily payout schedule, because that is what sellers like and what the platform advertised when onboarding them. The $731 leaves Stripe for their bank.
This is the moment the story is decided, and nothing has gone wrong yet. There is no dispute, no signal, nothing to act on. The platform's exposure just quietly became unsecured.
Day 24 — the dispute
The buyer's card issuer raises a chargeback. charge.dispute.created fires.
{
"type": "charge.dispute.created",
"data": { "object": { "amount": 86000, "status": "warning_needs_response" } }
}
Most platforms treat this event as a customer-service trigger: gather evidence, respond, hope. Very few treat it as a funds trigger, which is the more consequential reading. Nothing has been lost yet, and this is the last moment at which the seller's balance is the platform's problem to reach rather than a memory.
Day 24 — the platform balance is debited
Stripe debits the platform immediately, pending the outcome: the full $860, plus a dispute fee.
Read that carefully, because the asymmetry is the whole point. The platform is debited the charge amount. The platform only ever kept the fee. The $731 that the buyer's money actually became is in a bank account belonging to somebody else, and the connected account is untouched by any of this unless the platform explicitly reverses the transfer.
Day 38 — the dispute is lost
The evidence was not sufficient. charge.dispute.closed fires with status: lost, and the provisional debit becomes permanent.
The platform's position, worked through:
- Debited $860 for the disputed charge.
- Debited a dispute fee on top.
- Lost the $129 of application fee revenue, since the sale is gone.
- Still owed, in principle, the $731 that sits in a seller's bank account.
The recoverable figure is $731 — the transfer — and not $860. This distinction matters more than it looks. The $129 was never in the connected account, so attempting to reverse $860 does not merely overstate the loss on a report, it fails outright, because you cannot reverse more than you transferred. Any tool or script that computes the loss as the charge amount will produce reversals Stripe rejects.
It is also wrong to add the lost application fee to the loss. It is revenue that did not materialise, which belongs in a different column from cash that left the building. Counting it as loss double-counts money the platform kept.
The customer got their money back. Your platform paid for it. The seller kept it.
The two moments where this could have gone differently
Day 3, the payout schedule. Daily payouts to a new or low-volume seller are a choice about risk, not just about seller experience. A short delay on the first weeks of a seller's history — or on categories with a known chargeback profile — keeps funds inside the reversal window for the period when the exposure is highest. This is a product decision that finance and engineering have to make together, and it is usually made by neither.
Day 24, the dispute-created event. This is the actionable one. A reversal attempted the moment a dispute opens has a materially better chance of succeeding than one attempted two weeks later, because it is racing fewer payouts.
// On charge.dispute.created, not charge.dispute.closed
const charge = await stripe.charges.retrieve(chargeId, { expand: ['transfer'] });
const transferId = typeof charge.transfer === 'string' ? charge.transfer : charge.transfer?.id;
if (transferId) {
await stripe.transfers.createReversal(transferId, { amount: 73100 });
}
The trade is real and worth stating plainly: reverse on open and you will occasionally claw back from a seller on a dispute you go on to win, and you will have to make them whole. Whether that is acceptable depends on your win rate and how you communicate it. What is not defensible is arriving at the decision by default, having never considered it, and discovering the answer at month end.
The detection half of this is not complicated. A charge.dispute.created where the linked transfer has no reversal against it is the entire signal, and if you already handle Stripe webhooks you already have the input.
Automating Stripe Connect dispute clawbacks
FeeGuard is an independent product and is not affiliated with, endorsed by, or sponsored by Stripe, Inc. "Stripe" and "Stripe Connect" are trademarks of Stripe, Inc., referenced descriptively.