# Extracting invoices from Gmail with n8n and Gemini, then checking the IBAN before anyone pays it

> Source: <https://dev.to/member_bb10b304/extracting-invoices-from-gmail-with-n8n-and-gemini-then-checking-the-iban-before-anyone-pays-it-27fe>
> Published: 2026-10-18 08:41:33+00:00

Most invoice automations stop at extraction: an LLM pulls the vendor, total and bank details out of a PDF, and the numbers go straight into a spreadsheet or ledger. The risky part is that an LLM can read a perfectly formatted IBAN with one wrong digit, and nothing downstream notices.

This post walks through a free n8n template that handles the extraction, and a small API I built that handles the checksum part. They're separate pieces: the template doesn't call the API out of the box. I'll show how to add it.

Nodes, in order:

`invoices` with a PDF attached.`needs_review`.
Setup takes about 10 minutes: a Gmail label and filter, a sheet with an `Invoices` tab, and three credentials (Gmail, Sheets, a free Gemini key from Google AI Studio).

Two things that made it more reliable:

Extraction confidence doesn't tell you whether an IBAN is real. The ISO 13616 mod-97 checksum does. I put that, plus a few related checks, in a Cloudflare Worker:

Up to 50 identifiers per batch request. Here's a real response from the keyless demo endpoint:

```
curl "https://invoice-validator.mohamed-bahaa53000.workers.dev/demo/v1/iban?value=DE89370400440532013000"
{
  "iban": "DE89370400440532013000",
  "country": "DE",
  "check_digits": "89",
  "bban": "370400440532013000",
  "valid": true,
  "formatted": "DE89 3704 0044 0532 0130 00",
  "sepa": true,
  "errors": []
}
```

The demo allows 10 calls per IP per day without a key.

Why a Worker rather than an n8n Code node? Mostly reuse: the same check is useful from Make, Zapier or a backend, and it's stateless, so it runs at the edge in milliseconds. It also isolates the slow part. VIES throttles by member state, so when it's unavailable the API returns `checked: false` with a reason instead of a false "invalid".

After Normalize Invoice, add an HTTP Request node:

`/v1/batch` endpoint from the RapidAPI listing`X-RapidAPI-Key` and `X-RapidAPI-Host`

```
{
  "items": [
    { "id": "INV-1042-iban", "type": "iban", "value": "DE89370400440532013000" },
    { "id": "INV-1042-vat", "type": "eu_vat", "value": "DE123456789", "live": true }
  ]
}
```

In n8n, map each value with an expression from the previous node, for example $json.invoice_number, $json.vendor_iban and $json.vendor_vat.

You'll need to add `vendor_iban` and `vendor_vat` to the Gemini schema first. Then treat `valid: false` as "needs review", and `checked: false` from VIES as "verify manually", not as a rejection. A person sees exactly why a row was flagged.

Feedback on either is welcome, especially edge cases where the extraction or the validation gets it wrong.
