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.
- Choose the environment
Sandbox for development and review artifacts; live for real authority calls. A key works in exactly one of them.
- Grant the narrowest scopes
vat:validatefor VAT checks,company:searchfor search and resolution,company:verifyfor registry verification,vat:discoverfor reverse VAT discovery, andhistory:readfor the read-only history. - 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, orunsupported. Only the first two come from the authority's own answer.evidence[].sourceandevidence[].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.
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.
{
"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 explicitinvalid, under a policy the customer configured. - Route
source_unavailableandunsupportedto review, with their labels intact. - On
429 RATE_LIMITED, wait forRetry-Afterand retry with the same idempotency key. OnQUOTA_EXCEEDED, stop and surface the allowance.