RevClamp Enterprise B2B REVENUE ASSURANCE Start Free
ENGINEERING DEEP-DIVE · EU B2B VAT & VIES CIRCUIT BREAKER
← Back to Engineering Blog • EU B2B VIES & REVERSE-CHARGE • 4 min read

VIES returns HTTP 200 when it's down, and I charged the wrong VAT because of it

When a German or French enterprise buyer enters a valid VAT number at checkout, they expect a 0% intra-community reverse-charge invoice once cross-border B2B supply conditions (sellerCountry !== buyerCountry) are met.

What caught me off guard is how the European Commission's VIES REST API (POST /taxation_customs/vies/rest-api/check-vat-number) behaves when a national tax registry is overloaded or unreachable: VIES returns HTTP 200 OK with actionSucceed: false and errorWrappers in the JSON body (or valid: false with userError in normalized adapters).

If your checkout code checks res.ok and then checks !body.valid before inspecting actionSucceed and errorWrappers, you treat a government database outage as an unregistered tax ID—and falsely charge your B2B customer 17%–27% VAT.

EU VIES HTTP 200 Soft-Failure Circuit Breaker and Reverse-Charge Vault Flowchart

1. The HTTP 200 OK soft-failure payload

Here is the structure returned by the VIES REST endpoint (POST https://ec.europa.eu/taxation_customs/vies/rest-api/check-vat-number) on an HTTP 200 OK error wrapper response:

{
  "actionSucceed": false,
  "errorWrappers": [
    {
      "error": "MS_MAX_CONCURRENT_REQ"
    }
  ]
}

Because the HTTP status is 200, standard HTTP retry middleware never triggers. And because body.valid is omitted (undefined) when actionSucceed is false (or mapped to valid: false alongside userError in adapter layers), any parser that checks !body.valid before inspecting transient outage codes in errorWrappers[] (MS_MAX_CONCURRENT_REQ, MS_UNAVAILABLE, SERVICE_UNAVAILABLE, GLOBAL_MAX_CONCURRENT_REQ, TIMEOUT—as opposed to permanent input errors like INVALID_INPUT or INVALID_REQUESTER_INFO) silently misclassifies a registry outage as an invalid VAT registration.

2. Prefix traps across 27 EU member states + XI

Before an order ever reaches VIES, raw checkout input across 27 EU member states plus Northern Ireland (XI for cross-border B2B goods under the Windsor Framework — 28 VAT prefixes) creates a second wave of false rejections:

Checkout VAT Scenario Naive VIES Integration Financial / Tax Impact RevClamp Circuit-Breaker Action
valid: true Cross-Border (DE → FR) Applies 0% without consultation ID Missing consultation reference for tax audit Approves 0% reverse-charge & archives SHA-256 consultation digest
HTTP 200 + MS_MAX_CONCURRENT_REQ Treats !body.valid as unregistered VAT Falsely charges 17%–27% VAT; cart abandonment Trips per-country breaker & queues PENDING_ASYNC_RETRY (up to 4 attempts)
Greece ISO GR123456789 Input Sends GR prefix directly to VIES Rejected by VIES with INVALID_INPUT Auto-normalizes GR → EL (EL123456789) prior to VIES lookup
Northern Ireland XI123456789 Goods Rejects XI as post-Brexit GB Strips valid Windsor Framework zero-rating Admits XI prefix under 28-prefix syntax & VIES registry rules
valid: false + Empty errorWrappers Blindly finalizes checkout or crashes Uncollected VAT liability or lost B2B order Places order in HOLD_INVALID_VAT (vat_audit:invalid_vat_id) for review

3. Separating checkout admission from registry verification

My first instinct when seeing MS_MAX_CONCURRENT_REQ was to retry the request synchronously during checkout. That backfired immediately: holding a checkout request open while a national tax server times out causes buyer drop-off, and retrying immediately adds load to a member state that is already rate-limiting concurrent connections.

A production B2B VAT pipeline separates checkout admission from registry verification across three states:

  1. Verified Active (valid: true + cross-border B2B supply): An active VIES consultation (requestIdentifier / consultationNumber) is a necessary condition alongside sellerCountry !== buyerCountry for 0% cross-border reverse-charge; archive the consultation timestamp, requester/buyer VAT IDs, and SHA-256 response digest (VERIFIED_ZERO_RATED, or VERIFIED_MISSING_CONSULTATION_ID hold if the consultation ID is absent).
  2. Member-State Soft Failure (transient errorWrappers present): Never charge penalty VAT and never block checkout. Place the order in PENDING_ASYNC_RETRY (vat_audit:pending_vies_retry), trip a per-member-state circuit breaker (so a DE slowdown never delays FR or NL buyers), and schedule bounded exponential-backoff retries (60s * 2^(attempt-1), up to 4 total attempts before escalating to HOLD_MANUAL_REVIEW_RETRY_EXHAUSTED).
  3. Truly Invalid (valid: false with empty errorWrappers, or permanent errors): When a VAT number is unregistered (valid: false with empty errorWrappers) or fails permanent validation (INVALID_INPUT, VAT_BLOCKED, or seller requester configuration error INVALID_REQUESTER_INFO), place the order in HOLD_INVALID_VAT (vat_audit:invalid_vat_id) so finance can request a valid VAT ID, fix requester credentials, or collect standard domestic/destination VAT.
RevClamp Enterprise — B2B Revenue & Compliance Guardrails
Open Free 20-ID EU VIES Batch Validator → Start Free Workspace (5,000 events/mo)