---
title: "The zero-rate check: the VAT number you validated once, two years ago"
subtitle: "Every EU business customer's VAT number checked before the invoice leaves and again each quarter, with the EU's own proof code filed next to the sale"
url: "https://rxed.ai/blueprints/eu-vat-number-recheck.html"
id: "eu-vat-number-recheck"
version: "1.0"
published: "2026-09-19"
kind: "workflow"
setup_effort: "An afternoon"
setup_hours: "3-6"
running_cost_usd_per_month: "0-12"
paid_software_usd_per_month: "0"
time_saved: "not a weekly time saving - it is the assessment that never lands"
time_saved_basis: "the exposure per missed check is the VAT on that invoice plus interest - 21% standard rate in Belgium, 19% in Germany, 20% in France - against EUR 0 of software and a VPS you may already run; paid VAT-validation APIs wrapping the same free endpoint publish plans from EUR 5.99/month for 500 lookups (VAT Sense, checked 19/09/2026)"
runs_by: "machine"
data_location: "your-server"
data_location_note: "The ledger holds your customer list and your own tax position on each sale. It calls one public EU endpoint and stores the answer locally; no customer data is handed to a third-party validation service that then knows exactly who you sell to."
first_to_break: "the day VIES answers MS_UNAVAILABLE and the workflow records it as invalid"
failure_signal: "loud-but-wrong"
problem: "When you sell to a business in another EU country you leave the VAT off the invoice, because they gave you their tax number. If that number was cancelled since the day you checked it, the tax office can come back years later and ask you for the money."
fix: "Every business customer's number is checked automatically before the invoice goes out, and again every quarter, against the official EU service. Each check is stored with its date and the reference code the EU gives back, so the proof sits with the sale instead of in someone's memory."
tools: ["VIES", "n8n", "Grist", "NocoDB", "Docker"]
tech: ["VIES", "n8n", "Grist", "Docker"]
repos:
  - name: "n8n"
    url: "https://github.com/n8n-io/n8n"
  - name: "Grist"
    url: "https://github.com/gristlabs/grist-core"
  - name: "NocoDB"
    url: "https://github.com/nocodb/nocodb"
categories: ["Invoicing", "Bookkeeping", "Documents & archive", "Self-hosted", "n8n"]
source: "RXed AI News — https://rxed.ai"
---
# The zero-rate check: the VAT number you validated once, two years ago

> Every EU business customer's VAT number checked before the invoice leaves and again each quarter, with the EU's own proof code filed next to the sale

## Before you start

- Your own VAT number - without it in the request the EU returns no consultation number, and you have a check you cannot prove you made
- A list of which customers you actually invoice without VAT as a business in another EU member state - usually 5 to 50 rows, not thousands
- An always-on machine or a mini-VPS at EUR 5-12/month running Docker
- Your accountant told, once, that this ledger exists - they own the rest of the exemption file

## What breaks first

the day VIES answers MS_UNAVAILABLE and the workflow records it as invalid. Half your customers get flagged dead on a Tuesday because a member state's system was down, and after two of those mornings you stop trusting the flags. Branch on three outcomes - valid, invalid, unreachable - from the first version, and count inconclusive checks per month.

Every EU business customer's VAT number checked before the invoice leaves, and again every quarter, with the EU's own reference code filed next to the sale.

 

### The problem

 You sell to a company in another EU country. They give you their VAT number, you leave the VAT off the invoice, everyone is happy. Three years later an inspector opens that file and asks one question: was that number valid on the day you invoiced, and can you show it. Since 1 January 2020 this stopped being paperwork. Directive (EU) 2018/1910 — the third of the "quick fixes" — made a valid VAT identification number, communicated by the customer, a substantive condition for zero-rating an intra-Community supply, not a formality you can repair afterwards. If the number was cancelled, or was never valid, the exemption is exposed and the VAT is yours to pay, on a sale whose margin is long gone. Most small businesses check the number once, at onboarding, on the EU website, and never again. Companies deregister, go bankrupt, restructure, change legal form. The number rots quietly and nothing tells you.

 

### The architecture

 
 [ New customer ]  or  [ Invoice about to go out ]  or  [ Quarterly sweep ] 
         ▼ 
 [ n8n (self-hosted) — one HTTP call ] 
         │  GET ec.europa.eu/taxation_customs/vies/rest-api/ms/<MS>/vat/<number> 
         │  ?requesterMemberStateCode=<yours>&requesterNumber=<yours> 
         ▼ 
 [ The check ledger (Grist) — one row per check, never overwritten ] 
         ├─ valid / invalid, date, name and address as returned 
         ├─ requestIdentifier — the EU's own consultation number 
         └─ the raw JSON, stored as-is 
 
 valid → the customer stays zero-rateable 
 invalid → flag on the customer, mail to you, nothing sent to the customer by machine 
 service down → retry, never recorded as "invalid"
 

 Three triggers, one call, one append-only ledger. The design decision is the ledger: you are not storing "this customer is valid", you are storing "on this date the EU answered this". A status field can be overwritten. A dated row with a consultation number is evidence.

 

