---
title: "Reconcile Invoices and Flag Anomalies in n8n"
description: "Build an n8n invoice reconciliation workflow that flags duplicates, overcharges, and anomalies with a built-in AI node. No API key required."
canonical: https://agentroost.app/en/blog/invoice-reconciliation-anomaly-n8n
date: 2026-06-05T20:00:00Z
---

[Canonical URL](https://agentroost.app/en/blog/invoice-reconciliation-anomaly-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

1. **Pulls new invoices** from your accounting API (or email inbox) on a schedule.
2. **Fetches the matching PO** for each invoice from your ERP or a Google Sheet.
3. **Merges and compares** amounts, vendor names, and line-item counts.
4. **Sends line-item text to an AI node** that flags anomalies — duplicates, unit-price drift, unexpected vendor codes, missing PO references.
5. **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:

```json
{
  "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()` and `DateTime` helpers consistently; mixing string comparison with ISO dates will silently break your `DateFrom` filter.
- **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:**

1. Sign up at [agentroost.app](/en/agents/n8n) — email, Google, Microsoft, or Discord.
2. Pick the **n8n** framework, name your instance, and click Create.
3. Your private n8n editor opens at `https://<your-id>.agentroost.app` in about 2 minutes.
4. Import the workflow JSON (or build it from scratch) — the AI credential is pre-configured.
5. 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](/en/pricing) or [see what's included with the n8n framework](/en/agents/n8n).
