Value Added Tax (VAT) is a consumption tax levied on goods and services at each stage of production and distribution. For developers building billing systems, e-commerce platforms, or SaaS products that serve EU customers, VAT compliance means getting four things right: knowing the applicable rate, validating the customer's tax status, applying the correct tax treatment, and generating compliant invoices.
What VAT Actually Means for Your Code
VAT is not just a percentage you multiply by the price. It's a legal determination that depends on: (1) the type of goods or services sold, (2) the location of the supplier, (3) the location of the customer, and (4) whether the customer is a business or a consumer. Getting any of these wrong creates compliance risk.
| Decision | What your code needs to determine |
|---|---|
| Supply type | Is this a good, service, or digital service? |
| Customer location | Which country's VAT rules apply? |
| Customer type | B2B with valid VAT number, or B2C consumer? |
| Rate | Standard, reduced, or zero-rated? |
| Mechanism | Normal VAT, reverse charge, or OSS? |
VAT Rate Lookup vs VAT Number Validation
These are two separate API calls that serve different purposes. Rate lookup retrieves the applicable VAT percentage for a country and product category. VAT number validation checks whether a business customer has a valid EU VAT registration, which determines if reverse charge applies.
const TAXID_API = 'https://api.taxid.dev/v1';
const headers = { 'Authorization': `Bearer ${process.env.TAXID_API_KEY}` };
// 1. Look up VAT rates for a country
async function getVatRates(countryCode) {
const res = await fetch(`${TAXID_API}/rates?country=${countryCode}`, { headers });
return res.json(); // { standard_rate: 20, reduced_rates: [10, 5.5] }
}
// 2. Validate a business customer's VAT number
async function validateVatNumber(vatNumber) {
const res = await fetch(`${TAXID_API}/validate`, {
method: 'POST',
headers: { ...headers, 'Content-Type': 'application/json' },
body: JSON.stringify({ taxId: vatNumber })
});
return res.json(); // { valid: true, companyName: 'ACME GmbH' }
}
// 3. Determine tax treatment
async function determineTaxTreatment({ customerCountry, vatNumber, isDigitalService }) {
const isEU = EU_COUNTRIES.includes(customerCountry);
if (!isEU) return { rate: 0, mechanism: 'out_of_scope' };
if (vatNumber) {
const { valid } = await validateVatNumber(vatNumber);
if (valid) return { rate: 0, mechanism: 'reverse_charge', vatNumber };
}
const { standard_rate } = await getVatRates(customerCountry);
return { rate: standard_rate, mechanism: 'standard' };
}B2B vs B2C: The Fundamental Split
The most important classification in EU VAT is whether you're selling to a business (B2B) or a consumer (B2C). For B2B sales with a valid VAT number, the reverse charge mechanism typically applies: the buyer accounts for VAT in their own country, and you issue a zero-rated invoice. For B2C sales, you must collect VAT at the customer's local rate.
Warning
Never grant tax exemption based solely on a customer self-declaring as a business. Always validate the VAT number through VIES or an API. A checked checkbox is not compliance evidence.
Implementing VAT in Checkout
- →Collect customer country at checkout start, before showing price
- →Show a VAT number field for business customers — make it optional but encourage it
- →Validate the VAT number server-side before finalizing the order
- →Compute tax treatment server-side — never trust client-side calculations for billing
- →Store the validation result (valid/invalid, company name, timestamp) on the transaction
- →Generate the invoice with the correct tax treatment note
Invoice Requirements
Every VAT invoice must include: your VAT number, the customer's VAT number (B2B), the VAT amount, the VAT rate applied, and for reverse charge transactions, the note 'Reverse charge' or the equivalent in the customer's language. Missing any of these makes the invoice non-compliant.
Go deeper
Start validating EU VAT numbers
Free plan — 100 validations/month. No credit card required.