The Odoo VIES VAT check: what it does and what it does not
Odoo checks the format of every VAT number, and with « Verify VAT Numbers » turned on (Accounting, Configuration, Settings, Taxes) it also asks VIES for partners in another country than your company, then ticks « Intra-Community Valid » from the answer. Fiscal positions that require a VAT number then need that box. It is the right default; what it does not keep is a dated record of the check with a consultation number, and when VIES fails it asks you to check by hand.
- Format checks always run; the VIES check runs only with « Verify VAT Numbers » on, for contacts in another country than the company’s.
- The result sets « Intra-Community Valid »; a manual override is logged in the chatter.
- In the 19.0 code the check goes through Odoo’s own VIES proxy and is redone when the VAT number changes; a pending answer is retried later by a scheduled job, a failed one posts « The VIES check failed. Please check the Tax ID manually. »
- No consultation number is stored on the partner.
When to add a check of your own
Keep Odoo’s setting on. Add a call of your own when you need what it does not keep: the dated answer and the consultation number for an audit file, a re-check of every EU customer before a quarterly return, or an answer during a member state’s outage (the API serves the last dated answer, marked stale). A scheduled action in your own module can call the API for partners whose check failed and post the result in the chatter.
import os
import requests
API = "https://veriport.io/api/v1"
def veriport_check(vat_number, requester=None):
params = {"number": vat_number}
if requester:
params["requester"] = requester # your company's VAT number: consultation number on paid tiers
r = requests.get(API + "/vat/validate", params=params, timeout=15,
headers={"Authorization": "Bearer " + os.environ["VERIPORT_KEY"]})
body = r.json()
if r.status_code == 503: # vies_unavailable and no cached answer: try again later
return None
r.raise_for_status()
return body # body["valid"], body["source"]["kind"], body.get("consultationNumber")
# e.g. from a scheduled action over partners whose VIES check failed:
# result = veriport_check(partner.vat, partner.company_id.vat)
# partner.message_post(body=f"VAT checked {result['requestDate']}: valid={result['valid']} ({result['source']['kind']})")What to keep in the chatter or a field
| Field | Why |
|---|---|
valid | The VIES answer for the number on the day asked. |
requestDate, source.checkedAt | When VIES answered (live) or when the cached answer was obtained. |
source.kind, source.stale | live or cache; stale: true when the member state was down and a dated earlier answer was served. |
consultationNumber | The Commission’s reference for this check, when you sent requester (your own VAT number) on a paid tier. |
name, address | What the member state publishes; Germany and a few others publish neither. |
Questions
- Does Odoo call VIES directly?
- In the 19.0 source, the check goes through Odoo’s IAP proxy (vies.api.odoo.com), which asks VIES. That is code, not documentation, read on 12 November 2026; it may change between versions.
- Can I re-check all partners at once?
- From your module, loop over the partners and call the API one by one, or use the bulk endpoint (100 numbers a request) on the monthly plan; the CSV guide shows the bulk call.
100 calls a month, no card; consultation numbers and kept records from $9.
Get a free keySources
- Odoo 19 Accounting: VAT numbers verification (Verify VAT Numbers, the Intra-Community Valid box) — Odoo S.A., consulted 2026-11-12
- Odoo 19 source, addons/base_vat/models/res_partner.py (vies_valid, the VIES check through Odoo’s proxy, pending and fault states) — Odoo S.A., consulted 2026-11-12. Behaviour read from the code, not the documentation.
- VIES on-the-Web endpoint: OpenAPI contract (POST /check-vat-number, GET /check-status; requester fields for the consultation number) — European Commission, DG TAXUD, consulted 2026-11-12