Financial reconciliation platform
Five reconciliation checks over one set of books, recomputed on every write
A multi-tenant platform giving finance administrators a continuously reconciled view of their position. Transactions arrive from file uploads, OCR'd invoices and a nightly sync from the institution's own accounting system; five different checks run over them and write tracked exceptions. The interesting problem is that these five checks disagree, and the design has to let them.
- Built for
- Employer product — name withheld
- My role
- Full-stack feature development on the reconciliation engine and the financial position surfaces, in a monorepo shared with a small team.
- When
- 2026
- Next.js
- React
- TypeScript
- tRPC
- Firebase Cloud Functions
- Firestore
- Turborepo
- Zod
What made it hard
The problems worth reading about. Everything else in this system was ordinary work.
- 01
Five checks run over the same records and answer different questions: is a department inside its approved budget, do related revenue and expenses tie out, are the records clean, does our view agree with the institution's general ledger, and are approved expenses actually paid at the amount approved.
- 02
They produce tracked exceptions rather than blocking writes. A reconciliation engine that refuses data it cannot reconcile is useless to a finance office, because the whole reason they need it is that the data is already inconsistent. The output is a worklist, not a validation error.
- 03
Money is tracked through a lifecycle — committed, then spent, then paid — instead of as a single amount. Those are three genuinely different states, and collapsing them is what produces a budget that looks fine until the invoices land.
- 04
An AI pass suggests object codes for records that look miscategorised, and never applies them. A suggestion a human accepts is a workflow; a silent recategorisation inside someone's books is a liability.
What it measures
Each figure says where it came from, so you can judge how much weight to give it. Some are measurements and some are chosen thresholds; the basis line tells you which.
- 5
- Reconciliation checks
- 18
- Tracked exception types
- 0
- AI categorisations applied automatically
Budget vs actual, revenue-to-expense tie-out, data quality, general-ledger match, and three-way invoice/expense/payment match.
Distinct exception types the five checks can raise, each separately triaged.
Structural: the AI pass annotates records with a suggested object code and the suggestion requires human acceptance. Same reasoning as confining an assistant's write access elsewhere in this portfolio.
The longer version
Why exceptions rather than validation
The instinct with financial data is to validate on the way in and reject what does not balance. That instinct is wrong here, and understanding why is most of the design.
A finance office adopting a reconciliation tool already has inconsistent records — that is why they are adopting it. A system that refuses the inconsistent ones imports a clean subset and leaves the actual problem outside the tool. So every check writes exceptions instead: typed, tracked, assignable, and countable. The product is the worklist.
The part that is genuinely hard
The five checks are not independent, and they can contradict each other. A record can be inside budget, tie out against revenue, and still fail the general-ledger match — and which of those an administrator should act on depends on context the system does not have. Most checks recompute on every write so the position is always current; the general-ledger tie-out is heavy enough that it runs on demand and after a journal import instead.
Deciding which checks are cheap enough to be continuous and which have to be explicit is the same shape of problem as splitting risk limits between an order path and a supervision loop, elsewhere in this portfolio: the cost of evaluating a rule determines where the rule is allowed to live.
A note on this write-up
Built for an employer; the product and institution are not named. The engineering described is my own work on it.