How to Handle VAT Validation in Your SaaS Billing Flow

If you sell B2B SaaS to European businesses, VAT validation is not optional. You need to determine whether to charge VAT or apply the reverse charge on every transaction. Getting this wrong means either overcharging (annoying customers) or undercharging (creating a tax liability for your company).

Why real-time validation matters

VAT validation must happen in real-time during checkout or signup, not asynchronously or after the fact. You can't block checkout on an async webhook. You can't retroactively fix an invoice that charged VAT when it shouldn't have, or one that skipped VAT when it should have been applied.

The validation result determines the tax treatment of the transaction at the moment it happens. A valid cross-border VAT number means reverse charge (zero-rate). An invalid number, or no number at all, means you charge VAT. That decision has to be made before you generate the invoice, not after.

The validation flow

Here is the full pattern, step by step. This applies regardless of your stack, billing provider, or framework.

  • Collect: Ask for the VAT number in your signup or checkout UI. Make it optional. Not all buyers are VAT-registered, and sole proprietors or consumers won't have one.
  • Validate: Call the API to verify the number is registered and active. Show the result to the user in real-time so they can correct typos or provide a different number.
  • Decide: If the number is valid and the buyer is in a different country than you (cross-border B2B), apply the reverse charge and zero-rate VAT. If the number is invalid, missing, or the buyer is in your country, charge VAT at the applicable rate.
  • Store: Save the validation result (valid/invalid, company name, consultation number if applicable) as proof. You need this for tax audits.
  • Invoice: Generate a compliant invoice reflecting the tax treatment you applied. If you zero-rated, the invoice must reference the reverse charge mechanism.

Code example

Using the @vatly/node SDK, here is a TypeScript function that determines tax treatment during checkout:

import Vatly from "@vatly/node";

const vatly = new Vatly("vtly_live_your_api_key");

async function handleCheckout(buyerVatNumber: string | null, sellerCountry: string) {
  if (!buyerVatNumber) {
    // No VAT number provided, charge VAT
    return { chargeVat: true, vatRate: getVatRate(sellerCountry) };
  }

  const { data, error } = await vatly.vat.validate({ vatNumber: buyerVatNumber });

  if (error) {
    // Validation failed (upstream down, invalid format, etc.)
    // Conservative approach: charge VAT and let customer dispute
    console.error(`VAT validation failed: ${error.code}`);
    return { chargeVat: true, vatRate: getVatRate(sellerCountry) };
  }

  if (data.data.valid && data.data.countryCode !== sellerCountry) {
    // Valid VAT, cross-border B2B: apply reverse charge
    return {
      chargeVat: false,
      reverseCharge: true,
      validation: {
        vat_number: data.data.vatNumber,
        company_name: data.data.company?.name,
        validated_at: data.data.requestedAt,
      },
    };
  }

  // Same country or invalid: charge VAT
  return { chargeVat: true, vatRate: getVatRate(data.data.countryCode || sellerCountry) };
}

Or with a simple curl request:

curl https://api.vatly.dev/v1/validate?vat_number=DE123456789 \
  -H "Authorization: Bearer vtly_live_your_api_key"

See the Vatly documentation for the full response schema, error codes, and SDK reference.

Plugging into your billing tool

Stripe

When a buyer provides a valid VAT number, set tax_exempt: "reverse" on the Stripe Customer object and store the VAT number in customer metadata. Stripe will then generate zero-rated invoices for that customer. Store the Vatly validation result (company name, consultation number) in invoice metadata for your audit trail.

If the validation fails or the buyer is in the same country as you, leave tax_exempt as "none" and configure Stripe Tax or manual tax rates for the applicable VAT rate. The key point: Vatly handles the validation, Stripe handles the invoicing, your code connects the two.

Paddle and Lemon Squeezy

These are merchant of record platforms that handle tax calculation and remittance for you. They compute the correct VAT automatically. However, you may still want to validate VAT numbers to determine if the buyer qualifies for reverse charge treatment before the transaction reaches the platform. Validating upfront means the platform applies the correct tax treatment from the start, avoiding corrections later.

Custom billing

If you built your own billing system, you own the entire flow. Vatly is the validation layer: call the API before generating each invoice, store the result, and use it to determine the tax line items. See the reverse charge guide for the regulatory details on when zero-rating applies.

Get started

Vatly's free tier includes 500 validations per month, enough to build and test a complete billing integration. The API and response format are the same across all plans.

Start validating for free →

Frequently asked questions

Do I need to validate VAT numbers if I use Stripe Tax?

Stripe Tax calculates and collects the correct tax, but it does not verify that a VAT number is registered and active. You still need to validate the number to determine whether the reverse charge applies. Vatly handles the validation, Stripe handles the tax calculation.

What happens if VIES is down during checkout?

If you call VIES directly, your checkout flow breaks. Vatly's 25-day cache and stale fallback ensure that validation requests succeed even when upstream services are down. The response includes meta fields indicating whether the result came from cache.

Should I validate VAT numbers on the frontend or backend?

Always validate on the backend. Frontend validation can be bypassed and should only be used for format checking (showing the user immediate feedback). The actual VIES/HMRC lookup must happen server-side before you finalize the tax treatment.

How do I handle a customer who provides an invalid VAT number?

Charge VAT at the applicable rate. You cannot apply the reverse charge without a verified, active VAT number. Show the customer a clear message explaining why their number was rejected and let them retry or proceed without the VAT exemption.