Sep 2026 · 4 min read
PublishedBuild Log #5: Teaching the App to Read One Real Bank Statement
Everything before this point — extraction, the job queue — moved bytes around without understanding a word of them. This is the post where the app reads an actual bank statement for the first time, using twelve consecutive months of my own real Santander checking statements as the test data.
Detection before parsing, never the filename
A statement's bank, account type, and layout version are detected from what's actually printed on the page, with a confidence score. Below that threshold, the statement is marked unsupported rather than run through a best-guess parser — a wrong parse that looks plausible is worse than an honest refusal.
The real statements turned up a layout wrinkle no synthetic fixture would have caught on its own: the combined PDF has a checking section followed by a savings section on the same pages. The parser reads checking only and explicitly stops before the savings section — a documented v1 scope decision, not an oversight.
- 01Transaction columns are read by the x-position of each amount on the page, not by text order — the columns sit far enough apart that position is a more reliable signal than sequence.
- 02A negative, overdrafted balance prints with its sign intact and is preserved exactly.
- 03Every parsed row is cross-checked against the running balance, and the parser's own credit/debit totals are cross-checked against the statement's own printed summary line — a mismatch there is treated as a misread, not a statement that merely fails to reconcile.
A parser that's confident and wrong is more dangerous than one that admits it doesn't know. Detection has to be willing to say "unsupported."
What the real data proved
All twelve months processed into 245 real transactions, correctly resolved to a single account — masked to its last four digits. I grepped the database directly for my real, full account number afterward. It came back nowhere. Every month's closing balance reconciled exactly against the next month's opening balance, across the entire year.
Next
Deduplication and the analytics engine — the first things that actually read what this parser produces.