### Tool choices — and why

 VIES, direct. The European Commission runs the VAT Information Exchange System and now exposes it as plain REST: GET /taxation_customs/vies/rest-api/ms/BE/vat/0417497106 returns JSON, no API key, no registration, no cost. Checked live on 19/09/2026: it answers isValid , requestDate , name , address . Add requesterMemberStateCode and requesterNumber — your own VAT number — and the response fills requestIdentifier with a consultation number (format WAPIAAAA… ). Without those two parameters that field comes back empty. That string is the point of the whole exercise: it is the Commission's own proof that you asked, on that date. Every paid VAT-validation API in this space is a wrapper around the same free endpoint.

 n8n (Sustainable Use License, free for internal business use, 204k stars, v2.39.5 released 14/09/2026) makes the call, on a webhook from your invoicing tool and on a cron for the quarterly sweep. An HTTP node, an IF node, an append node. If you already run n8n for another blueprint in this library, this is one more workflow on the same box, not another box.

 Grist (Apache-2.0, grist-core, active — v1.7.19) is the ledger. A spreadsheet your accountant can read and filter, with a real API underneath and Python formulas for the "last checked more than 90 days ago" column. NocoDB (2026.09.0) does the same job if you prefer its interface; move the date logic into n8n in that case. Either runs in Docker next to n8n.

 Monthly cost. Software EUR 0. VIES EUR 0. A small VPS at roughly EUR 5–12/month if you don't already have one, and EUR 0 if you do. Compare with the outcome you are insuring against: one disallowed zero-rating is the VAT on that invoice plus interest, at 21% in Belgium, 19% in Germany, 20% in France.

 

### Setup outline

 1. Pull your customer list. Keep only the ones you invoice without VAT because they are a business in another EU member state. For most small businesses that is 5 to 50 rows, not thousands. 
 2. Build the ledger table: customer, country code, VAT number, checked_at, is_valid, returned_name, returned_address, consultation_number, raw_response, trigger (onboarding / pre-invoice / quarterly). Append-only — no row is ever updated. 
 3. In n8n, one HTTP Request node to the REST endpoint, with your own VAT number in the two requester parameters. Test it against a number you know is good and one you know is bad before anything else is wired. 
 4. Branch on three outcomes, not two: valid, invalid, and unreachable. Unreachable retries after an hour and alerts you if it is still unreachable after three tries. 
 5. Trigger 1 — onboarding: a form or a webhook when you add an EU business customer. No valid check, no zero-rated invoice. 
 6. Trigger 2 — pre-invoice: if your invoicing tool has a webhook, check the number when the invoice is created. Cheap, and it catches the deregistration that happened last month. 
 7. Trigger 3 — quarterly sweep, spread over the night: re-check every active EU business customer before you file the EC Sales List for that quarter. 
 8. One page for your accountant: customer, last check date, result, consultation number. That page is what gets attached to the file when someone asks.

 

### Pitfalls — the real ones

 "Service unavailable" is not "invalid". VIES is not one database, it is a router to 27 national systems, each with its own maintenance windows and outages, and it returns MS_UNAVAILABLE when a member state does not answer. Code that treats a timeout as a failed check will, on a bad Tuesday, flag half your customers as dead. Retry with a delay and record the attempt as inconclusive. 
 Some countries return no name. Several member states answer valid/invalid only, with --- where the name and address should be. Never build a rule that requires the returned name to match your customer record — you will block real customers in those countries. Use the name when it comes, log it when it doesn't. 
 The consultation number only appears if you ask for it. Without requesterMemberStateCode and requesterNumber , requestIdentifier is an empty string and you have a check you cannot prove you made. This is the single most common way this gets built wrong. 
 A valid number is not the whole exemption. The quick fixes also require the supply to be correctly reported in your EC Sales List, and transport of the goods to be evidenced. This blueprint closes one condition, the one nobody automates. Your accountant owns the rest — show them the ledger and ask what else their file needs. 
 Don't hammer it. A nightly sweep of 40 customers is nothing. A loop that re-checks your whole list on every page load is abuse of a free public service, and it will be throttled. 
 Screenshots rot. A folder of PNGs from the VIES website is what most businesses have, and it survives exactly as long as the person who named the files. Store the JSON response and the consultation number as text, in a table, with the date.

 If you are in the US: the mechanism is different but the failure is the same shape — a resale or exemption certificate on file that has expired or was never valid, discovered years later in an audit. Same ledger, same re-check rhythm, different source of truth. Check with your accountant which states require re-validation and on what cycle.

 

### Verified repos

 
 [n8n](https://github.com/n8n-io/n8n) — Sustainable Use License (free self-hosted internal use), 204k stars, v2.39.5 released 14/09/2026 
 [Grist](https://github.com/gristlabs/grist-core) — Apache-2.0, active, v1.7.19 
 [NocoDB](https://github.com/nocodb/nocodb) — Sustainable Use License, release 2026.09.0 (alternative to Grist) 
 [VIES](https://ec.europa.eu/taxation_customs/vies/) — European Commission, free REST API, no key, verified live 19/09/2026
 

 
 Get the next blueprint as it publishes — free, practical, verified tooling only.

 [Subscribe](https://subscribe.rxed.ai) · Want it audited or extended? DM [@RXed_EU](https://x.com/RXed_EU)
