The brief
A growing fintech, 60 people, two entities, payments coming in via Stripe in five currencies and going out via three different rails. Their finance team — three people including the FD — was spending the first nine working days of every month closing the previous one. By the time the numbers were signed off, they were almost stale.
The FD wasn't looking to replace Xero. She was looking for someone who'd quietly fix the bit between Xero and reality.
What was actually broken
The same thing that's broken at most growing companies: reconciliation. Specifically, reconciliation across systems that all had slightly different ideas about what a transaction was.
- Bank vs Stripe: payouts batched, fees deducted at source, FX on top, occasional refunds dropping into the wrong settlement window.
- Payroll vs Xero: the payroll provider posted one journal a month; tax, NI, pensions and benefits-in-kind needed splitting across about thirty cost centres by hand.
- Expenses vs cards: Pleo for cards, manual claims for the rest, receipts in three places.
- Intercompany: two entities, monthly transfers, FX revaluation at month-end, manual journals.
- The dashboard: a Google Sheet, refreshed by hand, that was wrong roughly half the time.
"We weren't doing finance. We were doing data entry, very carefully, with a calculator." — Financial Director
The shape of the system
We built four things, all sitting on top of Xero rather than replacing it:
- A reconciliation engine in Python, pulling from the bank API, Stripe, Pleo, payroll exports and Xero on a schedule. For each pair of systems, a set of matching rules — exact, fuzzy, time-windowed — runs every match it can find automatically. Unmatched items land in a review queue with the most likely candidates surfaced.
- A split-and-post layer for journals that historically needed manual work — the payroll journal, FX revaluations, intercompany — using rules that codify what the FD had been doing in her head for three years.
- A close dashboard in Metabase, fed from a small Postgres warehouse that mirrors and tidies Xero data. Every metric has an "as of" timestamp. Numbers are either green (reconciled), amber (in review) or red (broken). For the first time, the finance team could see where they actually were.
- A runbook page — generated automatically each month — with the close checklist, exceptions, decisions taken, and who signed off on what. Audit-ready.
None of this was clever. All of it was careful. Finance automation rewards meticulousness over cleverness, every time.
The outcome
The dashboard is now trusted. The board pack writes itself the morning after close. The FD spends her time on the things FDs are actually paid to do — pricing models, hiring plans, investor conversations — instead of chasing missing receipts.
The finance team didn't shrink. It grew into the work it should have been doing all along.
What we learned
Finance automation is a matching problem dressed as a ledger problem.
The accounting parts of finance are mostly solved. The bit that eats time is matching transactions across systems that disagree about what a transaction is. Get the matching right and most of the close evaporates.
Make the human review queue obvious.
You will never automate 100% of reconciliations. The win is automating the 90% that are clearly fine, surfacing the 10% that need a human, and ranking the 10% so the most important ones are at the top. That's a UX problem, not an AI one.
Auditors love runbooks.
The auto-generated runbook turned out to be one of the most valuable parts of the system. Year-end audit time dropped by about a third because every question — "who approved this, when, against what evidence?" — had an answer with a link to it.
The one thing to take away
If your finance team is closing the books in weeks, you don't have a finance problem — you have a reconciliation-and-matching problem. The fix isn't a new ledger; it's the careful, boring layer that sits between the systems you already have and makes them tell the same story.