In a manual B2B billing workflow, every new enterprise customer requires a finance team member to open the VIES portal, type in the VAT number, screenshot the result, and file it somewhere. For 10 customers a month, that is manageable. For 500 new B2B signups a month across 15 EU countries, plus UK, Australia, and Norway, it becomes a significant operational cost with a high error rate. Automation does not just save time — it creates a consistent, auditable record that a manual process cannot reliably produce.
Where Automation Fits in the Invoice Lifecycle
B2B VAT validation automation has three distinct insertion points in the invoice lifecycle. First, at customer onboarding: validate the VAT number when the customer first provides it, before their account is activated for B2B pricing. Second, at invoice creation: re-validate before issuing any zero-rated invoice, particularly for invoice amounts above a threshold that warrants the extra verification step. Third, in the monthly subscription cycle: run a batch re-validation job for all reverse-charge customers before the billing date. Each insertion point catches a different failure mode.
| Automation point | What it catches | API call type |
|---|---|---|
| Customer onboarding | Invalid or mistyped VAT numbers at account creation | Single validate, synchronous |
| Pre-invoice validation | Numbers that have become inactive since onboarding | Single validate, synchronous or async |
| Monthly batch re-validation | Registrations revoked between billing cycles | Batch validate, background job |
| ERP/supplier import | Invalid supplier VAT numbers in bulk data imports | Batch validate, triggered on import |
Automating Onboarding Validation
At onboarding, the customer submits a VAT number during account setup. Your server calls the TaxID API immediately. If status: 'active' is returned, activate the B2B account with reverse-charge treatment. If status: 'invalid', show an error asking for a correction. If status: 'service_unavailable', create the account with standard VAT treatment and queue the customer for re-validation within 24 hours. This prevents EU maintenance windows from blocking legitimate business customers from onboarding.
type OnboardingResult =
| { status: 'approved'; vatData: VatResponse }
| { status: 'rejected'; reason: string }
| { status: 'pending_verification'; message: string };
export async function onboardB2BCustomer(
vatNumber: string,
declaredCountry: string
): Promise<OnboardingResult> {
const country = vatNumber.slice(0, 2).toUpperCase();
// Sanity-check: declared country matches VAT prefix
if (country !== declaredCountry.toUpperCase() && declaredCountry !== 'XI') {
return { status: 'rejected', reason: 'VAT number country does not match billing country' };
}
const res = await fetch(
`https://taxid.dev/api/v1/validate/${country}/${vatNumber}`,
{ headers: { Authorization: `Bearer ${process.env.TAXID_API_KEY}` } }
);
const data = await res.json();
if (data.status === 'active') {
return { status: 'approved', vatData: data };
}
if (data.status === 'service_unavailable') {
return {
status: 'pending_verification',
message: 'VAT registry is temporarily unavailable. Your account will be upgraded to B2B pricing once verified (usually within 24 hours).'
};
}
return { status: 'rejected', reason: `VAT number status: ${data.status}` };
}Building the Monthly Re-Validation Job
The monthly batch re-validation job is the most important automation for subscription businesses. It catches two scenarios that onboarding validation misses entirely: businesses that deregister voluntarily after signing up, and businesses whose VAT registration is revoked by the tax authority. Both events happen regularly in the EU — restructurings, company dissolutions, and voluntary deregistrations each produce a formerly-valid number that returns inactive from the next VIES query.
// Run 3 days before monthly billing to leave time for corrections
export async function monthlyVatRevalidation() {
// Get all customers currently on reverse-charge treatment
const customers = await db.query<{ customer_id: string; vat_number: string; stripe_id: string }>(
`SELECT customer_id, vat_number, stripe_id
FROM customers
WHERE tax_treatment = 'reverse_charge' AND vat_number IS NOT NULL`
);
const batchSize = 50;
const issues: Array<{ customerId: string; newStatus: string }> = [];
for (let i = 0; i < customers.length; i += batchSize) {
const batch = customers.slice(i, i + batchSize);
for (const customer of batch) {
const country = customer.vat_number.slice(0, 2);
const vat = customer.vat_number.slice(2);
const res = await fetch(
`https://taxid.dev/api/v1/validate/${country}/${vat}`,
{ headers: { Authorization: `Bearer ${process.env.TAXID_API_KEY}` } }
);
const data = await res.json();
// Log every result regardless
await db.insert('vat_validation_log', {
customer_id: customer.customer_id,
vat_number: customer.vat_number,
status: data.status,
request_id: data.request_id,
checked_at: new Date().toISOString(),
});
// Flag problems for finance review
if (data.status === 'inactive' || data.status === 'invalid') {
issues.push({ customerId: customer.customer_id, newStatus: data.status });
}
}
// Throttle to avoid rate limits
if (i + batchSize < customers.length) {
await new Promise(r => setTimeout(r, 1000));
}
}
// Send summary to finance team
if (issues.length > 0) {
await notifyFinanceTeam(issues);
}
return { total: customers.length, issues: issues.length };
}Automating ERP and Supplier Import Validation
Finance and procurement teams often manage lists of 500–5,000 supplier VAT numbers in spreadsheets or ERP exports. Importing these without validation creates a silent compliance risk: the list may contain invalid, inactive, or incorrectly transcribed numbers that have never been checked against VIES. Integrate a validation step into the import flow that runs the full list through the batch endpoint and flags any numbers returning non-active statuses before they enter your accounting system.
The TaxID bulk validation feature covers the CSV import pattern with a web UI for non-developer finance team members, plus the API pattern for developers building it into automated ERP sync pipelines. The ERP supplier validation use case covers SAP, Oracle, and custom ERP integration patterns specifically.
Audit Trail Requirements
Automation is only as good as its audit trail. EU tax authorities ask to see, for each zero-rated invoice: the VAT number validated, the name and address returned by VIES at the time of validation, and the date of validation. Store this at the invoice level, not just the customer level. A customer object with a single 'last validated' timestamp does not prove the number was valid on the date of the 8th monthly invoice if the customer deregistered between invoice 6 and invoice 8. The audit record must be immutable and time-stamped at the point of issuance.
Note
The TaxID API response includes a `request_id` that uniquely identifies the VIES query. Store this with every invoice. If a tax auditor challenges a specific invoice, the request_id lets you retrieve the exact validation response that was on file at the time.
Handling Multiple Jurisdictions in a Single Workflow
A SaaS company with customers in EU countries, the UK, Australia, and Norway cannot build a single national-registry integration and call it done. Each jurisdiction has its own registry, its own format rules, and its own reliability characteristics. HMRC (UK) is a REST API with better uptime than VIES. The ABR (Australia) is also REST-based but has different field names. VIES is SOAP and the least reliable of the three. Building direct integrations with each registry means maintaining four separate clients, each with their own error taxonomy and downtime patterns.
The practical solution for multi-jurisdiction automation is a unified API that normalises all registries into a consistent interface — the same endpoint, the same response shape, the same status codes regardless of country. This is the architecture the TaxID API provides: your validation logic does not need to branch on country, handle SOAP for some cases and REST for others, or maintain separate error handling per jurisdiction. A single call to GET /api/v1/validate/:country/:vat works identically for DE, GB, AU, NO, and CH.
For automation workflows that process invoices from a global customer base, this uniformity matters operationally. Your monthly re-validation job, your onboarding gateway, and your pre-invoice check all use the same code path regardless of which country the customer is in. When a new jurisdiction needs to be supported — Norway was added to the TaxID API without requiring any customer code changes — existing automations inherit the new coverage automatically.