Iurii RoguliaIurii Rogulia
AboutServicesPricingProjectsStackReviewsPhrasesBlog
Contact
Iurii ships.

Iurii Rogulia, IT partner for business & fractional CTO. Professionally building software since 2001.

Think of a number
PricingQuality checklistPrivacy PolicyCookie Policy

Business

TMI Iurii Rogulia
VAT ID: FI29845875
DUNS: 368664211
Lappeenranta, Finland 🇫🇮

[email protected]
Back to projects

Iurii Automates: n8n Community Node — EU VAT Validation in Workflows

August 6, 2026

Community node that puts VIES VAT validation and EU rate lookups inside n8n, so an onboarding or invoicing workflow can decide on a real VAT number instead of a well-formed one.

Source code

Stack

TypeScriptNode.jsn8n

Services

npmGitHub Actionsvatnode API

Topics

Workflow AutomationNo-CodeIntegrationsDeveloper ToolsOpen SourceTax/VAT

Key Results

  • A workflow can act on whether a VAT number is real, not merely well-formed
  • Free operations — format check and rate lookup — run with no account and no quota
  • The VIES consultation number lands in the workflow as audit evidence for the customer record
  • Available to any n8n AI agent as a callable tool, no glue code
n8n Community Node — EU VAT Validation in Workflows

The Business Problem

The people who most need a VAT number checked are usually not the people who can call an API. A finance operations lead onboarding B2B customers, an agency automating invoicing, a founder wiring together a signup flow — they build in n8n, and in n8n a VAT number was just a string.

What they could do with a regular expression is confirm a number looks like a German VAT number. What decides whether an invoice carries VAT is whether that number is actually registered — and the official EU register, VIES, speaks SOAP over an endpoint that goes down country by country. Reaching it from a no-code tool means an HTTP Request node, hand-built XML, and a parser that breaks the first time a country is offline.

So the check either doesn't happen, or it happens once by hand at the wrong end of the month.

The Solution

I published a community node that makes VAT validation an ordinary step in a workflow. Two resources, four operations, and a deliberate split between what costs nothing and what costs a request:

VAT Number — Check Format runs entirely offline against the country's pattern: no key, no network call, no quota. Validate verifies the number against the live VIES register through vatnode and returns validity, the registered company name and address, the registration date, and the VIES consultation number when the account has a requester VAT configured.

VAT Rate — Get returns the standard, reduced, super-reduced and parking rates for one country plus its VAT number format; Get Many returns the same for every covered country as one n8n item each. Both are free and need no account.

The intended shape of a workflow is that order: Check Format filters typos for nothing, Validate confirms only the survivors against VIES, and an IF node downstream routes on valid while the consultation number is written to the customer record as evidence. Nobody spends quota on a mistyped number.

The node is also declared usableAsTool, so an n8n AI agent can call VAT validation directly as one of its tools — the same capability the MCP server gives Claude and Cursor, delivered inside the automation platform instead of the desktop assistant.

Results

MetricValue
Release[email protected], 5 August 2026
Surface2 resources, 4 operations
Free of quota3 of 4 operations — format check and both rate ops
Source251 lines node + 64 lines credential, TypeScript
CountriesEU-27 + XI via VIES, ~45 for rate lookup
CI secrets0 — npm Trusted Publishing via OIDC
ProvenanceSigstore-signed, publicly auditable per release
LicenseMIT

This is the automation-platform surface of the same VAT stack that also ships as a SaaS API, an open rate dataset, an MCP server and a WooCommerce plugin. Each one meets a different person where they already work: the developer in code, the AI assistant in chat, the shop owner in WordPress, and here the operations lead in a workflow canvas.

Under the Hood

For the technically curious, here is how the core pieces are built.

A credential test that costs nothing

n8n calls the credential test whenever someone saves a key, and a naive implementation validates a real VAT number — which means every save spends a request from the user's monthly quota, and a user fiddling with settings burns their free plan before running a single workflow.

The test sends the reserved XX0000001 number instead. No country uses the XX prefix, so the API rejects it on format before it ever reaches VIES: the request proves the key authenticates and stops there. A working key and a rejected key are still perfectly distinguishable — only the quota is spared.

One bad item must not kill the batch

