§ Technical guide · how-to April 18 · 2026 12 min · intermediate stack: n8n · GPT-4o · Holded

How to automate
invoicing
with n8n + GPT-4o.

Practical guide with real code. By the end of this read you'll have a workflow that receives invoices by email, extracts data with AI, validates against your POs, and records in your ERP. Without typing.

TL;DR

An n8n workflow that listens to an invoices@ mailbox, sends each PDF to GPT-4o for structured extraction, validates the tax ID against your supplier master, cross-references the amount against open POs, and creates the entry in Holded/Odoo/Sage. Time per invoice: ~8 seconds. Replaces 4-7 minutes of manual work per invoice. This workflow is running today in real clients processing 8,000+ invoices/month.

Prerequisites

  • n8n 1.40+ self-hosted (Docker recommended). If you don't have it, npx n8n for testing.
  • OpenAI API key with GPT-4o access (~$0.01 per typical invoice).
  • ERP with REST API. We'll use Holded as example, but the logic is identical for Odoo, Sage, A3, Quipu, SAP B1.
  • Dedicated IMAP mailbox for invoices (Gmail / Outlook / corporate).
  • Basic knowledge of n8n: nodes, expressions, error handling.
Note Production-ready guide assuming the workflow will run unattended. If you just want a quick demo, skip steps 4 and 6.

01 · Prepare the n8n environment

Make sure you have n8n 1.40+ running self-host with IMAP credentials and OpenAI API key configured. Create a new workflow named "Supplier invoices → ERP".

02 · Configure the IMAP trigger

Add an Email Trigger (IMAP) node listening to your invoices@ mailbox. Filter by attachments with extension .pdf. Set check frequency every 5 minutes (or 1 min if you want near-real-time).

{
  "mailbox": "INBOX",
  "format": "resolved",
  "options": {
    "downloadAttachments": true,
    "fileExtensions": ["pdf"]
  }
}

03 · Extract data with GPT-4o Vision

Add an HTTP Request node calling OpenAI with a vision-capable model. Build the prompt to return strict JSON:

{
  "model": "gpt-4o",
  "messages": [
    {"role": "system", "content": "You are an expert in commercial invoices. Extract the following fields and return ONLY valid JSON: tax_id, supplier_name, invoice_number, invoice_date, total_amount, vat_amount, line_items (array)."},
    {"role": "user", "content": [
      {"type": "text", "text": "Extract data from this invoice."},
      {"type": "image_url", "image_url": {"url": "data:application/pdf;base64,{{$binary.attachment_0.data}}"}}
    ]}
  ],
  "response_format": {"type": "json_object"}
}

04 · Validate against supplier master and POs

Validation in two steps:

  1. Tax ID validation: query your supplier master (Holded API or your DB). If it doesn't exist, escalate to human review with proposal to create a new supplier.
  2. Amount validation: cross-reference against open POs from this supplier. If the difference is > 1% of expected amount, flag as an exception.

05 · Post in the ERP

If everything validates, create the invoice in Holded with the original PDF attached. Use the official Holded API endpoint POST /api/invoicing/v1/documents/purchase.

06 · Escalate exceptions to a human

Anything that doesn't validate cleanly goes to a Slack/Teams notification with the summary and a "Review" link to a simple internal interface (or simply assigns the invoice to a Trello/Notion board). Never let exceptions drop silently.

Common errors

  • OCR fails on poorly scanned PDFs: add a previous step with pdf-to-image at high resolution and re-process.
  • Total mismatch (sum of lines vs total): common in invoices with rounding. Define a tolerance of ±1 cent before flagging.
  • Different tax ID for the same supplier: sometimes the supplier has multiple legal entities (Holding S.L. vs Operating S.L.U.). Use a fuzzy match on the name, not just exact tax ID.
  • Hardcoded credentials: always use n8n credentials, never plain values in HTTP nodes.
Help If you'd rather not build it yourself, free 30-min assessment and we deliver it ready in 4+4 weeks. From €4,500 fixed fee.