paperflow.
Invoice & receipt extraction · confidence routing · a real eval

Invoices in. Verified ledger data out.

paperflow turns invoices and receipts into structured, checked data — and tells you exactly which ones a human still needs to look at.

Built on one idea: on a cheap, fallible model, the pipeline carries the reliability, not the model. It extracts the fields an accountant needs, checks them against the document's own arithmetic and its source text, scores its own confidence per field, and routes only the shaky ones to a fast human-review UI. Everything runs on free GLM flash-tier models through any OpenAI-compatible endpoint.

118
documents in the eval corpus
94.8%
overall field accuracy
78.0%
auto-accepted at 98.1% accuracy
~868
tokens / document
The problem

"Just use an LLM" gets you 90% — then quietly hurts you.

A bookkeeping team receives invoices as email PDFs, flatbed scans, and photos texted from a job site. Someone retypes each one. It's slow and the errors are expensive: a transposed total pays the wrong amount; a misread vendor pays the wrong company.

A flash model will confidently return a clean-looking JSON object with a wrong number in it, and nothing downstream knows. The hard part isn't extraction — it's knowing which extractions to trust. The design goal: auto-accept the documents the pipeline can verify, route the rest to a human, and measure honestly how often "auto-accept" is actually right.


How it works

One pipeline, from upload to ledger.

The same pipeline.process() runs for every upload and every eval fixture — so the eval measures exactly what production runs.

01

Ingest

Digital PDFs read their text layer; scans and photos are rendered and OCR'd (Tesseract, Otsu binarize).

02

Extract

An OpenAI-compatible / GLM flash call pulls fields and line items, with a brace-balanced JSON repair retry.

03

Normalize

Amounts, dates, and currency are resolved to canonical form — locale-aware, so 1.234,56 doesn't get divided by 1000.

04

Validate

Arithmetic reconciliation, required-field checks, ISO dates. A non-reconciling invoice is a warning, not a false error.

05

Score

Per-field confidence blends model self-report, source grounding, and arithmetic corroboration.

score ≥ threshold · grounded · reconciled

Auto-accept — no human needed. In the eval, 78% of documents land here at 98.1% field accuracy.

shaky · ungrounded · mismatch

Human review queue — the document opens beside its fields with the low-confidence value and any arithmetic mismatch flagged for a fast fix.


The interface

A ledger you can trust at a glance, and a review view built for speed.

Animated demo — upload through auto-accept routing, human review of an arithmetic mismatch, and approval
The full loop — upload, auto-accept routing, a human reviewing an arithmetic mismatch, approval.
The paperflow document ledger — every processed document with its status stamp and confidence bar
The ledger — every processed document with its status stamp and confidence bar.
The review view — a document beside its extracted fields, with the arithmetic mismatch flagged and the low-confidence field marked
Review — the document beside its fields, arithmetic mismatch and low-confidence field marked.

Eval results

Honest, reproducible numbers — not a hand-picked demo.

Every number here came out of python -m evals.run over 118 deterministic synthetic fixtures: clean PDFs, scans, skewed phone photos, multi-page invoices, and deliberately nasty edge cases. Model: glm-4.5-flash (free tier), reasoning disabled.

CategorynField accuracyFully correctAuto-accept
clean_pdf30100.0%30/30100.0%
multipage12100.0%12/12100.0%
edge2497.9%20/2475.0%
scan2894.9%13/2871.4%
photo2482.3%2/2450.0%
The number that matters for a bookkeeping workflow isn't raw accuracy — it's the material false-accept rate: of the documents auto-accepted without a human, how many had a wrong vendor, date, amount, or currency. That's 11 of 92 (12.0%) at the default threshold, and it dials toward zero as you raise the confidence bar (to 4 at threshold 0.95, trading away automation). The floor is genuine ambiguity — a bare $ that could be USD or CAD — that no amount of model quality resolves from the page. That's why it isn't 100%, and why claiming 100% would be a red flag.

How confidence works

A flash model's self-confidence is a weak signal. So it's never trusted alone.

The failure that matters most is a model returning numbers that are internally consistent but absent from the document. The confidence score blends three signals to catch exactly that:

01 · weak prior

Model self-confidence

The model's own per-field certainty — used only as a weak prior, because these models are cheerfully overconfident.

02 · the anchor

Source grounding

Does the value actually appear in the document text? A total the model is sure of but that appears nowhere is capped low — this is what stops a confident hallucination from auto-accepting.

03 · corroboration

Arithmetic agreement

Line items reconciling to a subtotal/total is evidence the numbers agree with each other — but only boosts fields that are also grounded, and only for non-trivial reconciliations.

A document auto-accepts only when its weighted score clears the threshold, no validation error is present, and the required fields (vendor, date, total) are each present, grounded, and above a floor — so a high average from reconciling financials can't carry a wrong vendor over the line.


Run it yourself

Clone it, drop in an invoice, watch it route.

Requires Python 3.11+ and (for scans/photos) Tesseract. Digital PDFs work without it.

# get the code
git clone https://github.com/lordbasilaiassistant-sudo/paperflow.git
cd paperflow
python -m venv .venv && . .venv/bin/activate
pip install -r requirements.txt

cp .env.example .env        # then add your key (any OpenAI-compatible endpoint)
uvicorn app.main:app --reload   # open http://127.0.0.1:8000

# see the eval
pip install -r requirements-dev.txt
python -m evals.generate    # build the 118-fixture corpus
python -m evals.run         # run the pipeline and print the report

Want it live on the web instead of local? It ships a Dockerfile with Tesseract baked in, so it runs free on Hugging Face Spaces or on Cloudflare Containers — see the deploy guide. Note: the app has no auth, so put any public deployment behind access controls before running real documents through it.