An n8n node receives a batch of items, and in a VAT workflow the batch is a customer list where one row will have a typo. The loop isolates failures per item and honours the user's continueOnFail setting, so a bad row becomes an item with an error field rather than a dead execution:

} catch (error) {
  if (this.continueOnFail()) {
    returnData.push({ json: { error: (error as Error).message }, pairedItem: { item: i } });
    continue;
  }
  // Input problems are ours, not the API's — keep them out of NodeApiError.
  if (error instanceof NodeOperationError) {
    throw new NodeOperationError(this.getNode(), error.message, { itemIndex: i });
  }
  throw new NodeApiError(this.getNode(), error as JsonObject, { itemIndex: i });
}

The distinction between NodeOperationError and NodeApiError matters in the UI: the first tells the user their input is wrong, the second tells them the upstream failed. Collapsing both into one error type sends people debugging their credentials when they actually typed an empty VAT number.

Every output item also carries pairedItem, so n8n can trace each result back to the row it came from — without it the "item linking" breaks and downstream nodes lose the connection to the original customer.

The parameter typed as a string that isn't one

An n8n parameter declared type: 'string' is not guaranteed to arrive as a string. Users fill these with expressions, and {{ $json.vatId }} on an item that has no vatId resolves to undefined. Calling .toUpperCase() on it crashes the node with a stack trace instead of a readable message:

function toStringParam(value: unknown): string {
  return value === undefined || value === null ? "" : String(value);
}

Coerced to an empty string, it flows into the same explicit emptiness check that produces "The VAT number is empty" — a message the person who wrote the expression can act on.

Country patterns generated at build time

The format check needs every country's VAT pattern, and hardcoding 45 regexes into the node makes them a second source of truth that quietly drifts from the API. A build-time script generates countries.generated.ts from the same open eu-vat-rates-data package the rest of the stack uses, so a pattern is corrected in one place and reaches the node on the next release.

One quirk survives the generation on purpose: VIES writes Greece as EL, while the rate endpoint keys it as GR. Both are accepted on input and mapped where needed — a user should not have to know which EU service decided which.

Iurii RoguliaAvailable

Need something similar?

I build custom solutions — from APIs to full products. Let's talk about your project.

View all projects

Related projects

vatnode-mcp — Official MCP Server for EU VAT Validation
vatnode-mcp — Official MCP Server for EU VAT Validation
May 20, 2026
vatnode-mcp — Official MCP Server for EU VAT Validation

Open-source MCP server that lets Claude Desktop, Cursor and other MCP clients validate EU VAT numbers and look up rates directly in chat.

Stack

TypeScriptNode.js

Libraries

Zod

Services

vatnode API

Topics

MCPAI ToolingDeveloper ToolsOpen SourceTax/VAT
eu-vat-rates-data — Free & Open-Source EU VAT Rates Dataset
eu-vat-rates-data — Free & Open-Source EU VAT Rates Dataset
February 25, 2026
eu-vat-rates-data — Free & Open-Source EU VAT Rates Dataset

Free, open-source EU VAT rates for all 27 member states + UK. Published as native packages for npm, PyPI, Packagist, Go, and RubyGems.

Stack

TypeScriptPythonPHPGoRuby

Services

GitHub ActionsnpmPyPIPackagistRubyGems

Topics

Open SourceTax/VATAutomation

Related posts

Publishing One Package to Five Registries with GitHub Actions
March 27, 2026· 10 min
Publishing One Package to Five Registries with GitHub Actions

How to publish one dataset to npm, PyPI, Go Module, RubyGems, and Packagist automatically with GitHub Actions — architecture, versioning, and per-ecosystem

Stack

TypeScriptPythonPHPGoRuby

Services

GitHub ActionsnpmPyPIPackagistRubyGems

Topics

Open SourceTax/VATAutomation
linkinator Tutorial: Find Broken Links in Next.js Sites
April 29, 2026· 11 min
linkinator Tutorial: Find Broken Links in Next.js Sites

Use linkinator to catch broken links before production: local URL rewriting, --silent flag, GitHub Actions integration, and false positive patterns for

Stack

Node.jsTypeScript

Topics

Developer ToolsSEOCI/CDProcess