Case study · Neoflo · In production

We tested six vendors before we built anything.

Every one of them got worse on the documents that actually arrive.

RoleHead of Product
Live sinceNov 2025
Volume6,000+ invoices a month
MarketsSix countries, six tax logics
Extracted0 / 14
Invoice number98%
Invoice date
Due date
PO number
Payment terms
Vendor name94%
Vendor address
VAT number
Buyer name
Buyer address
Subtotal
Tax / VAT
Total amount96%
Currency
0 posted0 to a person

One invoice. Recorded scores only. Currency has no bounding box because it isn't printed on the page.

92.5%Field accuracy, first of six platforms
97.6%Coverage, the highest of the six
87.8%On handwriting, where three fell below 56%
$0.0091Measured extraction cost per invoice

The problem

An extraction error isn't a typing error.

Every finance workflow starts by reading a document. Somebody opens an invoice and types the fields into an ERP. Vendor, dates, amounts, tax, terms.

That somebody had a name. In my first week I sat and watched her work. Forty-seven browser tabs, three spreadsheets, a legacy ERP, and a folder on her desktop called INVOICES_FINAL_FINAL_v3. She wasn't bad at her job. She was good at it. The software had never been built for the way she worked, so she'd built around it.

Get one wrong and it doesn't cost a retype. It propagates. A misread invoice number fails the downstream purchase order match. It surfaces three weeks later, in somebody else's reconciliation.

So the question was never how fast a machine reads. It was how often it's wrong, and whether it knows.

Everything below follows from that second clause.


The benchmark

Buying was the cheaper answer. So we ran the test the vendors had not.

Commercial document processing has existed for years, the vendors publish high accuracy numbers, and those numbers are real. They are measured on clean documents.

What arrives in an AP inbox is a phone photograph at an angle. A fax of a scan. Handwriting. Vietnamese, Bahasa, Chinese. Excellent on a clean PDF and poor on a photograph isn't excellent. It's untested.

BENCHMARK · 200 INVOICES · 15 FIELDS · 6 PLATFORMS Every platform got worse on handwriting. We dropped five points. Neoflo IDP Five commercial platforms, B to F 87.8, our handwritten score 0 20 40 60 80 100% 93.2 Digital 93.5 Scanned 89.5 Multilingual 87.8 Handwritten Axis from zero. Competitors ordered B to F, left to right, consistently across every group. The dashed line marks 87.8, our handwritten score. No platform beat it in any category.
BENCHMARK · 200 INVOICES · 6 PLATFORMS Every platform got worse on handwriting. We dropped five. DIGITAL Neoflo IDP 93.2 Platform B 87.3 Platform C 82.1 Platform D 80.7 Platform E 78.4 Platform F 75.9 SCANNED Neoflo IDP 93.5 Platform B 86.9 Platform C 80.4 Platform D 80.1 Platform E 76.8 Platform F 72.9 MULTILINGUAL Neoflo IDP 89.5 Platform B 86.4 Platform C 78.6 Platform D 75.5 Platform E 79.5 Platform F 69.1 HANDWRITTEN Neoflo IDP 87.8 Platform B 74.6 Platform C 54.7 Platform D 55.8 Platform E 64.6 Platform F 50.8 Axis from zero. The dashed line marks 87.8, our handwritten score. No platform beat it in any category.
200 real invoices, 15 fields, 6 platforms, 13,308 scored combinations against a key our finance team verified by hand. Every platform at its default settings, on the same field schema, scored exact match.

What decided it

Not the highest number. The smallest drop.

Every platform we tested got worse on handwriting than on its own clean documents. One dropped 27 points against itself. We dropped five.

Neoflo IDP
Clean PDF93.2
Handwritten87.8
5.4 ptYou don't get to choose which invoice arrives on a Tuesday.
The steepest commercial drop
Clean PDF82.1
Handwritten54.7
27.4 ptThree of the five sat between 50% and 56% on handwriting.

Our worst category beat every platform's best. We scored 87.8% on handwriting. No commercial platform reached higher than 87.3% on anything, including their clean digital PDFs.


How it's measured

Neither number means anything alone.

The benchmark's two headline figures aren't the same kind of thing. Naming them precisely is the difference between reporting a result and understanding one.

Benchmark termWhat it countsIn ML terms
Accuracy, 92.5%correct ÷ applicablerecall
Coverage, 97.6%attempted ÷ applicableattempt rate
94.8%, derivedcorrect ÷ attemptedprecision

The gap between the first two is 5.1 points, the smallest of any platform tested. Almost everything we attempt to extract turns out correct.

They pull against each other. You reach 100% precision by only answering when certain, and coverage collapses onto the AP team. You reach 100% recall by always guessing, and precision collapses into bad data in the ERP.

The confidence gate is the mechanism that trades one for the other, deliberately.When the system is unsure it declines to commit, converting a would-be precision failure into a coverage failure. A blank and a person, which is the cheaper of the two.

So the headline isn't 92.5%. It's that the 7.5% we got wrong, we flagged.

Two evals, and both are correction rate. Offline against the golden set, as a regression gate on every model change. Online against live traffic. The same metric on both sides. A regression in testing is directly comparable to a drift in production.

And every quality metric needs a counter-metric, or it gets gamed.

Accuracy coveragestops you buying accuracy by abstaining on the hard fields
Straight-through escape ratestops you buying throughput by loosening the gate
Review rate correction ratestops you mistaking workload for usefulness

Escape rate is the one we don't have. Everything measured today is work the system knows it did. Nothing measures what it got wrong without noticing. Until that exists, confidence that the gate holds is an assertion, not a number.

