Reconcile Invoices and Flag Anomalies in n8n
Finance teams catch payment errors late — often after the money has already moved. A vendor sends an invoice, someone checks it loosely against memory, AP clicks approve, and only the next month's reconciliation reveals the duplicate charge or the 12 % line-item overrun that should have been flagged.
An n8n workflow changes that. You pull invoices on a schedule, join them against purchase orders, and let an AI node read the free-text line items for you — flagging duplicates, price deviations, and odd vendor patterns before anyone hits "pay." This post walks you through building exactly that workflow, node by node.
What the finished workflow does
- Pulls new invoices from your accounting API (or email inbox) on a schedule.
- Fetches the matching PO for each invoice from your ERP or a Google Sheet.
- Merges and compares amounts, vendor names, and line-item counts.
- Sends line-item text to an AI node that flags anomalies — duplicates, unit-price drift, unexpected vendor codes, missing PO references.
- Routes results: clean invoices go to an approval queue; anomalies trigger an immediate Slack or email alert for human review.
Node-by-node build
1. Schedule Trigger
Add a Schedule Trigger node and set it to run at whatever cadence your AP cycle needs — twice daily works for most small teams:
Mode: Interval
Interval: 12 Hours
This is the heartbeat. The workflow wakes up, does its job, and sleeps again without you touching it.
2. HTTP Request — fetch new invoices
Use an HTTP Request node to call your accounting tool's API. Most modern tools (QuickBooks Online, Xero, FreshBooks, Zoho Invoice) expose a /invoices endpoint that accepts a createdSince query param:
Method: GET
URL: https://api.xero.com/api.xro/2.0/Invoices
Params:
DateFrom: {{ $now.minus(12, 'hours').toISO() }}
Status: SUBMITTED
Authenticate with the Xero (or QuickBooks) node's OAuth credential — set up once in Credentials and reuse across all nodes.
No accounting API? Use a Gmail trigger with a label filter (
invoice-inbox) instead. The Extract from File node converts the PDF attachment to text, which the AI node can parse directly.
3. Split in Batches
If the API returns an array of invoices, add a Split in Batches node (batchSize: 1) so subsequent nodes process one invoice at a time. This keeps the Merge node logic clean.
4. HTTP Request — fetch matching PO
For each invoice, look up its purchase order. If your POs live in a Google Sheet:
Node: Google Sheets — Read Rows
Sheet: Purchase Orders
Filter: PO Number = {{ $json.Reference }}
If they live in an ERP (SAP, NetSuite, Odoo), swap this for an HTTP Request to that system's PO endpoint. The key is to always pull by the PO number the invoice carries — that field is your join key.
5. Merge — join invoice to PO
Add a Merge node in Combine mode, mergeBy: Reference / PO Number. This produces one enriched item per invoice containing both the invoice data and its matching PO data side by side.
For invoices with no matching PO (null on the PO side), route immediately to the anomaly branch — a vendor billing you without a PO on file is always worth a look.
6. Set — compute variance
Add a Set node to calculate the numeric checks you care about:
{
"amountVariance": "{{ $json.invoice.Total - $json.po.ApprovedAmount }}",
"vendorMismatch": "{{ $json.invoice.Contact.Name !== $json.po.VendorName }}",
"lineItemCount": "{{ $json.invoice.LineItems.length }}",
"poLineItemCount": "{{ $json.po.LineItems.length }}"
}
These computed fields flow into both the rule-based IF check and the AI node's context.
7. AI / LLM Node — catch what rules miss
Numeric variance checks catch obvious overcharges, but they miss things like a vendor quietly renaming a line item to avoid a keyword filter, or an invoice that is technically within tolerance but is a duplicate of one paid last month under a slightly different reference number.
Add an AI Agent (or Basic LLM Chain) node and give it a structured prompt:
You are a finance-audit assistant. Review the invoice data below and identify
any of the following anomalies:
- Duplicate invoice (same vendor + same amount within the last 30 days)
- Unit price > 10% above the PO line item
- Line item descriptions that differ from the PO but match in quantity
- Missing or mismatched PO reference
- Vendor name variation suggesting a different legal entity
Invoice JSON:
{{ JSON.stringify($json.invoice) }}
PO JSON:
{{ JSON.stringify($json.po) }}
Recent paid invoices (same vendor, last 30 days):
{{ JSON.stringify($json.recentPaid) }}
Return: { "anomalies": ["..."], "riskLevel": "low|medium|high", "summary": "..." }
The AI node returns structured JSON. Map riskLevel and summary into your item so downstream nodes can branch on them.
8. IF — route by risk
Add an IF node:
Condition A: {{ $json.riskLevel }} = "high" → anomaly branch
Condition B: {{ $json.amountVariance }} > 50 → anomaly branch
Else → clean branch
Anomaly branch → Slack message (or email) to the AP lead with the AI summary, a link to the invoice in Xero/QuickBooks, and a one-click "mark reviewed" webhook back into n8n.
Clean branch → append a row to a Google Sheet approval queue or POST to your accounting tool's approve endpoint directly.
9. Sticky Note (optional but recommended)
Add a Sticky Note node near the AI node documenting the prompt version and the variance thresholds. When a colleague tweaks the workflow three months from now, they will know what the numbers mean.
Tips and common pitfalls
- Date handling: accounting APIs return dates in various formats. Use n8n's
$now.toISO()andDateTimehelpers consistently; mixing string comparison with ISO dates will silently break yourDateFromfilter. - Deduplication across runs: store processed invoice IDs in a Google Sheet or a simple Postgres table and check against them at step 2. Without this, a 12-hour re-run will re-alert on the same invoice.
- AI prompt length: very large invoices with 50+ line items can push the prompt near a model's context limit. Summarize line items server-side (Set node) before passing them to the AI, or switch to a model with a larger context window from the dropdown.
- Tolerate partial data: not every vendor sends complete PO references. Add an IF before the Merge to handle missing references gracefully instead of letting the workflow error silently.
Running this on AgentRoost
Self-hosting n8n for a workflow this straightforward is more overhead than it looks: you need a VPS, Docker, SSL, a reverse proxy, and you still have to wire up your own OpenAI key and pay for every AI call on top.
On AgentRoost you get your own n8n instance — your login, your workflows, your data — on a public subdomain (https://<your-id>.agentroost.app). The AI node credits are already included in the subscription. No API key. No separate billing relationship with OpenAI. The instance runs 24/7 on dedicated hardware, so the Schedule Trigger fires on time whether your laptop is open or not.
How to set it up:
- Sign up at agentroost.app — email, Google, Microsoft, or Discord.
- Pick the n8n framework, name your instance, and click Create.
- Your private n8n editor opens at
https://<your-id>.agentroost.appin about 2 minutes. - Import the workflow JSON (or build it from scratch) — the AI credential is pre-configured.
- Activate the Schedule Trigger. Done.
Plans start at $19.99/mo all-in, with a 14-day money-back guarantee and no lock-in. Compare plans or see what's included with the n8n framework.
Frequently asked questions
Do I need my own OpenAI API key to use the AI node in this workflow?
Not on AgentRoost. LLM/AI credits are included in every subscription tier, so the AI node is wired up and ready to use straight out of your editor. On self-hosted n8n or other platforms you would need to supply and pay for your own API key separately.
What accounting or ERP tools can I connect to n8n for invoice data?
n8n has native nodes for QuickBooks, Xero, FreshBooks, and Zoho Invoice, plus a generic HTTP Request node for any REST API. For older systems without a direct API, the Gmail or Microsoft Outlook nodes can pick up invoice PDFs from a dedicated mailbox instead.
What happens if an invoice PDF arrives as an attachment rather than structured data?
Use the Gmail (or Microsoft 365 Email) trigger to catch the attachment, then the Extract from File node to get the binary, and finally pass the extracted text to the AI/LLM node for parsing. The AI node can pull out vendor name, amount, line items, and PO number from unstructured text with a simple prompt.
Will my workflow keep running if I close the browser tab?
Yes. On AgentRoost your n8n instance is a persistent, always-on process on dedicated hardware. The Schedule Trigger fires independently of whether you have the editor open. Nothing stops when you close the tab.
Can I cancel if the workflow does not suit my needs?
Yes. All AgentRoost plans are monthly with no lock-in, and there is a 14-day money-back guarantee. You can export your workflow JSON at any time from the n8n editor before cancelling — you own your data and your workflows.