termroam
TermRoam · field guide

Categorizing transactions safely

Reconciliation errors announce themselves; categorization errors do not. The design goal is not accuracy in aggregate — it is making the uncertain cases visible rather than plausible.

A three-part series. Part 1 — Reconciling books with AI agents · Part 2 — Automating a monthly close · Part 3 — Categorizing transactions safely (rules first, model second, humans on the edge)
01

The only step that needs judgement

By the time a reconciled month reaches categorization, the hard guarantees are already in place. Every transaction is accounted for, nothing is duplicated, and the list is provably complete. What remains is deciding what each one is — and that is genuinely a judgement call, which makes it the one place a model belongs.

It is also the place where a wrong answer is least visible. A miscategorized transaction does not break a balance. The books still tie, the totals still foot, and the error surfaces months later in a tax position or a margin that makes no sense. Reconciliation errors announce themselves; categorization errors do not.

So the design goal is not accuracy in aggregate. It is making the uncertain cases visible rather than plausible.

02

Rules first, model second, human on the edge

Three layers, in strict order, each handling what the one before could not.

Deterministic rules win outright

A regular expression mapping a merchant pattern to an account is not a lesser version of a model — it is a better one for this job. It is inspectable, it is stable across runs, it costs nothing, and when it is wrong you can see exactly why. Anything recurring belongs here: the cloud provider, the payroll processor, the monthly fee that has been the same for two years.

Rules also encode decisions that are dangerous to re-litigate. A payment to a corporate card platform is a transfer against a liability, not an expense — the individual card charges are the expenses. A model asked "what is this $15,000 payment to a card company" will reasonably answer "expense", and booking both sides would double the year's costs. That belongs in a rule precisely because it is the kind of thing a plausible answer gets wrong.

The model handles the long tail

Everything unmatched goes to the model in one batch, together with the actual chart of accounts, and comes back with an account and a confidence score. The important constraint is that it must choose from accounts that exist. A model inventing a plausible-sounding account name produces work rather than saving it.

Low confidence is a queue, not a guess

Below a threshold, nothing is applied. The transaction is flagged and surfaced for a person. In our run that was around 60% of the model's output — bare check numbers, unlabelled transfers, generic payment processor descriptors that genuinely could be anything.

A categorizer that labels everything has quietly converted "I don't know" into a wrong answer. The queue is the feature.

The layers also feed each other in one direction. Every review decision is a candidate rule: if a person has resolved the same merchant twice, it should never reach a model again. Over a few months the model's share shrinks and the deterministic share grows, which is the right direction for cost, speed and auditability at once.

Transfers need no category at all

Worth stating because it removes a large fraction of the queue. Moving money between two accounts you own is not income and not an expense — it is the same money in a different place. Reconciliation has already identified these, so they should never reach a categorizer, and a model asked to categorize one will invent something rather than say "this is not a category question".

03

What a flagged transaction looks like

The simulation below shows a batch coming out of categorization. Adjust the confidence floor and watch what moves between "book automatically" and "review". The point is the trade: a lower floor means less review and more silent error.

Categorization batch — illustrative transactions
DateAmountDescriptionProposedConfAction

All transactions and figures here are invented for illustration.

Notice which rows never move. A recurring cloud bill matched by rule sits at full confidence regardless of the floor, because no model was consulted. A payment to a card platform is likewise fixed — as a transfer, not an expense. The floor only governs the genuinely ambiguous middle, which is where it belongs.

The floor is also a business decision rather than a technical one, and it should be set by how much a wrong answer costs. Categorizing office supplies wrongly is noise. Categorizing a capital contribution as revenue is a different kind of problem entirely. Where the two live in the same queue, set the floor for the expensive case.

04

The double-count, which balances perfectly

The most expensive categorization error we found does not break anything. It books the same spending twice, the accounts still tie, and the only symptom is that the year's costs are roughly double what they should be.

It happens whenever a card is settled from a bank account. The bank feed shows one payment to the card issuer. The card feed shows every individual charge. Both are real transactions and both belong in the books — but only one of them is an expense. The other is a transfer that reduces a liability.

