Async VAT Validation: Webhooks, Batch Processing, and Background Validation
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
VIES can take 1 to 3 seconds per lookup depending on the member state. In a checkout flow, that delay is visible to the customer. Some countries (Italy, Spain, Poland) are consistently slow. 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 queues the request and retries automatically with exponential backoff over several hours. When the service recovers, the result is delivered via webhook. Your application never has to handle retry logic.
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. Vatly 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.vatly.dev/v1/validate/async/batch", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: "Bearer vtly_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/vatly", (req, res) => {
const signature = req.headers["x-vatly-signature"];
const timestamp = req.headers["x-vatly-timestamp"];
const body = req.body; // raw string
// Verify signature
const expected = createHmac("sha256", process.env.VATLY_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.data.error has the failure reason
flagForManualReview(event.data.vat_number, event.data.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.vatly.dev/v1/validate/async/batch", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${process.env.VATLY_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 24.17/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?
Vatly queues the request and retries automatically with exponential backoff over several hours. When the service comes back, the result is delivered via webhook. If it stays down for 24+ hours, you receive a validation.failed webhook.
Do async validations count against my monthly quota?
Yes. Each VAT number is counted once when the request is accepted (the 202 response), not when the result is delivered.
Can I mix sync and async validation?
Yes. The sync endpoints (GET /v1/validate, POST /v1/validate/batch) continue to work as before. Use sync for real-time validation and async for bulk processing or when you want resilience against downtime.
What is the maximum batch size for async?
Pro: 200 items per batch. Business: 1,000 items per batch.