commit 662cfe8 2026 AI & automation Computer & LLM vision

Invoice and Receipt Intake: n8n + Claude with Human Approval

Invoices and receipts (PDFs, scans, phone photos) come in by email or an upload form. Claude reads them, the workflows flag missing values, wrong totals and duplicates, and an approver approves, rejects or corrects each one from an email, without an account. All of it runs on n8n: no server, no database, nothing else to host. I built it as a proof of concept against a prospective client's written brief.

role
Design & implementation · 10 n8n workflows, tests, documentation
client
prospective client · name withheld · built against their written brief
sector
Finance operations · accounts payable
stack
n8n · Claude API · JavaScript · Google Sheets · Google Drive · IMAP / SMTP · Gmail
hosting
n8n only · Cloud or self-hosted · no server, no database
timeline
October 2026 · two days from the brief to a live test on n8n Cloud
status
● proof of concept · every acceptance test in the brief passes
01 / The brief

Read, check, then ask a person

The brief described a small finance workflow. Invoices and receipts arrive by email or through a form, as PDFs, scans or phone photos. The system reads the vendor, number, date, amounts and tax, checks for missing values, wrong totals and documents that were already processed, and emails each one to an approver with the original attached. Approved documents go to a Google Sheet, rejected ones and duplicates to a separate list, and the owner gets an email when anything fails.

Two lines in the brief shaped most of the design: do not silently guess missing values, and the approver must be able to approve, reject or correct without creating an account.

02 / How it works

All on n8n, nothing else to host

No server, no database, no file host. The upload form is an n8n form, the review page is served by an n8n webhook, the ledger is an n8n Data Table, the signing secret for approval links is an n8n credential, and the watchdog is an n8n schedule. Outside n8n there is only what the job needs anyway: a mailbox, Google Sheets and Drive, and the Claude API.

diagram · data flowdashed box: everything that runs on n8n
n8n · cloud or self-hosted · no other server mailbox imap · gmail claude api reads pdfs and photos approval email original + signed links upload form pdf · scan · photo process document checks · duplicates review page approve · reject · correct watchdog hourly · error alerts ledger n8n data table owner email failures · digests google sheets approved · exceptions · log decision from the ledger
no server, no database, no file host · outside n8n: the mailbox, google sheets and drive, the claude api · the sheet is written from the ledger, never the other way round

Running costs are the n8n plan plus Claude usage, about 5 cents per document with Opus and less with Sonnet or Haiku.

On n8n Cloud there is nothing to host at all: no server to patch, no database to back up. A self-hosted n8n works just as well.

Ten workflows: two intake triggers, the processing pipeline, the review page, the watchdog, an error workflow, a config workflow and three small adapters. Every outside service goes through exactly one adapter, so retries live in one place and a backend is a config switch (Gmail or IMAP/SMTP, Google Sheets or n8n Data Tables, Google Drive or local disk).

The system of record is the Data Table, not the Sheet. Google Sheets has no atomic update and a request quota, and people edit it by hand. The ledger also keeps what the Sheet never shows: per-field confidence, extracted versus corrected values, link state.

03 / Reading documents

Claude reads, and never guesses

There is no separate OCR service. PDFs go to Claude as documents, with the text layer and the page images, so scans work too. Photos are downscaled and sent as images. The answer is structured JSON with a confidence level for every field and a legibility rating. The prompt forbids calculating or inferring values, so anything that isn't printed or isn't readable comes back empty, and validation flags it.

Validation checks the required fields, that subtotal plus tax equals the total, the tax rate, dates, currency and confidence. A missing or low-confidence required value blocks approval until the approver fixes it or approves anyway with a written reason. When the AI is down, refuses, or can't read the format, the document still goes to the approver for manual entry and the owner is told. Nothing is dropped.

Every document is approved by a person, even a perfect one. The AI only fills in the form.
04 / Duplicates

Three layers, safe under concurrency

layermatches · catches
exact file same SHA-256 · repeated uploads, forwarded and re-sent emails · no AI call
same invoice same vendor (normalised name or VAT ID) and same number · re-scans, regenerated PDFs
possible same vendor and total, a number one character off or dates within 3 days · a warning, the approver decides

Consecutive numbers like INV-1001 and INV-1002 are normal and never flagged on their own. The ledger row, file hash included, is written before the duplicate check, so of two copies processed at the same moment exactly one stays the original. Approval checks again for an already approved copy, and register rows are upserted by key, so a retried write updates a row instead of adding a second one.

05 / Approval without an account

Links that survive mail scanners

Each approval email carries signed links: an HMAC over the record, a per-document nonce and an expiry, with the secret kept in n8n's credential store. Opening a link only shows the review page, and the decision is a button press on that page. That matters because email security scanners open every link in an email, and a link that approved on open would approve every invoice.

Recording a decision rotates the nonce, so every earlier link, reminders included, then shows who decided and when. Two simultaneous clicks record exactly one decision. Corrections are stored next to the extracted values, and the Approved register shows what was changed.

06 / When things fail

The owner hears about it

Every document gets a ledger row before anything that can fail. A failure after that point becomes a processing error, an Exceptions row and an owner email with the original attached, so the document can still be handled by hand. The hourly watchdog sends reminders with fresh links, re-sends approval emails that failed, catches stuck documents and checks that the AI is reachable. The workflows keep only failed executions in n8n's history, so invoice contents don't pile up in its database.

07 / Built as code

Workflows generated from tested code

The n8n workflow files are build output. Node layout and wiring live in JavaScript, and the business logic (validation, duplicate rules, link signing, email and page HTML) is plain JavaScript that is inlined into the Code nodes at build time. Each node gets only the helpers it uses, so it stays readable on the n8n canvas. The same code runs under unit tests outside n8n, and one command deploys all ten workflows to any n8n instance through its API.

The end-to-end suite runs the real exported workflows on a local n8n, with an IMAP server, an SMTP sink, local stand-ins for Sheets and Drive, and a mock of the Claude API. Its 40 checks map onto the brief's acceptance tests and add the race cases: two simultaneous uploads of one file, two simultaneous approve clicks, an AI outage, a storage failure halfway through processing.

08 / Status

What was proven, and what wasn't

Every acceptance test in the brief passes end to end. On n8n Cloud, with a real mailbox, Google Sheets, Google Drive and the real Claude API, the upload form, email intake, duplicate detection, corrections, approval and rejection all worked.

Two things weren't tested. The Gmail trigger and send nodes never ran against a live account (IMAP and SMTP did). And extraction accuracy wasn't measured on a larger set of real documents. The repository includes a script that measures it field by field, for a few cents per run.

~/work
01 / 01 ←→ navigate · Esc close