Toggle the treatment below.

One month, one card, two treatments

All figures here are invented for illustration.

Nothing about the wrong version looks wrong. The bank reconciles, the card reconciles, every transaction has a source and a category. A reviewer scanning the queue sees a payment to a recognisable card company and approves it without hesitation, because in isolation it is obviously a business cost.

The errors reconciliation cannot catch are the ones where both entries are real. Those need a rule, written once, by someone who understands the flow of money rather than the individual transaction.

The same shape appears with payroll providers, payment processors that settle in batches, and any account funded by sweep. In each case a gross figure and its components are both visible, and booking both doubles the total. It is worth listing these deliberately when you set up the rules, because every one of them will otherwise be discovered the hard way.

05

Categorization is where the tax questions surface

Two classes of decision emerge here that no amount of reconciliation will reveal, because both involve transactions that are already correctly recorded.

Contractor payments and information returns

Payments to individuals and unincorporated businesses for services may require an information return at year end, and gathering the paperwork is far easier during the year than the following January. Scanning categorized spend for likely candidates is cheap and mostly mechanical.

One subtlety worth encoding rather than rediscovering: payments made by card or through a third-party network are reported by the processor, not by you. A contractor paid entirely by card is generally not your filing obligation; the same contractor paid by bank transfer may be. A tool that aggregates a vendor's total spend without separating the rails will produce a list that is confidently wrong in both directions.

Instruments that look like expenses and are not

Investor funds arriving, loans, capital contributions, and instruments that convert later all land in a bank feed looking like ordinary inflows. Their classification is a real accounting decision with balance sheet consequences, and it is not one a categorizer should be making silently.

These belong in the review queue by construction, not by confidence score. A large inbound wire from an unfamiliar counterparty is not a low-confidence expense — it is a question for whoever knows what the money was for, and no amount of context in the prompt changes that. The right behaviour is to route it, not to guess well.

A related trap: an instrument classified once, early, tends to stay classified. If the first month books an investor inflow to a liability account and nobody revisits it, the rule engine will keep booking it there, and by year end the balance sheet is confidently and consistently wrong. Decisions with balance-sheet consequences deserve a second look before they become a rule.

06

Review is a grid; posting is one path

The review step wants a spreadsheet, not a terminal. Rows, an editable account column, multi-select, bulk-apply. Reviewing eighty flagged transactions one prompt at a time is how a queue stops getting cleared.

But the surface that reviews should not be the surface that writes. In our setup the grid produces an approved batch and a single command-line path does the posting, because that path already holds every safeguard worth having: it is dry-run by default, it books only rows the reconciler proved were missing, it stamps each created entry so imports are identifiable and reversible, and it refuses input that does not balance.

Two ways to write to the ledger means maintaining those guarantees twice. The failure mode of getting it wrong is duplicate entries in books that feed a tax filing.

The cost is that posting requires a second action rather than a button in the grid. That is a few seconds, against a class of error that is genuinely painful to unwind — reversing a batch of duplicate entries in a live ledger is slow, visible, and lands in the audit trail forever.

Stamp everything you create

Every entry written by a tool carries a marker identifying the batch that produced it. That one field turns "undo the import" from an archaeology exercise into a query. It also makes the next reconciliation honest: an entry the tool created and an entry a person created are different kinds of evidence, and being able to tell them apart is worth more than it costs.

Dry run by default, and mean it

The posting path prints exactly what it would create and exits without writing unless explicitly told otherwise. Not because the code is untrusted, but because the input might be: an approved batch assembled from the wrong month, or a file edited by hand between review and posting. The dry run is the last place a person sees the actual entries before they exist.

07

How TermRoam handles this

Categorization is bursty. A month's queue is an hour of decisions that rarely happen in one sitting — some at a desk, some on a phone, some a week later when the answer to "what was this wire" finally arrives.

TermRoam keeps that work in a session that survives all of it. The batch you were halfway through is still there, the agent you had digging into one vendor is still running, and nothing needs reconstructing because a laptop closed.

Start of the series: the mental model and the proof.