> ## Documentation Index
> Fetch the complete documentation index at: https://docs.avatcado.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Async Validation

> Submit validation requests and receive results via webhook

# Async Validation

The async endpoints accept one or more VAT/GST numbers and return 202 immediately. Results are delivered to your [webhook URL](/webhooks) when validation completes.

Available on Pro and Business plans. Requires a webhook URL to be configured.

Async endpoints require a live API key (`avat_live_` prefix). Test keys are rejected with `403 forbidden`.

## Single async validation

**Endpoint:** `POST /v1/validate/async`

Submit one VAT number for background validation.

<CodeGroup>
  ```bash curl theme={null}
  curl -X POST https://api.avatcado.com/v1/validate/async \
    -H "Authorization: Bearer avat_live_your_api_key" \
    -H "Content-Type: application/json" \
    -d '{
      "vat_number": "DE123456789",
      "requester_vat_number": "NL987654321B01"
    }'
  ```

  ```typescript @avatcado/node theme={null}
  import Avatcado from "@avatcado/node";

  const avatcado = new Avatcado("avat_live_your_api_key");
  const { data, error } = await avatcado.vat.validateAsync({
    vatNumber: "DE123456789",
    requesterVatNumber: "NL987654321B01",
  });

  if (error) {
    console.error(`${error.code}: ${error.message}`);
  } else {
    console.log(data.data.requestId); // UUID to correlate with webhook delivery
    console.log(data.data.status);    // "pending"
  }
  ```

  ```python Python theme={null}
  from avatcado import Avatcado

  avatcado = Avatcado("avat_live_your_api_key")
  result = avatcado.vat.validate_async(
      "DE123456789",
      requester_vat_number="NL987654321B01",
  )
  print(result.data.request_id)  # UUID to correlate with webhook delivery
  print(result.data.status)      # "pending"
  ```
</CodeGroup>

### Request

```json theme={null}
{
  "vat_number": "DE123456789",
  "requester_vat_number": "NL987654321B01",
  "cache": true
}
```

### Response (202 Accepted)

```json theme={null}
{
  "data": {
    "request_id": "550e8400-e29b-41d4-a716-446655440000",
    "status": "pending",
    "vat_number": "DE123456789"
  },
  "meta": {
    "request_id": "660e8400-e29b-41d4-a716-446655440000"
  }
}
```

Format validation happens immediately. If the VAT number format is invalid, you get a 422 response, not a 202. Invalid numbers are never queued.

**When to use:** checkout flows where you want to avoid 1-3 seconds of VIES latency, or when you want resilience against VIES downtime.

## Batch async validation

**Endpoint:** `POST /v1/validate/async/batch`

Submit multiple VAT numbers for background validation. Pro: up to 200 items per batch. Business: up to 1,000 items per batch.

<CodeGroup>
  ```bash curl theme={null}
  curl -X POST https://api.avatcado.com/v1/validate/async/batch \
    -H "Authorization: Bearer avat_live_your_api_key" \
    -H "Content-Type: application/json" \
    -d '{
      "vat_numbers": ["DE123456789", "NL987654321B01", "XX000"],
      "requester_vat_number": "NL987654321B01"
    }'
  ```

  ```typescript @avatcado/node theme={null}
  import Avatcado from "@avatcado/node";

  const avatcado = new Avatcado("avat_live_your_api_key");
  const { data, error } = await avatcado.vat.validateBatchAsync({
    vatNumbers: ["DE123456789", "NL987654321B01", "XX000"],
    requesterVatNumber: "NL987654321B01",
  });

  if (error) {
    console.error(`${error.code}: ${error.message}`);
  } else {
    console.log(`Accepted: ${data.data.accepted}/${data.data.total}`);
    for (const item of data.data.rejected) {
      console.error(`${item.vatNumber}: ${item.error.message}`);
    }
  }
  ```

  ```python Python theme={null}
  from avatcado import Avatcado

  avatcado = Avatcado("avat_live_your_api_key")
  result = avatcado.vat.validate_batch_async(
      ["DE123456789", "NL987654321B01", "XX000"],
      requester_vat_number="NL987654321B01",
  )
  print(f"Accepted: {result.data.accepted}/{result.data.total}")
  for item in result.data.rejected:
      print(f"{item.vat_number}: {item.error.message}")
  ```
</CodeGroup>

### Request

```json theme={null}
{
  "vat_numbers": ["DE123456789", "NL987654321B01", "XX000"],
  "requester_vat_number": "NL987654321B01",
  "cache": true
}
```

### Response (202 Accepted)

```json theme={null}
{
  "data": {
    "batch_id": "550e8400-e29b-41d4-a716-446655440000",
    "status": "pending",
    "total": 3,
    "accepted": 2,
    "rejected": [
      {
        "vat_number": "XX000",
        "error": {
          "code": "invalid_vat_format",
          "message": "Unsupported country code: XX"
        }
      }
    ]
  },
  "meta": {
    "request_id": "660e8400-e29b-41d4-a716-446655440000"
  }
}
```

Items with invalid formats are rejected immediately in the 202 response and never queued. Only accepted items count against your monthly quota.

When all accepted items finish processing, a single `batch.completed` [webhook event](/webhooks#batchcompleted) is delivered with all results.

**When to use:** periodic revalidation of your customer database, large data migrations, bulk compliance checks.

## How it works

1. You submit a validation request. Avatcado validates the format immediately.
2. Valid items are accepted (202) and queued for background processing.
3. Usage is counted at acceptance time, not when the result is delivered.
4. Avatcado validates each item against the upstream service (VIES, HMRC, BFS, BRREG, or ABR).
5. Each validated item writes to the same validation records as the sync endpoint. Cache, status page data, and usage analytics stay consistent.
6. If an upstream service is down, Avatcado retries automatically with exponential backoff (up to 5 retries over several hours).
7. When processing completes, the result is delivered to your webhook URL.

## Error handling

**Format errors** are returned immediately with a 422 status. The request is never queued.

**Upstream errors** (VIES down, HMRC timeout, etc.) are retried automatically. After all retries are exhausted, a `validation.failed` webhook is sent with the error details.

**Batch errors** are per-item. If 3 out of 200 items fail upstream validation, the `batch.completed` webhook includes 197 successes and 3 failures. The batch as a whole always completes.

**Missing webhook configuration** returns 400 with error code `webhook_not_configured`. Configure a webhook URL before using async endpoints.

**Free tier** returns 403 with error code `tier_insufficient`. Async validation requires Pro or Business.

| Scenario                          | Response                                                            |
| --------------------------------- | ------------------------------------------------------------------- |
| Invalid VAT format                | 422 immediately (not queued)                                        |
| No webhook configured             | 400 `webhook_not_configured`                                        |
| Free tier                         | 403 `tier_insufficient`                                             |
| Batch too large                   | 422 `validation_error`                                              |
| Monthly quota exceeded            | 429 `rate_limit_exceeded`                                           |
| Upstream down (during processing) | Retried automatically, `validation.failed` webhook after exhaustion |
