n8n + n8n: Invoice & NF-e Processor — GPT-4o Vision Extracts Data to Sheets + Slack
Every invoice that lands in your Gmail is a small tax on someone's afternoon. Someone opens the PDF, squints at the CNPJ, copies the total into a spreadsheet, checks the date, flags anything weird, an
Every invoice that lands in your Gmail is a small tax on someone's afternoon. Someone opens the PDF, squints at the CNPJ, copies the total into a spreadsheet, checks the date, flags anything weird, and moves on to the next one. Multiply that by 40, 80, 200 invoices a month and you have a full-time job made entirely of copy-paste — plus the errors that come free with manual data entry. This article shows you how to replace that job with an n8n workflow that reads every invoice and NF-e with GPT-4o Vision, writes the structured data to Google Sheets, and pings your team on Slack only when a human actually needs to look.
The problem: invoices are unstructured data pretending to be structured
Brazilian NF-e and generic supplier invoices arrive in a dozen shapes: a PDF attachment, a scanned image, a DANFE printout, an email body with the numbers buried in a table. The data you actually need — CNPJ do emissor, valor total, data de emissão, and the line items — is always there, but never in the same place twice. Traditional OCR chokes on layout variety. Regex breaks the moment a supplier changes their template. So the work falls back to humans.
That creates three concrete costs. First, latency: an invoice that arrives Friday at 6pm might not get logged until Monday, which delays approvals and payments. Second, errors: manual transcription runs about 1% error per field, and a wrong total or a transposed CNPJ can mean a duplicate payment or a compliance headache. Third, no visibility: because the data lives in inboxes and not in a table, nobody can answer "how much did we spend with this vendor this quarter?" without a manual audit. The fix is not more discipline. The fix is to make extraction automatic and reserve human attention for the exceptions.
The solution: GPT-4o Vision as the extraction engine
GPT-4o Vision changes the economics because it reads a document the way a person does — it looks at the whole page, understands that "CNPJ" near a 14-digit number is the issuer's tax ID, and returns clean JSON regardless of layout. You don't train it, you don't maintain templates, and it handles PDFs, scanned images, and photos of printed invoices with the same prompt.
The workflow wires this into a straight pipeline: Gmail Trigger catches incoming invoices → the attachment (or rendered PDF) goes to the OpenAI node running GPT-4o Vision → the model returns structured fields → a Google Sheets node appends a row → an IF node decides whether the invoice is clean or suspicious → Slack fires an alert only for the suspicious ones. Everything that passes validation is logged silently. Everything that needs a human gets a message with a link. Your team stops processing invoices and starts reviewing exceptions.
Step-by-step: building it in n8n
1. Trigger on the right emails. Add a Gmail Trigger node. Set the poll interval to every minute and use a search filter so you don't process your entire inbox — something like has:attachment (invoice OR "nota fiscal" OR NF-e OR fatura), or filter by a dedicated label like faturas/inbox that a Gmail rule applies automatically. Enable "Download Attachments" so the binary data flows downstream.
2. Normalize the attachment. Invoices come as PDF or image. GPT-4o Vision accepts images directly; for PDFs you have two options. The simplest is to pass the PDF's first page as an image — insert an Edit Image or a lightweight conversion step, or use the Extract from File node when the PDF has a real text layer. For scanned NF-e (no text layer) keep it as an image and let Vision do the reading. Add an IF node that branches on {{$binary.data.mimeType}} so PDFs and images take the right path.
3. Extract with GPT-4o Vision. Use the OpenAI node (or the HTTP Request node hitting /v1/chat/completions) with model gpt-4o. Pass the binary as an image input and give it a tight system prompt: "You are an invoice parser. Return ONLY valid JSON with keys: cnpj_emissor, razao_social, valor_total, data_emissao, numero_nota, line_items (array of {descricao, quantidade, valor_unitario}). Use null for missing fields. valor_total as a number, no currency symbol." Set temperature: 0 for deterministic output and turn on JSON mode / response_format json_object so you never get prose back. Low temperature matters here — you want the same invoice to produce the same numbers every run.
4. Parse and validate. Follow with a Code node (or Set node) that JSON.parses the model output and runs cheap sanity checks: is the CNPJ 14 digits after stripping punctuation? Is valor_total a positive number? Does the sum of line items roughly equal the total (within a small tolerance for taxes/rounding)? Is data_emissao a valid, recent date? Write a boolean field needs_review and a review_reason string based on those checks. This is your exception logic — spend time here, it's what keeps humans out of the loop for the 90% that's clean.
5. Log everything to Google Sheets. Add a Google Sheets node in "Append or Update" mode. Map each JSON field to a column: CNPJ, Razão Social, Número, Total, Data, Line Items (JSON string), plus metadata like the Gmail message ID, processing timestamp, and needs_review. Use the Gmail message ID as the matching column so a re-processed email updates its row instead of duplicating it — that idempotency detail saves you from double-counting when the trigger re-fires.
6. Alert on exceptions only. Add an IF node on needs_review === true. On the true branch, a Slack node posts to your #financeiro channel: the vendor name, the total, the review reason, and a direct link to both the Gmail message and the Sheets row. Format it as Slack Block Kit with a couple of buttons if you want one-click approval later. The false branch does nothing — silence is the signal that everything is fine.
Benefits: what changes on day one
Speed. An invoice is parsed and logged within a minute of arriving, day or night. No more Monday-morning backlog.
A real database of spend. Because every invoice becomes a row, you can pivot by vendor, by month, by category without touching an inbox. Feed the sheet into a dashboard and you have live spend visibility for free.
Attention where it counts. Your team only sees the invoices that failed validation — a mismatched total, an unknown CNPJ, a suspiciously round number. That's maybe 10% of volume, which means a 90% reduction in manual touches.
Accuracy. GPT-4o Vision at temperature 0 plus arithmetic validation catches errors that a tired human at 6pm would miss. The workflow never gets bored on invoice number 150.
Auditability. Every row carries the source message ID and a timestamp, so any figure in the sheet traces back to the original email in one click.
Common pitfalls (and how to dodge them)
Letting the model return prose. Without JSON mode and an explicit schema, GPT-4o occasionally wraps its answer in "Here is the extracted data...". Always set response_format: json_object and wrap the parse in a try/catch that routes failures to Slack instead of crashing the run.
Trusting extraction blindly. Vision is excellent but not infallible on faint scans or handwritten adjustments. That's exactly why the validation node exists — never write straight to your accounting system from the model output. The Sheet plus Slack review step is your safety net; keep a human on the exceptions until you've measured accuracy on your own invoice mix.
Reprocessing duplicates. Gmail triggers can re-fire, and forwarded invoices arrive twice. Use the message ID as the Sheets matching key and consider a dedupe check on invoice number + CNPJ so the same NF-e never lands twice.
Ignoring cost and rate limits. Each Vision call costs a few cents and counts against your OpenAI rate limit. For high volume, batch the Gmail trigger sensibly, and add a small Wait or a retry-with-backoff on the OpenAI node so a burst of month-end invoices doesn't hit a 429 and drop data.
Oversharing on Slack. The temptation is to alert on every invoice. Don't — that just recreates the inbox noise in a new channel. Alert only when needs_review is true. If the channel is quiet, that means the system is working.
Put together, this is maybe seven nodes of n8n and one good prompt standing between you and never manually typing an invoice total again. Start with the validation rules loose and a human reviewing more than you'd like, then tighten the rules as you build trust in the extraction. Within a week you'll have a spreadsheet that fills itself and a Slack channel that only speaks up when it matters.
Ja construimos isso pra voce
Nao comece do zero. O Invoice & NF-e Processor — GPT-4o Vision Extracts Data to Sheets + Slack e um workflow n8n pronto para instalar — conecta suas ferramentas em minutos, sem codigo.
Instalar por $79.0 →