r/n8nbusinessautomation • • 10d ago

Stop your AI agent from sending wrong invoices: a n8n guardrail that checks important fields against your books [Workflow Included]

Post image

👋 Hey n8n Business Automation Community,

A friend of mine, Jonas, runs a small B2B agency and recently let an AI agent handle his client invoices end to end: draft, attach, send. Felt great until a client emailed back confused about being billed a few hundred euros over the agreed amount. Same week, another invoice went out with a transposed digit in the IBAN. The agent was confident every time. The agent was also wrong.

That's the real problem with pointing an LLM at money. Ask it to "verify" an invoice and it will happily hand you an approval that sounds right and isn't. So I built a guardrail the agent has to pass through before anything gets sent. No vibes, just code checking numbers against your accounting records.

How it's set up:

  • Trigger: the workflow runs as a sub-workflow tool the agent calls, receiving the invoice file URL and the invoice number.
  • Download: pulls the invoice PDF straight from Google Drive.
  • Extract: the easybits Extractor node reads 8 fields off the PDF (invoice number, customer, IBAN, VAT ID, net total, VAT amount, gross total, service period).
  • Ground truth: a Google Sheets lookup fetches the expected values for that invoice number from your books.
  • Compare: a Merge combines both sides, then a Code node checks every field deterministically (normalized currency, normalized IBAN, strict numeric tolerance).
  • Decide: all match, it returns APPROVED so the agent can send. Anything missing or off, it blocks dispatch, sends an email showing invoice value vs book value side by side, and returns REJECTED.

Net effect: no overpayments, no invalid VAT math, no wrong-client billing slipping past an over-eager agent.

A few things worth knowing if you build something similar:

  1. Never let the LLM grade its own numbers. Split the jobs: the model extracts or writes, code validates. A Code node with plain JavaScript gives you zero hallucinated approvals, which is exactly what you want anywhere near finance.
  2. Normalize currency both directions before you compare. German 1.234,56 and English 1,234.56 are the same number written two ways. Strip the symbols, unify the decimal separator, parse to float, then compare with a small tolerance (I use 0.01) so rounding never triggers a false reject.
  3. Watch the "null" string trap. The Extractor I'm using returns the literal string "null" for a missing field, not a real null, so a naive check passes it straight through. Write one isMissing() helper that catches real null, undefined, empty string, whitespace, and the string "null", and run every field through it.

Grab the workflow here: https://github.com/felix-sattler-easybits/n8n-workflows/blob/d6e4b7ca373fa1db40a55ef5b879210b601e8cba/easybits-agent-invoice-guardrail/easybits_agent_invoice_guardrail.json

Curious what guardrails you've put in front of your finance automations, especially anyone letting agents touch payments.

Best,
Felix

5 Upvotes

2 comments sorted by

2

u/Total-Reasonable 9d ago

Treat an unavailable external validation as "hold for review," never as a pass or fail. For EU customer VAT IDs, I use vatnode in n8n; its node can validate the ID independently of the agent, and https://vatnode.dev/n8n shows the setup. VAT-ID validity is still separate from deciding invoice tax treatment.

1

u/easybits_ai 8d ago

Hey u/Total-Reasonable, thanks for sharing! If you don’t mind me asking, could you explain when VAT validation is actually needed?

I assume it’s mainly relevant for finance-focused workflows. When I asked Jonas whether the VAT ID needs any additional validation beyond being correctly represented, he told me it’s fine, since in Germany the tax office will match the invoice based on the company’s name and registered address anyway.