vat.toolsDocumentation
Open workspace

Quickstart

A sandbox key, one request, and the evidence it returns.

This walkthrough takes a developer key from the workspace to a first attributed result. It uses the sandbox environment, so nothing you do here spends live authority capacity.

Create a developer key

Keys are organization-owned and bound to one environment. Create one in the workspace under Integrations → Developer keys, choosing sandbox for this walkthrough and the scopes your integration needs.

  1. Choose the environment

    Sandbox for development and review artifacts; live for real authority calls. A key works in exactly one of them.

  2. Grant the narrowest scopes

    vat:validate for VAT checks, company:search for search and resolution, company:verify for registry verification, vat:discover for reverse VAT discovery, and history:read for the read-only history.

  3. Store it server-side

    Send it as Authorization: Bearer vat_… from your backend or the platform's secret store. Never embed a key in a URL, a mobile app, or a client-side bundle.

Make the first call

Validate one VAT identifier. The body carries the jurisdiction, the identifier, and the environment; the header carries the key.

curl --request POST \
  'https://vat.tools/api/v2/validations' \
  --header 'Authorization: Bearer vat_sandbox_REPLACE_WITH_YOUR_KEY' \
  --header 'Content-Type: application/json' \
  --header 'Idempotency-Key: 8f0c1a52-4b6e-4f17-9d2a-3c5e7b9a1d40' \
  --data '{"countryCode":"DE","vatNumber":"123456789","mode":"sandbox"}'

Read the result

The response is one envelope. Branch on result.outcome, but keep the rest:

  • result.outcome — valid, invalid, source_unavailable, or unsupported. Only the first two come from the authority's own answer.
  • evidence[].source and evidence[].observedAt — who answered and when it was observed. Store both beside the outcome.
  • freshness — how long the answer stays current. Serving the same result again does not move its observation time.
  • usage — the meter and units this call settled. Replays settle at zero.

Read the evidence model →

Branch honestly

Most integration bugs are not transport bugs; they are outcome bugs. A checkout or an approval rule must never treat an unanswered source as a negative answer.

429 · application/json
{
  "error": {
    "code": "RATE_LIMITED",
    "message": "Too many requests. Retry after the interval in Retry-After."
  },
  "retryable": true,
  "retryAfterSeconds": 30
}
  • Continue on valid; flag or block only on an explicit invalid, under a policy the customer configured.
  • Route source_unavailable and unsupported to review, with their labels intact.
  • On 429 RATE_LIMITED, wait for Retry-After and retry with the same idempotency key. On QUOTA_EXCEEDED, stop and surface the allowance.