The statement showed a business doing well. A full year of trading, steady turnover, a balance that dipped and recovered the way real cashflow does, the kind of file an underwriter is pleased to see. It arrived as a PDF because the applicant’s bank sits outside open banking coverage. Our document agent read the file and returned every transaction on it. Then the verification that runs behind the agent did its work, and the document failed.
Nothing about that file looked wrong, and it still does not, if you read it the way a person reads a bank statement. Which is to say quickly, and believing it. Nothing in our pipeline reads it that way, and that is the only reason we noticed.
Why do we read statements line by line?
Fundably is a credit broker. A large part of our lender panel is unsecured lenders who make their decisions on cashflow rather than on assets, which covers most merchant cash advance and revenue-based products. Our matching engine exists to mimic each of those lenders’ underwriting criteria before we put an application anywhere near them, and that cannot be done from summary figures. A lender that cares about minimum average balance, or the ratio of credits to debits, or a particular pattern of returned payments, needs every line on the statement.
Coverage matters just as much and is easier to overlook. Our panel has more than 50 lenders on it and they do not all ask for the same window of history, so a match is meaningless unless we can say exactly what we hold. A pile of pages is not six continuous months of trading.
Which raises the fair question of why anybody is still parsing PDFs. We would rather not. Open banking is the truth: an authorised feed straight from the account, no document in the middle and nothing in it to forge. Where we can use it we do, under PSD2, and it is always the route we push an applicant towards first.
It does not reach everybody. Plenty of SMEs bank with providers open banking cannot see, or run the account that shows the real trading through something outside coverage, or hold a legacy account no aggregator ever connected. You cannot tell a business it is uncreditworthy because of where it banks, so the PDF route has to exist. But it is also the only route where a document can be altered on its way to us. It is the route that needs a gate.
We do not take the document’s word for it
Most document pipelines treat what a bank statement says about itself as data. We treat it as a claim.
The reading is done by an agent. It takes the PDF directly, with no separate OCR stage in front of it, and returns transactions against a strict output schema. It is one of a number of agents in the Fundably ecosystem, working alongside the ones that handle lender matching and credit decisioning. I built the infrastructure they run on. Like anything built on a model, the document agent is probabilistic. It is right almost all of the time, and it will sometimes be wrong.
So nothing is allowed to take it at its word either. Behind the agent sits a deterministic layer that decides whether the document holds together. It does not ask the agent how confident it feels. It works out independently whether the numbers on the page can all be true at the same time, and nothing downstream sees the extraction until that question has an answer.
The first version of that layer came from engineering paranoia rather than suspicion. It existed to check our own work, not the applicant’s honesty. If the agent misreads an amount or drops a transaction, the document stops holding together and we see the problem immediately, with no test data and nobody marking answers. As quality checks on a probabilistic system go, it is close to free and close to certain. I would have built it whether or not anybody was lying to us.
Some people were.
When the parser is right and the document is wrong
Documents the agent had read perfectly well started failing the check.
That is a disorienting result the first time you meet it. We spent a while assuming the extraction was at fault, which is the sensible first assumption when there is a probabilistic component in the loop. Usually it was. But a stubborn few kept failing where the extraction was demonstrably right, and the only explanation left was that the document itself did not hold together.
A PDF does not recalculate. A genuine statement is consistent all the way through because the bank’s systems generated it that way, without anyone having to try. An altered one has to be made consistent on purpose, and that is a great deal more work than editing the number somebody came to edit.
Cerberus, the fraud gate
The check had outgrown its original job, so it got a name. Cerberus is the three-headed dog of Greek myth that guards the gate of the underworld, and the fit was too good to pass up. Ours guards three things: the matching engine, the lender panel and the credit decision. It sits between document ingestion and everything else in the Fundably ecosystem, and nothing reaches any of the three without clearing it first. When it went live we did not just point it at new arrivals. Every PDF statement Fundably has ever received has been back through the gate.
What happens on a failure is deliberately not automatic. A document that fails is evidence of a discrepancy, not proof of fraud, and those two things get confused in this industry more often than they should. Bad scans exist. Strange layouts exist. Pages go missing. Our own agents get things wrong. So a failure stops the document and flags it to the broker handling the application, whose job is to work out what went wrong. Sometimes the statement is simply incomplete and the fix is asking for the missing months. Often it is asking for an open banking connection or fresh statements from the source. And sometimes nothing verifiable comes back and the application closes there. That last case is the one the gate turned out to be for.
What else Cerberus tests, and what it does with the answers, is not going in a blog post. It costs us nothing to say that a document has to be internally coherent, because an honest one always is. Everything past that stays in the building.
Probabilistic extraction, deterministic verification
Which brings me to the part actually worth taking away. Every probabilistic component in a system that makes decisions about money needs a deterministic invariant sitting above it. Not a confidence score, which is a model grading its own homework. An independent check that does not rely on the model at all.
Agents built on language models are very good at reading financial documents and they are not reliable the way a parser is reliable. Nothing in this class of system is right 100% of the time, and a team that tells you otherwise has not looked hard enough at its own output. The alternative is worse: a hand-written template per bank, maintained forever, breaking every time a bank changes its footer. Arithmetic, meanwhile, is not a judgement call. It does not have good days and bad days, it does not drift when we swap models underneath it and it catches our own mistakes and somebody else’s dishonesty through the same mechanism.
Taking that seriously has consequences well beyond one check, and they are where most of the work goes. You cannot manage behaviour you cannot see, so prompts, models, parameters and output schemas are version-managed and loaded at runtime, and every model call is traced. When behaviour changes, the change is attributable to a version rather than to a feeling that things got worse last week. You cannot improve what you cannot measure, so real production traces are pulled back into A/B prompt experiments and evals, which means a change is judged against documents we have genuinely seen rather than a handful of files somebody picked because they were interesting.
The unglamorous half is the same discipline pointed at operations. Retries and rate limits are handled properly, because at volume they stop being edge cases. Model routing lives in configuration rather than in code, so a cheaper model does the job wherever a cheaper model is good enough, and the layer is deliberately provider and model agnostic, because capability and cost have both moved a long way and will keep moving.
None of it makes the documents any nicer. Every institution has its own idea of what a statement is, so Barclays, Starling and Tide turn up in three unrelated shapes. Then there are multi-account statements, scans of photocopies, three date formats in one file and pages where the column boundaries are more of a suggestion than a fact. None of it is interesting and all of it has to work.
All processing is EU-resident under UK GDPR. Open banking data is handled under PSD2.
Who actually pays for this
Fraud in SME lending is not a victimless technical curiosity. A lender that advances £150,000 against a doctored statement eats the loss, and then it does what any sensible business does, which is ask everybody for more documents and take longer over every file. The honest applicant pays for that in friction and delay and never finds out why.
There is a less noble reason as well. We are the ones who hand the document over. A broker’s whole worth to a lender is that what arrives has already been checked, so passing on a forged statement would be embarrassing, and embarrassing in this business is expensive. Cerberus guards our own name before it guards anybody else’s.
Fundably sits in the flow of documents, which is a useful place to sit, whether an application comes to us directly or through an embedded lending partner. We would rather stop a bad file at our own gate than hand it on and let a lender discover it in a default two years later. If you are a lender or a platform that wants to know what a document has been through before it reaches you, we are happy to have that conversation.
