Automate Expense Reports with AI and n8n: Receipt Extraction, Checks and Approval
Published 2 August 2026 · 6 min read
Every month, the same ritual: employees photograph their receipts, lose half the supporting documents, fill in an approximate spreadsheet, and someone — often you — re-enters everything for accounting while checking spending limits by hand. Yet a receipt is a short document with a predictable format, from which you always extract the same fields: the ideal automation candidate. With n8n, a vision model and a few control rules, you can build the complete pipeline: receipt collection, extraction, checks, manager approval and a monthly export for accounting.
This guide is the "receipts" sibling of our article on extracting data from PDF invoices with AI: same underlying logic, but shorter, messier documents and a more formalized human validation loop.
Step 1: collect the receipts
Three entry points cover almost every organization, and nothing stops you from combining them in the same workflow:
- A dedicated email inbox (
expenses@your-domain.com): an IMAP or Gmail trigger picks up each message and its attachments; forwarding a photo from a phone requires no new tool for employees. - An n8n form: the n8n Form Trigger builds an upload form in minutes, with a file field, the employee's name and a comment, enforcing required fields at submission time.
- A shared Drive folder: a Google Drive trigger watches an "Expenses" folder. Convenient when the habit already exists, but the link between file and author disappears — plan one subfolder per person.
Whatever the channel, this step must output an n8n item with the receipt as binary data and the requester's identity as JSON. For smarter routing upstream (telling a receipt apart from a supplier invoice or a contract), the AI-based incoming document classification pattern slots in naturally here.
Step 2: extract the data with a vision model
A till receipt is almost always an image (photo or scan), not a text-based PDF. The simplest approach is to send the image straight to a multimodal model — GPT-4o, Claude or Gemini — which reads the photo without a separate OCR step, as covered in our guide to AI image analysis in n8n. This is not a leap of faith: the SROIE competition, presented by Zheng Huang and co-authors at ICDAR 2019 (paper on Google Scholar), established a benchmark of 1,000 scanned receipts on which the best systems already exceeded 90% F1 on key-field extraction.
To get reliable JSON instead of a paragraph of prose, pair a Basic LLM Chain with a Structured Output Parser using this schema:
{
"type": "object",
"properties": {
"merchant": { "type": "string" },
"date": { "type": "string", "description": "Receipt date, YYYY-MM-DD format" },
"total_amount": { "type": "number" },
"net_amount": { "type": ["number", "null"] },
"tax_amount": { "type": ["number", "null"] },
"currency": { "type": "string", "description": "ISO code, e.g. EUR" },
"category": {
"type": "string",
"enum": ["meals", "transport", "lodging", "supplies", "other"]
},
"readable": { "type": "boolean", "description": "false if any critical field is illegible" }
},
"required": ["merchant", "date", "total_amount", "category", "readable"]
}
Two prompt instructions make all the difference: never invent a missing value (return null for net amount or tax if they are missing from the receipt, which is common) and set readable: false as soon as a required field is illegible rather than guessing. The category field with its enum forces the model to pick from your expense plan — it is what drives the spending limits. The semantic structuring of receipts is also well documented: the CORD dataset, published by Seunghyun Park and his team at the NeurIPS 2019 Document Intelligence workshop (reference on Google Scholar), annotates thousands of receipts precisely for this kind of post-OCR parsing.
Step 3: automatic checks
This is where the workflow earns its credibility with accounting. Three rules cover the essentials, implemented in a Code node followed by a Switch:
const LIMITS = { meals: 25, transport: 300, lodging: 130, supplies: 150, other: 50 };
const r = $json;
const issues = [];
// 1. Illegible receipt → ask for a resend
if (!r.readable || !r.total_amount) issues.push("illegible");
// 2. Spending limit exceeded for the category
if (r.total_amount > (LIMITS[r.category] ?? LIMITS.other)) issues.push("over_limit");
// 3. Amount and date consistency
if (r.net_amount && r.tax_amount
&& Math.abs(r.net_amount + r.tax_amount - r.total_amount) > 0.05) issues.push("inconsistent_amounts");
if (new Date(r.date) > new Date()) issues.push("future_date");
// Deduplication key
const key = `${r.merchant}|${r.date}|${r.total_amount}`.toLowerCase();
return { ...r, issues, dedup_key: key, status: issues.length ? "needs_review" : "compliant" };
For duplicate receipts, the merchant|date|amount key is compared against already-recorded expenses: through a query on your tracking table, or with the Remove Duplicates node in cross-execution mode. This semantic key catches the classic case: the same receipt submitted twice, once by email and then dropped again into Drive.
Routing at the Switch output:
illegible→ automatic email back to the employee asking for a new photo, then stop processing this item.over_limitor any inconsistency → manager approval loop (step 4), with the issue spelled out explicitly.compliantbelow your auto-approval threshold (50 euros, for instance) → direct insertion, no human involved.
Step 4: manager approval with the Wait node
For expenses that require human judgment, n8n has a native mechanism: the Wait node resuming on a webhook, or more simply the "Send and Wait for Response" operations of the Slack and Gmail nodes. The manager receives a summary (employee, merchant, amount, category, flagged issue, link to the photo) with two Approve / Reject buttons; the workflow stays paused until they click. The full setup — buttons, expiration timeouts, reminders — is covered in our guide to human approval with Wait and Slack.
Two settings are worth configuring:
- A reminder timeout: with no answer within 72 hours, a reminder branch resends the notification instead of leaving the expense in limbo.
- A rejection reason: on rejection, ask for a short comment and forward it to the employee. An unexplained rejection creates more friction than it prevents.
Step 5: tracking table and monthly export
Every processed expense — approved, rejected or auto-approved — is written to a tracking table with its status, the decision timestamp and the approver's identity. n8n's built-in Data Tables are enough for typical SMB volumes; a Postgres database or Airtable works if accounting wants to query the data directly.
For the monthly close, a Schedule Trigger (on the 1st) filters the previous month's approved expenses and produces the file with the Convert to File node: CSV for a direct accounting import, or Excel with one sheet per category. Our guide to generating Excel and CSV files in n8n covers the format options; the file then goes out by email to the accountant or lands in the shared Drive.
Limitations and best practices
- Extraction is not infallible: aim for a system where AI handles 90% of cases and cleanly routes the rest to a human, not a system that pretends to automate everything. The
readableflag and the consistency checks exist for exactly that. - Spending limits change: store them in a Data Table or environment variables rather than hardcoding them in the Code node, so they can change without touching the workflow.
- Keep the original receipt: archive the photo (Drive, S3) with a link from the tracking table. In a tax audit, the source document is what counts, not the extracted JSON.
- Watch out for currencies: a receipt in pounds or dollars cannot be compared to a euro limit without conversion. At minimum, route any non-EUR currency to manual approval.
- Test on your real receipts: build a test set (crumpled receipts, faded thermal print, handwritten totals) before going live, and measure the correct-extraction rate.
Key takeaways
An n8n expense workflow chains five blocks: multi-channel collection (dedicated inbox, n8n form, Drive folder), vision-model extraction constrained by a Structured Output Parser, automatic checks (duplicates, per-category spending limits, legibility), manager approval via Wait with auto-approval below a threshold, then a tracking table and a monthly Excel/CSV export. The JSON schema with its category enum and the rules Code node shown here are the core of the system — the rest is assembly of standard n8n building blocks. Start with the email + extraction + tracking table circuit, then add checks and approval once extraction quality is validated on your own receipts.
FAQ
Frequently asked questions
Which AI model should I use to read photographed receipts?
A recent multimodal model (GPT-4o, Claude, Gemini) reads a receipt photo directly, with no separate OCR step. For high volumes or badly degraded receipts, a dedicated OCR service (Google Cloud Vision, Mistral OCR) followed by an LLM for structuring remains more robust and cheaper per document.
How do I detect an employee submitting the same receipt twice?
Build a deduplication key from the extracted fields (merchant + date + total amount) and check it against your tracking table before inserting. A SHA-256 hash of the binary file additionally catches identical re-uploads, but not a new photo of the same receipt — which is why combining both works best.
Should every expense go through manager approval?
No — auto-approval is the main time saver. Set a threshold (say 50 euros) below which rule-compliant expenses are approved automatically. Only high amounts, limit overruns and ambiguous cases go through the manual approval loop.
What should happen when the extracted amount looks wrong?
Add a consistency check before insertion: net amount plus tax must equal the total within a few cents, and the date must be in the recent past. On any inconsistency, route the expense to human review instead of writing a doubtful value into the tracking table.
Bundle FlowKit Complet
€269