LedgerQuarry: invoices and receipt photos turned into clean, checked data
An n8n workflow that reads invoice PDFs and images from Gmail or Google Drive, extracts every field with Claude, checks the arithmetic in code, and files each invoice as approved or for review.
At a glance
Someone on the team retypes every invoice, and checks the sums by eye.
Invoices arrive as text PDFs, scanned PDFs and phone photos. Each one has to be read, typed into a sheet or ledger, and checked: do the lines add up, is the tax right, have we seen this invoice before?
A model can read the document, but it can also misread a digit. LedgerQuarry lets Claude do the reading and leaves every check to plain code, so a misread total cannot be filed as approved.
Arrives
A Gmail attachment or a new file in a watched Drive folder starts the workflow.
How it works
Each stage as it exists in the repository. Select a stage to see what it uses and what happens when it fails.
Trigger
Starts on a new Gmail attachment or a new Drive file
n8n Gmail Trigger, Google Drive Trigger
Logged to the Error Log sheet with an admin email
invoice_march_2026.pdf
Execution sequence
One item, traced through the system boundaries.
- Gmail / Drive to n8n workflow: new attachment: invoice.pdf
- n8n workflow to Claude API: POST /v1/messages (text or image)
- Claude API replies to n8n workflow: JSON fields + confidence
- n8n workflow to n8n workflow: validateMath · validateFields · determineStatus
- n8n workflow to Google Sheet: check duplicate, then append row
- n8n workflow to Gmail / Drive: success or review email; file moved to folder
One item, start to finish
A supplier emails a scanned PDF. Text extraction finds almost no text, so the page is converted to an image.
Claude returns invoice number, dates, vendor, line items, subtotal, tax and total, each with a confidence score.
The lines add up to the subtotal, but subtotal plus tax does not equal the stated total: TOTAL_MISMATCH.
The row is written as REVIEW_REQUIRED and a review email goes out with the discrepancy spelled out.
The checks that decide each invoice’s status
Change any number as if the model had misread it. The status logic below is the workflow’s own code, translated line for line.
After Claude returns the extracted fields, three n8n Code nodes decide what happens to the invoice. Change the numbers as if the model had misread one.
Logic copied from LedgerQuarry workflows/workflow.json (nodes validateMath, validateFields, classifyConfidence, determineStatus). Inputs are yours to change; nothing here calls a model or a server.
Architecture
The layers and the decision path, taken from the repository code.
| Layer | Component | Tech Stack | Responsibility |
|---|---|---|---|
| Layer 1: Triggers | Gmail and Drive watchers | n8n trigger nodes, Google OAuth | Start one run per document |
| Layer 2: Extraction | Claude multimodal callKEY | Anthropic Messages API, claude-3-5-sonnet-20241022 | Read text or image into a fixed JSON schema with confidences |
| Layer 3: Checks | Deterministic validationKEY | n8n Code nodes (JavaScript) | Arithmetic, required fields, confidence, status |
| Layer 4: Records | Sheet, folders, email | Google Sheets, Drive, Gmail, Apps Script formatting | Invoice rows, audit log, error log, filing |
- Gmail / Drive goes to Text or image?: file
- Text or image? goes to Claude extract: text or page image
- Claude extract goes to Maths + fields: JSON + confidence
- Maths + fields goes to Sheet + folder: AUTO_APPROVED
- Maths + fields falls back to Review email: REVIEW_*
Decisions and trade-offs
| Decision | Instead of | Why | Where in the code |
|---|---|---|---|
| Arithmetic checked in code | Asking the model whether the totals add up | The model reads; code adds. A misread digit shows up as a mismatch instead of being trusted. | workflow.json, node validateMath |
| Confidence per field | One confidence for the whole document | The schema asks for a confidence on every value, so low-confidence fields can be counted and listed. | workflow.json, nodes Prepare Claude Multimodal Payload, classifyConfidence |
| Text first, image only when needed | Always sending page images | Text PDFs go as text; only PDFs with under 50 characters of text are converted to images. | workflow.json, node IF PDF Text Length > 50 |
| Duplicates quarantined, not dropped | Silently skipping repeats | A repeated invoice is moved to a duplicates folder and an alert is sent, so nobody has to wonder where it went. | workflow.json, nodes Check Duplicate, Duplicate Alert |
Known limits
Output is a Google Sheet
There is no direct connection to Xero, QuickBooks or other accounting software in this workflow. Rows land in a sheet; posting to a ledger would be another step.
No VAT number check
The workflow checks arithmetic and required fields. It does not validate supplier VAT numbers against VIES or HMRC.
Single page images
Scanned PDFs are converted to an image for the model; long multi-page scans have not been evaluated.
Results
Only numbers produced by a committed, repeatable script appear here as measured. Until then, each result says how it will be measured.
Date, total, tax and vendor accuracy on a public receipt dataset (CORD or SROIE), with the document count, model version, licence and failures shown.
Count of AUTO_APPROVED vs review statuses on the same public dataset.
Only measurable with a client: minutes per invoice before and after.
Run it yourself
From the installation guide: run n8n, add credentials, import the workflow. Full instructions ↗
git clone https://github.com/arslanvuzmal/LedgerQuarry.git
# 1. Run n8n (docs/INSTALLATION.md, step 3 has a docker-compose file)
# 2. In n8n, add an Anthropic API key and Google OAuth2 credentials (step 4)
# 3. Workflows → Import from File → LedgerQuarry/workflows/workflow.json (step 5)
# 4. Point the Gmail / Drive triggers and the Google Sheet at your own, then activateQuestions people ask first
What files does it read?
PDF (text or scanned), PNG and JPEG. The file type is detected from the file’s first bytes, not its name.
Does it post to my accounting software?
Not in this version. It writes one row per invoice to a Google Sheet and files the document in Drive. A connection to Xero or QuickBooks would be an extra workflow step.
What if a receipt is unreadable?
Fields Claude cannot read come back empty with zero confidence. Missing totals or invoice numbers make the invoice REVIEW_REQUIRED, and a review email is sent.
What happens to a duplicate?
The invoice is looked up in the sheet first. A repeat is moved to a duplicates folder and an alert email is sent; the sheet is not touched.
Related reading & systems
Want LedgerQuarry working in your business?
Book a 20-minute scoping call. I will review your current systems, map where manual friction is costing you time, and give you an honest quote.