Email receipts → clean expense ledger in Sheets (Gmail + OpenAI) — built to survive weird receipts

Hi all — sharing a workflow that turns the “shoebox of email receipts” problem into a spreadsheet that’s actually ready for your accountant.

Most templates are fair-weather demos. This one is built for the day things go wrong:

  • ✓ The same receipt is never logged twice, even across re-runs (ledger lookup by Gmail message ID before every append)
  • ✓ A weird receipt can’t corrupt the ledger — strict structured-output parsing means a bad extraction produces no row, not a broken one
  • ✓ Every AI and Google call retries automatically (3×, with backoff) — transient failures don’t silently kill a run
  • ✓ Canvas setup notes (~15 minutes)

What it does: watches Gmail for receipt emails, has OpenAI read the attachment (PDF text or photo via vision) and extract the fields that matter (merchant, date, amount, currency, category — picked only from your category list), and appends each as a clean row in a Google Sheets ledger. On the 1st of each month it emails you last month’s spending summary by category — no formulas required.

Template (free, official library): Log email receipts to Google Sheets with Gmail and OpenAI | n8n workflow template

Feedback welcome — especially on categories: what taxonomy do you use for small-business expenses?

إعجاب واحد (1)

the message id lookup is the right place to start, but i would make the ledger idempotent at more than one level. store the gmail message id, attachment hash, extraction status, and reviewed timestamp in separate fields. that protects you when the same receipt arrives in a forwarded email or when a failed run is retried after the row was already written. i would also send low confidence amounts and currencies to a human review queue rather than rejecting the whole receipt. for categories, keep a small controlled list and log the model suggestion beside the final approved value.

إعجاب واحد (1)

Thanks @Long_Vu_Nguyen — this is exactly the kind of feedback I was hoping for, and you clearly speak from scars. :slight_smile:

Multi-level idempotency — you’re right that the message ID alone has blind spots. The forwarded-receipt case is the one that bit me in testing too (same PDF, brand-new message ID). Adding an attachment hash column next to the message ID is cheap and covers it, and an explicit extraction-status field would make retries-after-partial-write a non-event instead of a “hope the ledger lookup ran”. Putting both on the list for the next version.

Low-confidence → review queue instead of “no row” — fair point. My current stance was “a missing row is easier to notice than a wrong number”, but I’ll admit “silently no row” isn’t great for the person doing month-end. A needs_review status (row written, but excluded from the monthly summary until approved) is probably the honest middle ground.

Model suggestion beside the approved category — love this. It’s basically free to log, and after a few months you get a real picture of where the model and the human disagree, which is the data you’d want before touching the category list anyway.

Out of curiosity — in your setups, who actually works the review queue? Owner, bookkeeper, or does it mostly sit until month-end? Trying to figure out how much of the review UX belongs inside the workflow vs. just in the sheet.