Projects / Demo

Transaction Categorisation

Demo · All data is synthetic

I built this demo around the month-end job of tagging every line in a business's card and bank feed so the books make sense. Having worked on card programs, first as a software engineer at i2c and now as a fintech/payments PM, I know most of the feed is easy, the same SaaS tools and the same ride apps every month, and the real risk is in the few lines that look normal but aren't: a double charge, a refund that gets counted as income, or a transfer to savings that shows up as spend. So the workflow works in a fixed order. It first matches the messy card descriptor to a known merchant, then looks for keywords like a Stripe payout or payroll, then falls back to the card's merchant category code, and anything still unknown goes to a person instead of being guessed. Each line records which rule tagged it, and duplicates, refunds, internal transfers, and large payments get flagged. The rules live in a JSON file a bookkeeper can edit. I made up 40 transactions with the answer each one should get, and a check script confirms the workflow gets all of them right. It's a demo on synthetic data; going live means pointing it at Stripe, Plaid, or a bank export and sending the results to QuickBooks, Xero, or a Sheet, with an LLM optionally suggesting categories for the leftover lines.

How it works

Manual / daily trigger → Read + Parse rules.json → Read + Parse transactions.csv → Categorise & Flag (Code) → categorised.csv + summary.md

Rules, in order

1. Merchant

The messy card descriptor (AMZN Mktp US*2K4L19, GOOGLE *Workspace) is matched to a known merchant, which maps to a category.

2. Keyword

Patterns in the description, such as STRIPE PAYOUT, PAYROLL or TRANSFER TO SAVINGS.

3. MCC

The card network's merchant category code, by range. For example, 3000-3299 (airlines) and 3500-3999 (hotels) go to Travel.

4. Needs review

Nothing matched, so the line is left for a person instead of guessed.

Flags

possible_duplicate (same descriptor and amount within 2 days), refund (netted against spend), internal (transfers between own accounts), large (1,000 or more) and review.

Check

40 made-up transactions, each with the answer it should get. The check script passes 40 of 40 on category, method and flags. That checks my own seeded cases, not accuracy on real data.

Demo

A short walkthrough of the demo, made from the screenshots below, not a screen recording.

Screenshots

The n8n workflow canvas
The n8n workflow canvas
A successful run
A successful run
The categorised ledger, with the rule that matched and flags
The categorised ledger, with the rule that matched and flags
The month-end summary and the check result
The month-end summary and the check result

Stack

n8n 2.8.4 (self-hosted, SQLite), JavaScript in the Code nodes, and Python 3 for the synthetic data and the check script. Categories live in a JSON rules file a bookkeeper can edit.

This is a demo on synthetic data. The 40 transactions were made up for the demo, and no real accounts, API keys or paid services are used.