Appendix C · The webhook map for money events
The Platform Refund Ledger · 3 min read
Webhooks are how a platform learns about backward money movements at the moment they start, rather than at month-end when they have finished. Event names and payload shapes are Stripe's (docs.stripe.com/api/events/types); the "money meaning" column is the operational reading this book has built up, and the last column is the smallest useful response.
| Event | Fires when | Money meaning | First action worth taking |
|---|---|---|---|
charge.refunded | A refund is created | The buyer's leg moved; the seller's and fee legs moved only if flags were set | Record refund; compute expected reversal and fee share per chapter 8; compare against live transfer state |
charge.refund.updated | Refund status changes (pending, failed, canceled) | A failed or canceled refund returns funds to the originating balance — destination patterns re-credit the platform | Reopen the episode if it was counted as done |
charge.dispute.created | Issuer opens a dispute | Platform debited now on destination/separate patterns; seller untouched; recovery window starts closing at each payout | Log exposure = transfer.amount − amount_reversed; run dial-3 policy from chapter 10 |
charge.dispute.closed | Dispute resolves; check status | lost: provisional debit becomes permanent. won: funds return via adjustment lines | On lost: execute clawback or queue for netting; on won: schedule any owed retransfer (mind cross-border limits) |
charge.dispute.funds_withdrawn | Stripe pulls disputed funds from the account | Confirms the debit as ledger lines of type adjustment | Match to the dispute register entry |
charge.dispute.funds_reinstated | Funds returned after a win | The credit side of the same story | Match, close the register row |
application_fee.created | Fee object created (asynchronously by default on direct charges) | Your revenue line now exists and can be audited | Attach to charge record for step 4 of the audit |
application_fee.refunded | Fee refunded (via flag or standalone call) | Platform balance debited; seller credited | Verify proportionality against expected share |
transfer.created | Transfer to a connected account created | Seller's money is now in their balance and reachable | Start of the reachability clock |
transfer.reversed | A reversal lands (full or partial) | Money came back; amount_reversed changed | Update findings; confirm against expected figure with tolerance |
payout.paid | Payout reached the bank | Reachability for that amount ends | Mark the window closed for affected transfers |
payout.failed | Payout failed; funds return to balance | Balance still reachable after all — reversals remain clean | Prioritize any queued findings on that account |
balance.available | Funds become available | Pending became spendable | Feeds cutoff accounting (chapter 9) |
account.application.deauthorized | Connected account disconnected your platform | API recovery path closes; support/legal path remains | Freeze automated actions; route to human process |
Two wiring notes. First, events are delivered at-least-once: handlers must be idempotent, because a retried delivery that triggers a non-idempotent reversal creates exactly the double-recovery problem chapter 8 warns about. Second, event order is not guaranteed across types — a charge.dispute.closed may arrive before its created twin finishes processing; key state on object ids, not arrival sequence.