Compliance9 min readAlberto García

B2B Invoice VAT Automation: Number Validation for ERP Systems

For ERP and accounts payable teams, VAT validation is both a buyer obligation and a seller verification step. This guide covers the validation architecture for invoice processing at scale.

b2berpinvoicingvatautomation

B2B invoice workflows require VAT validation in two directions: validating your customers' VAT numbers before issuing zero-rated sales invoices, and validating your vendors' VAT numbers before accepting their input VAT claims on purchase invoices. Both use the same TaxID API endpoint, but the compliance risk and the business logic on failure differ. This guide covers both, plus the bulk validation patterns needed for ERP integration.

Outbound Invoices: Validating Your Customers

For sales invoices applying zero-rate (reverse charge), validate the customer's VAT number before creating the invoice. This validation result is the legal basis for the zero-rate treatment — store the `request_id` from the API response alongside the invoice record.

typescriptlib/invoice-vat.ts
async function validateForSalesInvoice(
  vatNumber: string,
  invoiceId: string
): Promise<{ applyZeroRate: boolean; auditRef: string }> {
  const vat = vatNumber.replace(/\s/g, '').toUpperCase();
  const country = vat.slice(0, 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();

  // Store audit record
  await db.vatValidation.create({
    data: {
      invoiceId,
      vatNumber: vat,
      status: data.status,
      companyName: data.company_name,
      requestId: data.request_id,
      validatedAt: new Date(),
      direction: 'outbound', // sales invoice
    },
  });

  return {
    applyZeroRate: data.status === 'active',
    auditRef: data.request_id,
  };
}

Inbound Invoices: Validating Your Vendors

When a vendor sends you a VAT invoice, validating their VAT number serves two purposes: confirming they are entitled to charge VAT (so your input VAT deduction is valid) and detecting fraudulent invoices. If a vendor's VAT number is inactive or format-invalid, do not pay the VAT portion until they resolve the registration issue.

typescriptlib/vendor-vat.ts
async function validateVendorInvoice(vendorVat: string, invoiceAmount: number) {
  const vat = vendorVat.replace(/\s/g, '').toUpperCase();
  const country = vat.slice(0, 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();

  if (data.status === 'inactive') {
    // Flag invoice for AP review — vendor not entitled to charge VAT
    return {
      action: 'flag_for_review',
      reason: 'Vendor VAT registration has lapsed. Input VAT deduction may be disallowed.',
    };
  }

  if (data.status === 'format_invalid') {
    return {
      action: 'reject',
      reason: 'Invalid VAT number format on invoice. Contact vendor to correct.',
    };
  }

  return {
    action: 'approve',
    vendorName: data.company_name,
    vatInputDeductible: true,
  };
}

Bulk Validation for ERP Imports

ERP systems often import vendor and customer lists from CSV. For bulk validation at import time, use the TaxID batch endpoint which validates up to 500 numbers per request. This avoids rate limit issues from calling individual validation endpoints for each record.

typescriptlib/bulk-validation.ts
async function bulkValidateVatNumbers(vatNumbers: string[]) {
  const results = [];

  // Process in batches of 50 to respect rate limits
  for (let i = 0; i < vatNumbers.length; i += 50) {
    const batch = vatNumbers.slice(i, i + 50);

    const batchResults = await Promise.all(
      batch.map(async (vat) => {
        const normalised = vat.replace(/\s/g, '').toUpperCase();
        const country = normalised.slice(0, 2);
        const res = await fetch(
          `https://taxid.dev/api/v1/validate/${country}/${normalised}`,
          { headers: { Authorization: `Bearer ${process.env.TAXID_API_KEY}` } }
        );
        const data = await res.json();
        return { vatNumber: normalised, ...data };
      })
    );

    results.push(...batchResults);

    // Rate limit: pause between batches
    if (i + 50 < vatNumbers.length) {
      await new Promise(r => setTimeout(r, 1000));
    }
  }

  return results;
}

Start validating EU VAT numbers

Free plan — 100 validations/month. No credit card required.

AG
Alberto García

Founder, TaxID

Building EU VAT validation tools for developers. Obsessed with compliance automation and developer experience.