Home / Use cases / Custom

Custom

EU VAT compliance for SaaS billing

Handle EU VAT for SaaS subscriptions. Validate customer VAT numbers at signup, determine B2B vs B2C tax treatment, and generate compliant invoices with the correct VAT amount.

EU VAT treatment for SaaS subscriptions hinges on a single question: is the customer a taxable person (business) or a private consumer? Under EU VAT Directive 2006/112/EC Article 196, if the buyer is a VAT-registered business in another EU member state, the supply is zero-rated and the buyer accounts for VAT under the reverse-charge mechanism. If the buyer is a consumer, you must charge VAT at the rate applicable in their country of residence, which is where the EU One Stop Shop (OSS) scheme becomes relevant.

At signup, call GET /api/v1/validate/:country/:vat to confirm the VAT number. A response with status: 'active' classifies the account as B2B: set tax_treatment: 'reverse_charge' in your billing database and pass tax_exempt: 'reverse' to Stripe. A missing or invalid VAT number classifies the account as B2C: determine the customer's country from their IP or billing address and apply the local OSS VAT rate. Store the company_name and company_address from the TaxID response on the customer record; these fields must appear on compliant EU VAT invoices.

Re-validate VAT numbers periodically — VIES supports querying at any time, and a business can deregister. A safe approach is to re-validate on each subscription renewal and cache the result for 24 hours (matching TaxID's cache window for active numbers). If a previously valid number returns invalid on renewal, freeze the reverse-charge treatment, notify the customer, and revert to charging VAT until a valid number is supplied.

Implementation steps

  1. 1

    Collect VAT number at signup

    Add an optional VAT number field to your signup form and show it conditionally when the user selects an EU country. Use a client-side regex to catch obvious format errors before the form is submitted (e.g. DE must be DE + exactly 9 digits), reducing unnecessary API calls and giving instant feedback to users who mistype their number.

  2. 2

    Validate and classify customer (B2B/B2C)

    On form submission, call GET /api/v1/validate/:country/:vat from your server. Set the customer's tax_treatment column to 'reverse_charge' when status === 'active', 'standard_rate' when status === 'invalid' or no VAT number was provided, and 'pending' when status === 'service_unavailable' so a background job can retry within the hour. Store the cached flag from the response to distinguish a live VIES confirmation from a cached result.

  3. 3

    Configure billing engine for correct tax

    Pass the tax classification to your billing engine: for reverse_charge customers, disable VAT calculation and add a 'Reverse charge — VAT to be accounted for by the recipient' line to the invoice per EU invoice Directive Article 226(11a). For B2C customers, apply the destination-country VAT rate and flag the invoice for OSS reporting. Using Stripe Tax, this translates to tax_exempt: 'reverse' for B2B and a standard tax_behavior: 'exclusive' subscription for B2C.

  4. 4

    Generate compliant PDF invoices

    EU-compliant invoices must include the supplier's VAT number, the buyer's VAT number (for reverse-charge invoices), the company_name and company_address from the TaxID validation response, and the reverse-charge annotation. Render these fields from your customer record into your PDF template; missing any of them can cause the invoice to be rejected by the buyer's tax authority and denied as input VAT credit.

Code example

Node.js

const res = await fetch(
  'http://localhost:3000/api/v1/validate/DE/DE123456789',
  { headers: { 'Authorization': 'Bearer YOUR_API_KEY' } }
);
const { valid, status, company_name, company_address } = await res.json();

if (valid) {
  console.log(`Valid EU business: ${company_name}`);
} else if (status === 'service_unavailable') {
  // VIES is temporarily down — retry or allow with manual check
  console.log('VIES unavailable — check back in a few minutes');
} else {
  console.log('Invalid VAT number — charge local tax rate');
}

Python

import requests

res = requests.get(
    "http://localhost:3000/api/v1/validate/DE/DE123456789",
    headers={"Authorization": "Bearer YOUR_API_KEY"}
)
data = res.json()

if data["valid"]:
    print(f"Valid: {data['company_name']}")
elif data["status"] == "service_unavailable":
    print("VIES temporarily unavailable")
else:
    print("Invalid VAT number")

API response

The TaxID API returns a consistent JSON response for every validation request:

200 OK (active)valid
{
  "valid": true,
  "status": "active",
  "country_code": "DE",
  "vat_number": "123456789",
  "company_name": "Example GmbH",
  "company_address": "Musterstraße 1, 10115 Berlin",
  "request_date": "2026-05-10T00:00:00.000Z",
  "cached": false,
  "request_id": "req_01j..."
}

Error handling

The API uses a consistent Stripe-style error format. Always handle service_unavailable separately — VIES has occasional downtime and you should not reject valid customers during outages.

active

VAT number is valid and the business is registered

invalid

VAT number format is wrong or not registered in VIES

service_unavailable

VIES or the national system is temporarily down — retry later

Further reading

Evaluating EU VAT APIs? Compare TaxID with:

Ready to implement?

Get your free API key and start validating EU VAT numbers today.

Get free API key

Related use cases

DAC7 marketplace seller VAT verification

Verify seller VAT numbers during marketplace onboarding for DAC7 compliance. EU platforms must colle...

Bulk VAT number validation via CSV import

Validate hundreds of EU VAT numbers in batch using parallel API requests. Ideal for CRM data cleansi...

Validate German USt-IdNr. (DE VAT numbers)

Specifically validate German Umsatzsteuer-Identifikationsnummern (USt-IdNr.) via VIES. Understand th...