The problem
EU VAT validation looks like a solved problem — VIES is a free, official government endpoint. It just isn't reliable per member state: it goes down, rate-limits aggressively, and offers no fallback of its own. A B2B platform doing reverse-charge validation on every invoice needs an answer even when VIES doesn't have one, and needs proof of the check for the tax authority later, not just a boolean.
The second half of the problem is company data itself: VAT numbers, national company registration numbers and trade-registry identifiers are genuinely distinct identifiers a single company can hold at once, sourced from incompatible national registries with no shared schema.
The approach
The product isn't the VAT lookup — it's what happens around it. A per-country
circuit breaker sits in front of VIES: three failures trip it open, a cooldown
period gates recovery attempts, and while it's open the service serves the
last known-good response instead of an error, with the response labelled so a
caller can distinguish a live check from a cached one. A ?live=true bypass
exists as an explicit, quota-weighted path for callers who need a compliance-
grade check regardless of cache state.
Every VAT check — live or cached — returns VIES's own consultation number, the piece that makes a validation legally usable as proof to a tax authority rather than just an internal boolean.
Company data behind one identifier
Registry ingestion runs as scheduled ETL jobs per country — VIES on demand, UK Companies House daily, French INSEE Sirene weekly (a multi-million-record bulk load), Romanian ONRC monthly, with several more national adapters in varying states of coverage. Every record resolves to a canonical ID derived from a hash of country and registration number, so the same company arriving from two different national sources still lands on one row. Validation failures during ingestion go to a dead-letter table instead of failing the whole job, and distributed locks stop overlapping runs from double-processing a source.
Billing kept out of the request path
Billing runs as its own service, with its own database access and its own Stripe integration — the core API never talks to Stripe directly, and the browser never talks to billing directly either; everything routes through an internally authenticated call. The separation means a billing-provider outage degrades checkout, not VAT validation.
Design decisions worth noting
- Scope discipline on day one. The first version deliberately ingested one country's registry, not two at once — the stated reasoning being that ingesting two national registries simultaneously was the single biggest schedule risk in the initial build.
- No SLA promises the system can't back up. Uptime claims are scoped to what the service itself controls, separated explicitly from VIES's own availability, rather than a single blended number with no load-test evidence behind it.
- Account deletion ordering matters. A subscription must be cancelled with the billing provider before local billing records are purged — reversing that order left customers who no longer existed locally still being billed externally, a real support-and-money bug rather than a cosmetic one.
- Plan resolution by ID, not by label. An early version fell back to matching a Stripe price by its human-readable nickname when a lookup missed — which meant a price's nickname, not its ID, could accidentally grant plan access. Plan resolution now requires the exact price ID.
Case study
See this in production: Building a resilient compliance API on top of an unreliable government service.
Availability
Private. Built and operated in-house; not distributed under an open licence.