Workflow sheetInbox → Ledger → Summary
Gmail trigger
Label as processed
Split into PDFs
Loop over each PDF
Extract & clean text
Has a text layer?
Gemini extraction
Validation gate
Already in ledger?
Already in Drive?
File to Drive
Ledger row
If the PDF is a scan
Gemini reads the image
Rejoin at validation
If a gate fails or it's a duplicate
Errors log
Reply to sender
When every PDF is done
Today's invoices & errors
Run summary email
TriggerAI modelGuardrailOutputMain pathFallback path
Edge cases from the real sample set
- Not every PDF is an invoiceA delivery note marked "this is not an invoice" is classified and rejected, not forced through.
- One PDF, two invoicesThe model returns an array; each invoice becomes its own item before filing.
- The second-notice duplicateA reminder copy is caught twice, against the ledger by invoice number, then against Drive by file name.
- European number formats2.280,04 € is explicitly normalized to 2280.04, never left to chance.
- Scans with no text layerEmpty extraction falls back to a vision-capable model reading the page image.
- Total due vs. discountThe prompt targets the final amount due, not the early-settlement figure beside it.


