Async VAT Validation: Webhooks and Batch Processing
To process a CSV locally, follow the Python CSV validation guide.
Sync VAT validation works for most use cases, but it breaks down in three scenarios: high-latency checkout flows, bulk revalidation of existing customers, and VIES downtime. Async validation solves all three. Submit the request, get an immediate 202, and receive the result via webhook.
When sync validation is not enough
Checkout latency
In our monitoring, VIES takes 1 to 3 seconds per lookup depending on the member state, with some countries consistently at the slow end. In a checkout flow, that delay is visible to the customer. Async validation lets you accept the order immediately and process the VAT check in the background.
Bulk revalidation
If you have 500+ B2B customers, revalidating their VAT numbers quarterly means 500+ sequential API calls with rate limiting delays. With async batch validation, you submit all numbers in a single request (up to 200 for Pro, 1,000 for Business) and receive one webhook when all results are ready.
VIES downtime resilience
When a country's VIES service is down, sync validation fails with a 503 error. Async validation moves that failure handling out of your request path: Avatcado retries VIES briefly during the lookup and falls back to a recent cached result when one exists. If the registry is down and there is no cached result, you receive a validation.failed webhook and the validation is refunded to your quota, so you can resubmit once the service recovers.
How async validation works
Single async validation
POST one number to /v1/validate/async, get a 202 with a request_id, and receive a validation.completed or validation.failed webhook when processing finishes.
Batch async validation
POST an array of numbers to /v1/validate/async/batch. Avatcado validates each format immediately. Invalid formats are rejected in the 202 response (never queued). Valid items are processed in the background and a single batch.completed webhook is delivered when all items finish.
Example: submitting a batch
const response = await fetch("https://api.avatcado.com/v1/validate/async/batch", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: "Bearer avat_live_your_api_key",
},
body: JSON.stringify({
vat_numbers: ["DE123456789", "NL987654321B01", "FR12345678901"],
cache: true,
}),
});
const { data } = await response.json();
// data.batch_id: "550e8400-..."
// data.accepted: 3
// data.rejected: []
// data.status: "pending"
// Results arrive via webhook when processing completesHandling webhook deliveries
Every webhook is signed with HMAC-SHA256 using your signing secret. Always verify the signature before processing the payload.
Webhook handler example
import { createHmac } from "crypto";
// Express / Node.js example
app.post("/webhooks/avatcado", (req, res) => {
const signature = req.headers["x-avatcado-signature"];
const timestamp = req.headers["x-avatcado-timestamp"];
const body = req.body; // raw string
// Verify signature
const expected = createHmac("sha256", process.env.AVATCADO_WEBHOOK_SECRET)
.update(`${timestamp}.${body}`)
.digest("hex");
if (signature !== `sha256=${expected}`) {
return res.status(401).send("Invalid signature");
}
const event = JSON.parse(body);
switch (event.event) {
case "validation.completed":
// event.data contains the validation result
updateCustomerVatStatus(event.data.vat_number, event.data.valid);
break;
case "batch.completed":
// event.data.results contains all items
for (const item of event.data.results) {
if ("data" in item) {
updateCustomerVatStatus(item.data.vat_number, item.data.valid);
}
}
break;
case "validation.failed":
// event.error has the failure reason (event.data contains the vat_number)
flagForManualReview(event.data.vat_number, event.error);
break;
}
res.status(200).send("OK");
});Use case: quarterly customer revalidation
Companies with hundreds of B2B customers should revalidate VAT numbers periodically. VAT registrations can be revoked, businesses can close, and numbers can become invalid at any time. Quarterly revalidation is a common practice for compliance.
With async batch validation, the process is simple:
- Query your database for all active customer VAT numbers
- Submit them in a single batch request (up to 200 or 1,000 depending on your plan)
- Receive a
batch.completedwebhook with all results - Update your records and flag any newly invalid numbers for review
// Quarterly revalidation script
const customers = await db.query("SELECT vat_number FROM customers WHERE active = true");
const vatNumbers = customers.map((c) => c.vat_number);
const response = await fetch("https://api.avatcado.com/v1/validate/async/batch", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${process.env.AVATCADO_API_KEY}`,
},
body: JSON.stringify({ vat_numbers: vatNumbers }),
});
const { data } = await response.json();
console.log(`Batch ${data.batch_id}: ${data.accepted} accepted, ${data.rejected.length} rejected`);
// Results will arrive via webhookGet started
Async validation and webhooks are available on Pro (starting at €29/month) and Business plans.
- Create a free account and upgrade to Pro
- Configure your webhook URL in the dashboard
- Read the webhook documentation for signature verification and event payload details
- Read the async validation documentation for endpoint details and error handling
Frequently asked questions
What happens if VIES is down when I submit an async request?
Async validation is designed to keep registry failures out of your request path. When the lookup runs, Avatcado retries VIES briefly and falls back to a recent cached result when one exists: for a number with a cached result, that result is served (marked stale if it is past the TTL) and delivered via webhook as usual, so your workflow does not even notice the outage. If the upstream registry is down and no cached result exists, the request fails fast rather than hanging: you receive a validation.failed webhook with the failure reason in event.error, and the validation is refunded to your quota, so the failed attempt costs nothing. Your webhook handler should route these failures to a retry queue or manual review and resubmit once the registry recovers. This is the main operational difference from sync validation, where the same situation surfaces as a 503 you have to catch inline while the customer waits.
Do async validations count against my monthly quota?
Yes. Each VAT number in an async request is counted once when the request is accepted, meaning at the 202 response, not when the webhook later delivers the result, so a 500-number batch consumes 500 validations from your quota at submission time. Two refinements make the accounting fair. Numbers that fail format validation are rejected in the 202 response itself and never queued, so obviously malformed input in a batch does not consume quota for a lookup that could never succeed. And when a validation fails because the upstream registry was down with no cached result available, you receive a validation.failed webhook and that validation is refunded to your quota, so you can resubmit after the outage without paying twice. Plan bulk revalidation with this in mind: a quarterly sweep of your active customer base costs its size in validations, on top of whatever your checkout traffic already consumes that month.
Can I mix sync and async validation?
Yes. The sync endpoints (GET /v1/validate and POST /v1/validate/batch) continue to work exactly as before; async adds POST /v1/validate/async and POST /v1/validate/async/batch alongside them, on the same API key, and both styles draw from the same monthly quota and benefit from the same cache. The sensible split follows the use case. Use sync where a human is waiting on the answer: checkout and signup flows, where the result decides tax treatment before the order completes. Use async where nobody is waiting and volume or resilience matters: quarterly revalidation of your customer base, backfilling validation for imported records, or any bulk job where retrying around VIES downtime should not be your code's problem. A common production pattern combines them: sync at checkout for the immediate decision, then a scheduled async batch that revalidates active subscribers and flags any number that has gone invalid before the next billing cycle runs.
What is the maximum batch size for async?
Pro allows up to 200 numbers per async batch and Business up to 1,000, compared with 50 per request on the synchronous batch endpoint, which makes async the right tool once a job outgrows what you want to hold open in a single HTTP request. Submit the array to POST /v1/validate/async/batch: format validation happens immediately, invalid formats are rejected in the 202 response and never queued, and accepted items process in the background with a single batch.completed webhook delivered when all items finish, carrying per-item results you can iterate over. Sizing a job is simple arithmetic: revalidating 3,000 customers on Business takes three batches of 1,000 submitted one after another, while on Pro it takes fifteen batches of 200. Every accepted item still counts against your monthly quota (10,000 on Pro, 50,000 on Business), so the practical ceiling for a bulk sweep is usually the quota rather than the per-batch limit.
Sources
- Avatcado async validation documentation Avatcado, accessed August 13, 2026
- Avatcado webhooks documentation Avatcado, accessed August 13, 2026
- VIES on-the-Web European Commission, accessed August 13, 2026
Related guides
VIES Downtime: Why It Happens and How to Handle It
Why VIES goes down, which EU countries are least reliable, and how to keep your VAT validation working during outages with caching and stale fallback.
Validate VAT Numbers in Your CRM
How to add real-time VAT number validation to CRM account creation and lead qualification workflows. Covers Salesforce, HubSpot, and no-code automation tools.
VIES API Guide: REST Endpoint, SOAP & Integration
Use the official VIES REST API from Node.js, handle HTTP 200 faults, preserve consultation evidence, compare SOAP, and plan for member-state downtime.