Extracting invoices from Gmail with n8n and Gemini, then checking the IBAN before anyone pays it A developer built a free n8n template that uses Gemini to extract vendor, total and bank details from Gmail invoices, paired with a separate Cloudflare Worker API that validates IBANs via the ISO 13616 mod-97 checksum and EU VAT numbers through VIES. The API batches up to 50 identifiers per request, returns a reason rather than a false "invalid" when VIES is throttled, and flags failed checks as "needs review" so a human sees why a row was flagged. 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.