Skip to content
Viesproof

API reference

One call at checkout. One to prove it later.

Every request is authenticated with a bearer API key. Branch on decision, not on status— the decision already applies your organisation’s policy for the unavailable case.

Authentication

Create keys in the dashboard. A key is shown once and stored only as a SHA-256 digest, so a lost key is replaced rather than recovered.

Authorization: Bearer vp_live_XXXXXXXXXXXXXXXXXXXXXXXX

Endpoints

  • POST/api/v1/verify

    Check a VAT number. The checkout call.

    Synchronous. Returns a decision, not just a verdict. Counts against the quota only when a live consultation is made.

  • GET/api/v1/checks

    The audit archive, newest first.

    Cursor paginated with ?limit= (max 100) and ?cursor=. Filter with ?status= and ?vatNumber=.

  • GET/api/v1/checks/{id}

    One check, with its receipt re-verified on read.

    Verification is recomputed from the stored facts, never read from a column — a stored 'valid' flag would defeat the point.

  • GET/api/v1/audit

    Verify the whole receipt chain.

    Walks the chain oldest first and reports the index it broke at, if any. Free; it does not count against the quota.

  • GET/api/v1/status

    Per-member-state VIES availability.

    Useful for explaining an UNAVAILABLE answer, and for a status page.

curl -X POST https://viesproof.altixcode.com/api/v1/verify \
  -H "Authorization: Bearer vp_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "vatNumber": "DE143454214",
    "clientReference": "order_9001",
    "trader": { "name": "Example GmbH", "city": "Berlin" }
  }'

Decisions

Three answers, three decisions. The mapping for UNAVAILABLE depends on your organisation’s policy, which is why the decision is computed here rather than left to each caller to re-derive.

decisionstatusMeaning
ACCEPT_ZERO_RATEDVALIDVIES confirmed the registration and issued a consultation number. The reverse charge may be applied.
CHARGE_VATINVALIDThe member state replied: not registered. Charge domestic VAT. Also returned for UNAVAILABLE when your policy is fail-closed.
REVIEWUNAVAILABLENobody answered. Your policy is to flag rather than block, so the sale may proceed but must not be recorded as verified.

When VIES cannot answer

National tax systems go down routinely. Every reason below produces UNAVAILABLE — never INVALID, because treating an outage as a negative verdict blocks legitimate customers for reasons that have nothing to do with them.

  • MS_UNAVAILABLE

    That member state's own system is down.

  • MS_MAX_CONCURRENT_REQ

    That member state is rate-limiting.

  • SERVICE_UNAVAILABLE

    VIES itself is down.

  • GLOBAL_MAX_CONCURRENT_REQ

    VIES is rate-limiting globally.

  • TIMEOUT

    VIES did not respond inside the timeout.

  • IP_BLOCKED / VAT_BLOCKED

    VIES has blocked the request.

An unknown fault is also UNAVAILABLE. We do not guess: a fault code we have not classified could mean anything, and the one interpretation that is certainly wrong is “this number is invalid”.

Receipts and verification

Every consultation is appended to a per-organisation hash chain. Each receipt commits to the previous receipt’s digest, so an entry cannot be edited, removed or reordered without breaking verification from that point on. The digest is HMAC-signed with a key held only by the server.

GET /api/v1/audit

{
  "verified": false,
  "checked": 412,
  "brokenAt": 412,
  "reason": "digest_mismatch",
  "chainLength": 1204
}

brokenAt is the index of the first entry that failed. An auditor needs to know which record was altered, not merely that something was.

Supported member states

Syntax is checked locally before any call to VIES, so a malformed number costs nothing and produces a message that says what the right shape is. Great Britain is absent: it left the EU VAT area. Northern Ireland remains, as XI.

  • AT
  • BE
  • BG
  • CY
  • CZ
  • DE
  • DK
  • EE
  • EL
  • ES
  • FI
  • FR
  • HR
  • HU
  • IE
  • IT
  • LT
  • LU
  • LV
  • MT
  • NL
  • PL
  • PT
  • RO
  • SE
  • SI
  • SK
  • XI

Errors

StatusCodeMeaning
400vat_bad_syntaxThe number does not match its country's format. The message says what the format is.
400vat_unknown_countryNot an EU VAT country code. GB is no longer one; XI is.
400vat_emptyNo VAT number was supplied.
401missing_api_key / invalid_api_keyNo bearer token, or one we do not recognise.
402quota_exceededThe monthly check limit is reached. Never returns VALID instead.
404not_foundNo check with that id in your organisation.