It watches the inbox, pulls the attachment, reads the document — scanned or not — and turns it into one clean row with the vendor, the dates, the line items, the tax, and the total. Then it checks its own arithmetic, and anything that doesn't add up is flagged instead of exported.
Two ways in, one queue, and a check at the end that decides whether a row is finished or needs a person. The original never leaves the record.
two ways in
then, one queue
The system connects to a Gmail or Outlook mailbox and keeps watching it. When a message lands with a PDF, JPG, or PNG attached that looks like an invoice or a receipt, it's pulled and queued on its own. This is the step that makes the whole thing hands-free — the alternative is a human remembering to check a folder, and the folder always wins.
Gmail API · Microsoft Graph · attachment detectionNot everything arrives by email. Paper that got scanned, a photo of a receipt taken in a car park, a batch someone exported from a portal — all of it can be dragged straight into the web interface. Same queue, same treatment from here on.
web upload · PDF, JPG, PNG, scansDocuments with a real text layer are read directly, which is fast and lossless. Anything scanned or photographed goes through OCR instead, with image preprocessing first, and comes back as text with its position on the page attached — because on an invoice, where a number sits is half of what tells you what it is.
PyMuPDF · Tesseract OCR · Pillow preprocessingThe text becomes a structured record: vendor, invoice number, invoice date, due date, line items with descriptions and amounts, subtotal, tax and tax rate, total, currency, payment terms. This is where the format chaos gets absorbed — "Net 30" and an actual date are the same field, tax buried in a total is still tax, and anything genuinely ambiguous carries a confidence score rather than a confident guess.
Claude API · structured extraction · confidence scoringDates get one consistent format, currencies get identified and labelled, and the arithmetic gets checked: line items should sum to the subtotal, and subtotal plus tax should equal the total. When they don't, the record is flagged rather than exported. This is a deterministic rules pass, not a second opinion from the model.
rules engine · reconciliation, dates, currencyEach invoice becomes one row — written to a CSV or pushed straight into a connected Google Sheet, with flagged records highlighted so a person knows exactly which handful to look at. The source document is stored alongside the record, so any figure can be traced back to the page it was read from months later, when someone asks.
Google Sheets · CSV · originals in S3 or local storeA model can misread a digit on a blurry scan — any extraction system can. What it can't do is make a wrong number add up. Line items that don't sum to the subtotal, or a subtotal and tax that miss the total, catch the error without anyone reading the document. That's why the validation pass is plain deterministic rules: it's the part that has to be right when the reading isn't.
One row per invoice, twelve-plus fields, in the same shape every time regardless of how the vendor laid the page out.
The identity of the document — who billed you and their reference for it. What you need to match a payment to a bill, and to spot the same invoice arriving twice.
Normalised to one format regardless of how it was written. "Net 30" is resolved to a real date, so a due-date column can actually be sorted.
Each line captured with its description and amount rather than collapsed into a single total, which is what makes the record useful for anything beyond paying it.
Tax is broken out even when the vendor buried it in the total, and the currency is identified rather than assumed — which matters the first time an international invoice lands.
Ambiguous fields carry a score and reconciliation failures carry a flag, so a finance team reviews the handful of documents that need it instead of spot-checking all two hundred.
A model in the middle for reading, deterministic rules on either side of it.
Watches a mailbox for attachments and accepts direct uploads for everything that doesn't arrive by email. Both feed one queue.
Direct text extraction where there's a text layer, OCR with preprocessing where there isn't. Same output either way.
Turns messy page text into the same set of fields every time, and scores its own confidence where a field is genuinely ambiguous.
Normalises dates and currencies and reconciles the totals. Deterministic by design — this is the layer that catches the reading being wrong.
One row per document into the sheet or file the business already works in, with flagged rows highlighted, and the source document retained so every figure stays traceable.
Six modular blocks. Mailbox, export target, and storage swap per client; the queue and the check don't.
Spots invoice attachments and queues them.
Text layer when there is one, OCR when there isn't.
Page text becomes the same fields every time.
Checks the maths, normalises dates and currency.
One row per invoice, straight into the sheet.
Keeps the original beside the record it produced.