Calibration is the other gap. Stated confidence plotted against observed correctness, per field. We have never drawn that curve. The currency finding below is what its absence looks like from the outside.

The architecture

Four of the eight stages run no model at all.

The organising idea isn't the order. It's the cost. Every stage is built to avoid paying for the next one. The expensive model is fenced into a single place that almost never fires.

01

Junk filter

four size and shape rules

Logos, signatures and tracking pixels die before anything is paid for.

No model
02

Digital or image

PyMuPDF and pdfplumber

Both try to pull the text out. The ratio they recover decides it. A rule, not a classifier.

No model
03

Document type

keyword, top 30%, then all

Three tiers. Each pays only for what the tier before could not settle.

Cascade
04

Extraction

PaddleOCR, our own GPUs

Every field returns a value, a bounding box, and a confidence score.

No per-page cost
05

Verification

Sonnet, one token out

Asked is this right. Never what is the right value. Payload is the bounding box, never the page.

Cheap · all fields
06

Repair

Sonnet, different prompt

Only where the check failed. Single shot, no retry loop.

Expensive · rare
07

Arithmetic

plain code

Line totals against header totals. A bigger model can't tell you which number is the lie.

No model
08

Review gate

per-field threshold

Configurable per tenant and per workflow, as is the field schema itself.

No model

Scroll the pipeline sideways

runs no modela model, no per-page costcheap, runs on everythingexpensive, runs almost never

Three decisions I would defend

01

The most expensive model reads every field, and answers in one word.

Input tokens cost less than output tokens. So the judge is asked is this right, not what the right value is. Checking everything becomes affordable because the answer is one token. Repair stays expensive, and fires only where checking failed. Most systems trade coverage against cost. Constraining the output removes the trade.

02

Arithmetic failures never reach a model. They go to a person.

An inconsistency is localisable but not resolvable by re-reading. If the line items sum to 40 and the header says 44, a higher-fidelity read tells you nothing. It can't say which of the two is the lie. That isn't escalating hard cases to a bigger model. It's knowing which hard cases a bigger model can't help with.

03

Review is a product surface, not an error path.

A reviewer sees one uncertain field, highlighted where it sits on the page. They correct it. That correction fixes the document, measures the system, and produces a training label. One action, three jobs. Which is why we could ship before accuracy was good enough.

A purchase order number inside its bounding box on the invoice with a 97% confidence score, beside the extracted data panel

What the reviewer sees

The bounding box is the whole product.

Extraction returns three things per field. The value, the bounding box it came from, and a confidence score per character.

The confidence score decides whether a person ever sees it. And the bounding box is why a reviewer sees one uncertain field in context. Not a whole document to re-read.

Nothing here depends on the model being right. It depends on the model knowing when it isn't.

The finding I didn't expect

Our most accurate field is the one we send to a person most often.

Currency scores 98.4%, the highest of the fifteen. A reviewer sees it more than any other field. Both are true.

The currency is usually not written on the invoice at all. It's worked out from where the invoice was billed. An invoice sent to the Philippines is in pesos, and nobody writes Philippines on an invoice. So the model is deducing, not reading. It's correctly less sure about a deduction than about something it can see.

That's right as behaviour and wrong as a threshold. Not an accuracy problem. A calibration problem.

The currency field marked as Suggested, proposed from the bill-to address
Where the system infers rather than reads, it says so. A suggestion is a different object from an extracted value, and the reviewer can see which is which.

The hardest problem

It was never extraction.

One of these was an invoice. Nothing downstream will ever know.

It was deciding what a document is, and what it belongs to. An invoice, a purchase order, a tax document and a delivery note all carry the same shape. A vendor, a number, a date, amounts. In Indonesia the tax document is itself an invoice, legally distinct, with its own number format, and a required relationship to the commercial one. A single email arrives with five attachments and no indication of which belongs to which.

And the failure mode is the worst kind: silent. Every other error here is loud. A low confidence score, an arithmetic mismatch, a model refusing to answer. A misclassified invoice produces no error at all. It's processed successfully as something else, so nothing downstream ever learns an invoice arrived.

Nobody chases a document they don't know exists. It surfaces weeks later as a vendor asking where their money is.

"Irrelevant" is a terminal state. So it should require the most confidence, not the least.

What it still can't do

The part that makes the rest believable.

01
The answer key is incomplete, and the report says so.

Ground truth was compiled incrementally, so not every invoice carries a value for all fifteen fields. Scoring runs only over the fields that were annotated.

A blank doesn't mean the field was absent from the document. It may mean nobody recorded it yet. So the aggregate over 13,308 scored combinations is solid, and the thinnest per-field results aren't. Publishing confidence intervals per field would cost nothing and would say which differences are real.

02
The checker only looks where the scanner pointed.

A field never found has no bounding box, so nothing checks it and nothing flags it. We catch a wrong answer. We don't catch a missing one.

03
There's no end-to-end accuracy number.

92.5% measures reading, on documents already known to be invoices. Of everything entering, what share comes out completely correct is necessarily lower, and it isn't measured.

04
The rule deciding PDF or picture has never been benchmarked.

One ratio, costing almost nothing, deciding which reader wins a disagreement. No measurement would show it going wrong. The piece nobody measured is the one that looked too simple to need it.

What comes next

Four things, and none of them is a bigger model.

The two measurement gaps above, escape rate and calibration, are the first two items on the list. These are the four build items behind them.

Product, architecture and benchmark design: mine. Modelling: our data science team.

Working on something that isn't obvious yet?
Let's build it.

Shubham Shrivastava
Shubham ShrivastavaHead of Product · Neoflo.ai
© Shubham Shrivastava 2026