Drop an invoice into a Google Drive folder. A minute later the vendor, invoice number, dates, subtotal, tax, total, currency and line items are sitting in a Google Sheet row. PDFs, scanned PDFs and phone photos of paper bills all work.
Built by Pankaj Pahuja. Free to use, change, and pull apart.
The full build, including the part where it invented an invoice that did not exist: https://youtu.be/oSlq1rAV8XA
Every invoice-extraction demo you have seen works on a clean invoice. Mine did too, until I fed it a document that was not an invoice and it confidently filled every field with plausible, completely fabricated data. A vendor name. A total. A tax figure. None of it was in the document.
That is the failure mode nobody films, and it is the one that matters, because a wrong number in your books is worse than no number at all.
So there is a trust guard after every extraction path. It takes each field the model returned and checks the value actually appears in the source text before letting it through. If it does not appear, the field is blanked rather than saved. The row still gets written, but it is flagged NEEDS REVIEW
instead of OK
.
The AI reads. The code decides what is allowed to be true.
Invoice lands in Drive folder
|
Download file
|
Is it an image?
/ \
yes no (PDF)
| |
Vision read Extract text layer
| / \
| has text? no text (scanned)
| | |
| Structured Send whole PDF
| extraction to the model
| | |
+---> TRUST GUARD <------------+
|
Append row to Google Sheet
Status: OK / NEEDS REVIEW
Three routes in, one guard, one sheet. Twelve nodes total.
| Route | When it fires | How it reads the file |
|---|---|---|
| Photo | mime type contains image |
|
| Vision model, high detail, transcribes then extracts | ||
| Text PDF | PDF with a real text layer | Text extraction plus n8n's Information Extractor with a strict JSON schema |
| Scanned PDF | PDF whose text layer is empty | Whole PDF posted to the OpenAI Responses API as a document |
The scanned-PDF branch is the one most templates skip. A photocopied or faxed invoice has no text to extract, so a text-only pipeline silently produces an empty row and you never find out.
In n8n:
Workflows → Import from File, pickai-invoice-extractor-template.json
. - Connect three credentials:
Google Drive OAuth2,** Google Sheets OAuth2**,** OpenAI**. Every node that needs one will show a red warning until you pick it. - Search the workflow for
YOUR_
and replace two placeholders:YOUR_DRIVE_FOLDER_ID
on the trigger, the folder you will drop invoices intoYOUR_GOOGLE_SHEET_ID
on the sheet node
Create a sheet named
Sheet1
with these headers in row 1, spelled exactly:Timestamp · File Name · Vendor · Invoice Number · Invoice Date · Due Date · Subtotal · Tax · Total · Currency · Line Items · Status
Activate, then drop a real invoice in the folder and watch it run.
The trigger polls every minute. Free n8n Cloud plans throttle polling, so on those you may see a delay or want to switch the trigger to a schedule.
A row is markedNEEDS REVIEW
is the feature, not a bug.OK
only when vendor, total, invoice number and currency all survived the guard. Anything else gets flagged. Sort your sheet by that column and you have a work queue instead of a pile of maybe-wrong data.The guard is deliberately strict, so it will sometimes blank a correct value. A vendor name that is printed as a logo image rather than text will not be found in the text layer, so it gets blanked. That is the trade I chose. A blank you can see beats a fabrication you cannot.Currency has a deterministic fallback. If the model leaves it empty, plain code reads the symbol off the document. £ is GBP, € is EUR, ₹ is INR, and a bare $ defaults to USD unless the address mentions Canada or Australia. Change that default if you are not US-first.The timestamp is hardcoded to I am in India. Change it on the sheet node.Asia/Kolkata
.Model is, at temperature 0 on the two PDF routes. Temperature 0 matters here, you want the same document to produce the same answer every time. The vision node does not expose a temperature setting, which is one more reason the guard sits downstream of all three.gpt-4.1
on all three routesCost is small but not zero. Every file hits a model, and the scanned and photo routes send the whole document. Point the trigger at a folder you control, not a shared inbox folder, or one bad sync will process a thousand files.This reads invoices. It does not pay them, approve them, or file them. No write path touches your accounting system, on purpose.
The workflow was designed and pushed to n8n from Claude Code over the n8n API rather than clicked together node by node. The trust guard exists because the build broke on camera and the fix had to be real. That whole sequence, including the fabricated invoice, is in the video.
Questions: hello@ingenify.ai