Invoice Approval Workflow
An n8n workflow that picks invoices up from Gmail and Google Drive, extracts them with a multimodal LLM, asks a human to approve, and writes clean rows to PostgreSQL.
- Year
- 2026
- Role
- Solo: workflow design, schemas, integration
- Status
- Shipped
- Context
- Product
Results
85%
shorter processing cycle
Compared with manual invoice handling
99.8%
structural insertion accuracy
Rows written to PostgreSQL
Gmail and Google Drive webhooks pick up incoming PDF invoices. A multimodal LLM extracts them against strict JSON schemas, so the output is structured before anything downstream sees it.
Every extraction then goes to a human through an n8n Form for approval, and a vector-storage risk assessment flags anything that looks unusual before it is written to PostgreSQL.
The result is a processing cycle 85% shorter than the manual one, with 99.8% structural accuracy on database inserts.
I also published an open-source accounts-payable version under MIT. It watches a Gmail AP inbox and archives originals to Google Drive, runs Mistral Document AI OCR into Markdown, and extracts with Gemini 2.5 Pro at temperature 0 against a strict schema covering vendor, tax ID, line items, totals and a GL category. Validation reconciles line items to totals and checks confidence; duplicates are blocked, exceptions are emailed to AP, high-value invoices go to Slack for one-tap approval, and clean ones are upserted into a Google Sheets ledger. Invoice text is treated as untrusted, so instructions hidden in a document are ignored.
README · synced from GitHubOpen original ↗
Touchless Accounts Payable on n8n. Invoices and receipts arrive in your inbox. Each one is read, validated against finance controls, routed for approval when needed and posted to your ledger, usually without anyone keying data in by hand.
Executive Overview
Finance teams still re-type invoice data from PDFs into spreadsheets and ERPs. The work is slow, error-prone and costly to scale. Duplicate payments and missed early-payment discounts slip through because no one has time to check every document.
This workflow replaces that manual loop with a six-stage, auditable pipeline:
| Stage | What happens | Business control |
|---|---|---|
| ① Intake & Archive | Watches a dedicated AP inbox, splits attachments and accepts PDFs and receipt photos | Every original is stored immutably in Google Drive |
| ② OCR | Mistral Document AI converts every page to structured Markdown, tables included | Automatic retries on transient API failures |
| ③ AI Extraction | Gemini 2.5 Pro (temperature 0) returns a record locked to a strict JSON schema | No free-text output; missing data becomes null and is never invented |
| ④ Validation | Required fields, line-item → subtotal → total reconciliation, date checks, confidence gate | Bad data never reaches the ledger silently |
| ⑤ Exceptions & Approval | Duplicates blocked, exceptions emailed to AP, high-value invoices approved in Slack with one click | Segregation of duties and duplicate-payment prevention |
| ⑥ Ledger & Alerts | Idempotent upsert of invoice header and line items to Google Sheets, Slack status, Gmail label | Full audit trail: who, what, when, plus a link to the original |
Extracted data model
| Group | Fields |
|---|---|
| Vendor | Name · Tax ID (VAT / EIN / GSTIN / ABN) · Address · Email |
| Invoice | Invoice # · Invoice date · Due date · PO # · Currency (ISO 4217) · Payment terms |
| Line items | Description · SKU · Quantity · Unit price · Tax rate · Line total |
| Totals | Subtotal · Tax · Shipping · Discount · Total |
| Classification | GL expense category (12-category enum) · Document type · AI confidence · Warnings |
Business ROI
Illustrative model. Replace the inputs with your own volumes and loaded labour cost.
| Input | Manual AP | With this workflow |
|---|---|---|
| Invoices per month | 1,000 | 1,000 |
| Handling time per invoice | ~8 min | ~1 min (exceptions only) |
| Staff hours per month | ~133 h | ~17 h |
| Cost at $40/h loaded | ~$5,300 / month | ~$700 / month + API usage |
| Cycle time (receipt → posted) | Days | Minutes |
Value drivers your CFO will care about
- ~85% fewer manual touches. Staff handle exceptions, not data entry.
- Duplicate-payment prevention. Each invoice is fingerprinted on vendor identity plus invoice number.
- Policy enforcement. Approval thresholds and confidence gates are configuration, not tribal knowledge.
- Faster close. Line-item data with GL categories is ready for spend analytics on day one.
- Audit-ready. Every ledger row links back to the archived original and the n8n execution ID.
Architecture
flowchart LR
subgraph S1["① Intake & Archive"]
A["📥 Gmail AP Inbox<br/>(trigger)"] --> B["⚙️ Config<br/>Pipeline Settings"]
B --> C["✂️ Split Attachments"]
C --> D{"PDF / Image?"}
D -- yes --> E["🗄️ Archive Original<br/>Google Drive"]
end
subgraph S2["② OCR"]
E --> F["⬆️ Upload to Mistral"]
F --> G["🔗 Signed URL"]
G --> H["🔍 Mistral Document AI<br/>all pages → Markdown"]
end
subgraph S3["③ AI Extraction"]
H --> I["🤖 Gemini 2.5 Pro<br/>temperature 0"]
J["📐 Strict JSON Schema<br/>Output Parser"] -.-> I
end
subgraph S4["④ Validation & Controls"]
I --> K["✅ Business Rules<br/>required fields · reconciliation<br/>dates · confidence · duplicates"]
K --> L{"Decision"}
end
subgraph S5["⑤ Exceptions & Approval"]
L -- Duplicate --> M["🚨 Slack: Duplicate Blocked"]
L -- Needs Review --> N["📧 Email AP Review Queue"]
L -- "Total ≥ threshold" --> O["💰 Slack Approve / Reject"]
end
subgraph S6["⑥ Ledger & Notifications"]
P["🧾 Prepare Ledger Record"]
L -- Auto-Approved --> P
L -- Needs Review --> P
O --> P
P --> Q["📊 Sheets: Invoices<br/>(upsert on invoice_key)"]
P --> R["📊 Sheets: Line_Items<br/>(upsert on line_id)"]
P --> S["💬 Slack Status Update"]
P --> T["🏷️ Label Email Processed"]
M --> T
end
Decision logic
| Decision | Condition | Outcome |
|---|---|---|
DUPLICATE |
Vendor + invoice # already processed | Slack alert, no ledger entry, email labelled |
NEEDS_REVIEW |
Any validation issue (missing field, totals don't reconcile, low confidence, credit note…) | Ledger row with status NEEDS_REVIEW, email to AP with the issue list |
APPROVAL_REQUIRED |
Clean extraction and total ≥ approvalThreshold |
Slack Approve/Reject buttons (48 h window) → APPROVED / REJECTED / APPROVAL_TIMEOUT |
AUTO_APPROVED |
Clean extraction below threshold | Posted straight to the ledger as APPROVED |
Repository Structure
├── workflows/
│ └── invoice_processing_pipeline.json # Import this into n8n
├── .env.example # Reference config & credential checklist
├── .gitignore
├── LICENSE
└── README.md
Setup & n8n Import
Prerequisites
- n8n 1.90+ (self-hosted or n8n Cloud). The instance must be reachable over HTTPS for the Slack approval buttons.
- Google Workspace account (Gmail, Drive, Sheets)
- Mistral API key (OCR)
- Google Gemini API key
- Slack workspace with a bot token (
chat:write,channels:read)
Step 1: Prepare Google Workspace
Gmail: create the label
ai-invoice-processed. Copy its label ID (open n8n → Gmail node → Get Many Labels, or use the Gmail API explorer).Drive: create a folder such as
AP / Invoice Archive. Copy the folder ID from its URL.Sheets: create a spreadsheet with two tabs and these header rows:
Invoicesinvoice_key | status | vendor_name | vendor_tax_id | invoice_number | invoice_date | due_date | po_number | currency | subtotal | tax_total | shipping_total | discount_total | total | expense_category | document_type | confidence | line_item_count | issues_text | warnings_text | drive_link | file_name | processed_at | execution_id | email_message_idLine_Itemsline_id | invoice_key | invoice_number | vendor_name | line_number | sku | description | quantity | unit_price | tax_rate | line_total | currency | expense_category
Step 2: Import the workflow
- In n8n, open Workflows → Add workflow → ⋯ menu → Import from File.
- Select
workflows/invoice_processing_pipeline.json. - The workflow opens as Enterprise AI Invoice & Expense Automation (inactive).
Step 3: Create credentials
Create these credentials and select them on the matching nodes (names are pre-filled as placeholders):
| Credential name | n8n type | Used by |
|---|---|---|
Enterprise AI Invoice · Gmail (AP Inbox) |
Gmail OAuth2 | Trigger, review email, label |
Enterprise AI Invoice · Google Drive |
Google Drive OAuth2 | Archive nodes |
Enterprise AI Invoice · Google Sheets |
Google Sheets OAuth2 | Ledger nodes |
Enterprise AI Invoice · Mistral API |
Header Auth → Name Authorization, Value Bearer <MISTRAL_API_KEY> |
3 OCR nodes |
Enterprise AI Invoice · Google Gemini API |
Google Gemini (PaLM) API | Gemini model |
Enterprise AI Invoice · Slack |
Slack API (bot token) | Alerts & approvals |
Step 4: Configure the pipeline
Open Config · Pipeline Settings. It is the only node you need to edit:
| Setting | Default | Purpose |
|---|---|---|
approvalThreshold |
5000 |
Totals at or above this need Slack sign-off |
confidenceThreshold |
0.85 |
Below this, the invoice goes to manual review |
amountTolerance |
0.02 |
Rounding tolerance for reconciliation (or 0.5% of amount, whichever is larger) |
requireTaxId |
true |
Set false for receipt-heavy expense flows |
defaultCurrency |
USD |
Used when the document has no currency |
slackChannel |
#ap-invoices |
Alerts and approvals |
apTeamEmail |
accounts-payable@… |
Exception queue |
googleSheetId / driveArchiveFolderId / processedLabelId |
placeholders | IDs from Step 1 |
Step 5: Test, then activate
- Send a sample invoice PDF to the AP inbox.
- Click Test workflow and inspect each stage's output.
- Toggle the workflow Active. Duplicate detection persists only in production executions (n8n workflow static data).
Customising
- Different LLM: swap the Gemini node for OpenAI, Anthropic Claude or Azure OpenAI. The schema parser and prompts are model-agnostic.
- ERP instead of Sheets: replace the two ledger nodes with QuickBooks, Xero, NetSuite or SAP (HTTP) nodes.
Ledger · Prepare Recordalready outputs a flat, ERP-ready object. - Stricter policies: add rules in
Validate · Apply Business Rules, e.g. vendor allow-list or PO matching. Each rule pushes a message toissues.
Security & Compliance Notes
- No API keys live in the workflow JSON; all secrets sit in n8n's encrypted credential store.
- Document text is treated as untrusted. The system prompt tells the model to ignore instructions embedded in invoices (prompt-injection hardening).
- Original documents are retained in Drive for audit. Apply your retention policy to the archive folder.
- Run self-hosted n8n if invoices must stay within your infrastructure or region.