Projects / Demo
Stripe Payout Reconciliation
n8n demo · All data is synthetic
I built this as a small demo to work through a problem I know well from payments: proving that the money a processor says it paid out actually reached the bank. I spent years as a software engineer at i2c working on card programs, and now I work as a fintech/payments PM. In both roles, reconciliation is where the edge cases show up: deposits that land a few days late, fees taken twice, missing credits, duplicates.
I generated Stripe-style payouts, balance transactions, and a bank statement, then seeded each of those cases on purpose. Next I built an n8n workflow that matches payouts to bank lines by amount and a date window, labels each exception with a plain reason, and writes out a report and a short summary. A check script confirms that it catches exactly the cases I planted. All of the data is synthetic and this is a demo, not a production system, but it shows how I think about matching rules and exception handling, and why it's worth explaining every exception rather than just counting them.
How it works
A Stripe payout and the bank deposit it creates often don't line up cleanly. Deposits land a day or more after the payout date, fees get deducted twice, a deposit goes missing, or the bank shows the same credit twice. Finance teams usually catch this in a spreadsheet. This workflow does the matching in n8n and writes a report that flags each exception with a reason.
Manual Trigger → Read/Parse Payouts CSV → Read/Parse Balance Transactions CSV → Read/Parse Bank Statement CSV → Reconcile (Code) → [Report CSV, Summary .md]Matching rules
Only positive bank credits whose description contains “STRIPE” are considered. Each bank line can be used once; the closest date wins.
MATCHEDThe bank amount equals the payout amount and the bank date is within ±2 days of the payout arrival date.
TIMING_DIFFERENCEThe amount is exact, but the bank date is 3–7 days away from the payout arrival.
AMOUNT_OFF_BY_FEEThe bank amount differs from the payout by exactly the payout fee, within ±2 days.
MISSING_DEPOSITNo bank credit found for this payout.
DUPLICATE_DEPOSITA bank line that repeats an already-matched amount within the date window.
UNMATCHED_BANK_CREDITA Stripe-labelled bank credit with no matching payout.
LEDGER_MISMATCHThe sum of charge net values in the balance transactions doesn't equal the payout amount.
Demo
A short screen recording of the workflow running in n8n on synthetic data.
Screenshots
From the planning board to the final output.




Stack
n8n 2.8.4 (self-hosted via npm, SQLite), Node 20, Python 3 for data generation and checks. The file paths in the workflow are absolute; edit them if you move the folder.
Optional LLM step
The canvas has a sticky note marking where an AI node could go, for example the Ollama Chat Model node running a free local model. It would turn exception rows into plain-English notes. It isn't wired in, so the demo runs without any key.
Seeded test cases
The synthetic data includes deliberate mismatches to verify the reconciliation logic:
po_005is missing from the bank statementpo_007is off by exactly its feepo_009has a duplicate depositpo_011lands 4 days late (timing difference)
A verification script confirms the output reports exactly these cases.
Resources
This is a demo built with synthetic data. No real Stripe account, bank, or customer data is used, and no paid service or API key is needed.