Learn what the userError field really tells you and why caching the wrong answer can cost you more than a fine.
INTRODUCTION
You run a small EU webshop. You need to know if a customer’s VAT number is valid before you apply a zero-rate intra-EU sale. You call the VIES service, get an HTTP 200, and you think the number is good. You store that result in a cache for the next 24 hours. Later you discover the number was actually invalid and you have under-charged VAT. The fine arrives, the stress builds, and you wonder where you went wrong. The problem is not the HTTP status code. It is what you do with the response body.
THE PROBLEM WITH CACHING
VIES always returns an HTTP 200 when the service is reachable, even when something inside the system is failing. The real signal lives in the userError field. When the number is truly valid the field says VALID. When it is truly invalid the field says INVALID. Anything else means the validation could not be completed at that moment. Those other values are:
- MS_UNAVAILABLE. the member state’s system is down
- MS_MAX_CONCURRENT_REQ. too many requests to that member state
- GLOBAL_MAX_CONCURRENT_REQ. overall VIES limit hit
- SERVICE_UNAVAILABLE. VIES itself is temporarily offline
- TIMEOUT. the request did not finish in time
If you treat every 200 as a confirmation and you cache the result, you might store MS_UNAVAILABLE as “invalid”. The next request for the same number will be served from the cache and you will refuse a legitimate sale. Or worse, you might store a timeout as “valid” and later sell to a fraudulent number. Both outcomes hurt your business.
WHAT HAPPENS WHEN YOU GET IT WRONG
Imagine you cache a timeout as VALID. A week later you use that cached answer to process an order. The VAT number is actually invalid, so you apply the zero-rate incorrectly. Tax authorities can recover the missing VAT, add interest, and issue a penalty. The cost is not just the fine; it is the time you spend defending the mistake, the damage to your reputation with customers who notice the error, and the possible loss of future sales when shoppers lose trust.
On the other side, caching MS_UNAVAILABLE as INVALID blocks genuine EU customers. You lose the order, you lose the margin, and you may push the buyer to a competitor who got the validation right. Over months, those lost sales add up to a real revenue leak that is hard to trace back to a caching decision.
HOW TO HANDLE IT CORRECTLY
The fix is simple but must be baked into your integration:
1. Always read the userError field from the VIES JSON response.
2. Treat only VALID as a confirmed good number. Treat only INVALID as a confirmed bad number.
3. For any other userError value, do not cache a definitive answer. Either retry after a short back-off or mark the request as “unknown” and ask the user to verify the number through another channel.
4. If you must cache something for performance, cache the raw userError value itself, not a boolean derived from it. Then your logic can re-evaluate the cached value each time it is read.
By separating the transport status (HTTP 200) from the service-level outcome (userError) you avoid mistaking a temporary glitch for a permanent fact. Your VAT validation becomes reliable, and you keep the confidence that your prices are correct and your filings are accurate.
CONCLUSION
VAT compliance is not about calling an API; it is about trusting the answer you receive. When you ignore the userError field you turn a service hiccup into a costly mistake. Treat the VIES response with the respect it deserves, and you will sleep better knowing your numbers are right.
Get the free VAT compliance checklist at https://insightoperator.org