POST /invoice/vat-treatment
Answers the question an e-invoicing validator cannot: who charges the VAT on this transaction, under which mechanism, where is it taxed, and what must the invoice say? Post one transaction - the two countries, goods or services, business or consumer customer, the kind of supply, the date, optionally the amount - and get a determination sourced article by article under Directive 2006/112/EC.
An invoice that omits its reverse-charge mention passes every EN 16931
rule and is still refused by the tax authority. That gap is what this endpoint
closes: /invoice/validate judges the
document, this judges the transaction.
The runtime is pure computation over a committed snapshot - no network call
and no secret at request time. See the live
/catalog for the authoritative endpoint listing and
price.
The customer’s VAT number is an input, taken as declared. This endpoint performs no VIES lookup - zero network by design - so
customer_vat_statussays what you know, and the response echoes it indata.customer_vat_checktogether with the request that would check it for real:GET /company/vat, a live VIES lookup. A valid customer VAT number is a substantive condition of the intra-Community exemption (Art. 138(1)(b)) and of the Art. 196 reverse charge: declare itunknownand the exemption falls away, as it should.
x402 golden rule: the agent pays for the answer to its question. A
well-formed transaction gets its determination -> 200 - and that includes a
motivated undetermined, which is a result, not a failure: an uncovered
regime answered with a plausible default would be worse than no answer at all.
Only requests the service cannot read - a missing or unknown field, a
malformed country code, a date before the rate snapshot - leave the 200 range
and are not billed.
Request
POST with a JSON body. Set Content-Type: application/json. Unknown fields
are refused (a typo is a 400 naming the field), never ignored.
POST /invoice/vat-treatment
Content-Type: application/json
{
"supplier_country": "FR",
"customer_country": "DE",
"transaction_type": "goods",
"customer_type": "business",
"customer_vat_status": "valid",
"supply_kind": "intra_eu_dispatch",
"date": "2026-07-01",
"amount": "12500.00",
"currency": "EUR"
}
| Field | Type | Required | Description |
|---|---|---|---|
supplier_country | string | yes | Where the supplier is established, ISO 3166-1 alpha-2 (EL accepted for GR) |
customer_country | string | yes | Where the customer is established, or where the goods arrive |
transaction_type | string | yes | goods or services |
customer_type | string | yes | business or consumer |
supply_kind | string | yes | One of eight closed values, below. Never inferred |
date | string | yes | Date VAT becomes chargeable, YYYY-MM-DD. The rate served is the one in force at that date |
customer_vat_status | string | no (default unknown) | valid, invalid or unknown - declared, never verified here |
property_country | string | with services_land_related | Where the immovable property is located |
amount | string | no | Taxable base, plain decimal string. Supplying it triggers data.computed |
currency | string | with amount | ISO 4217 (XXX and XTS refused - they have no billing use) |
Both countries are checked together, so two bad codes come back as two
issues in one 400. Territories outside the VAT territory of the Union that
ISO can express (IC, EA, AX, the French overseas departments) are third
territories; Monaco is FR.
The eight kinds of supply
supply_kind is a closed enumeration and is required: a value outside the
list is a 400 naming the admitted ones, never a fallback to general.
supply_kind | The operation declared |
|---|---|
general | Domestic supply, goods not dispatched (Art. 31) |
intra_eu_dispatch | Goods dispatched from one Member State to another (Art. 32, 138, 40) |
export_outside_eu | Goods leaving the Union (Art. 146) |
import | Goods entering the Union (Art. 60, 201) - not determined, see below |
services_b2b_general | Services to a taxable person (Art. 44, 196) |
services_b2c_general | Services to a non-taxable person (Art. 45, 59) |
services_land_related | Services connected with immovable property (Art. 47) - add property_country |
services_electronic_b2c | Electronically supplied services to a consumer (Art. 58) |
transaction_type and customer_type must be consistent with
supply_kind: services_b2b_general declared with a consumer customer is a
motivated refusal (a paid 200, undetermined), never a guess about what you
meant.
The five mechanisms
data.mechanism is the verdict, and data.liable says who owes the tax:
mechanism | liable | What it means for the invoice |
|---|---|---|
standard | supplier | The supplier charges VAT at the rate of the place of supply |
reverse_charge | customer | No VAT charged; the customer self-accounts. The mention is compulsory |
exempt_with_credit | supplier | Exempt with a right to deduct (export). The exemption mention is compulsory |
out_of_scope | not_taxable_in_eu | Outside the territorial scope of the harmonised system |
undetermined | undetermined | An honest refusal, with its rationale - a paid 200 |
Every verdict carries a rule_id of the form INV-VAT-*. These are
Invoket identifiers and are never mixed with the normative BR-* rule ids
of /invoice/validate.
200 response - UnifiedResponse
{
"data": { ... },
"provenance": {
"source": "directive-2006-112 + tedb",
"fetched_at": "2026-07-30T18:08:34Z",
"freshness": { "kind": "snapshot", "as_of": "2026-07-27T00:00:00Z" }
}
}
provenance.source:directive-2006-112 + tedb- the Directive for the rules, the Commission’s TEDB extract for the rates.freshness.kind:snapshot;as_ofdates the committed rate snapshot, not a fetch.
Fields of data
| Field | Type | Description |
|---|---|---|
rule_id | string | INV-VAT-* - the rule that decided, stable across calls |
rule_summary | string | That rule in one sentence, with its articles |
mechanism | string | One of the five above |
liable | string | supplier, customer, not_taxable_in_eu or undetermined |
place_of_supply | object | country (ISO code, null when it cannot be determined - never guessed) and basis |
rate | object | Standard rate of the place of supply at the requested date. Absent when there is no place of supply |
required_invoice_mentions | array | The wordings to print, each with its basis and, where one exists, its national_basis |
legal_basis | array | Every article the determination rests on, each with a ready-made /legal/eu-act query |
computed | object | Served only when amount was supplied and the mechanism is determined |
customer_vat_check | object | status (declared_valid, declared_invalid, not_checked) and how_to_verify |
rationale | string | Why this is not the nominal case. Present exactly when there is something to explain |
caveats | array | What the determination assumes but does not verify (proof of dispatch, product classification…) |
notes | array | Remarks on the request itself |
limits | array | Six constant sentences on what this endpoint does not cover. Served with every answer |
rate
| Field | Description |
|---|---|
country | The place of taxation whose rate is served |
kind | Always standard in v1 |
percentage | The standard rate in force at date, each rate carrying its own validity in the snapshot |
reduced_rate_candidates | Reduced rates published by that State - candidates only, no product classification here |
super_reduced_rate_candidates | Same, for super-reduced rates |
has_regional_rates | true when the State applies territory-specific rates (Corsica, overseas departments, Madeira, the Azores, the Greek islands) that are outside the snapshot |
computed - what an agent writes on the invoice
computed.rate_percentage is what the supplier charges - 0 under a
reverse charge, an exemption or out of scope - while rate.percentage is the
rate of the place of taxation, which the customer self-assesses. An agent
filling an invoice reads computed; an agent booking the acquisition reads
rate. Amounts are rounded to the cent with the rounding of the EN 16931
engine, restated in computed.rounding.
Example - intra-EU B2B dispatch, reverse charge
A French supplier ships EUR 12,500 of goods to a German business whose VAT number is declared valid. The supply is exempt, the customer self-accounts in Germany, and four mentions are served - the English wording of the Directive and the French domestic wording, for both the reverse charge and the exemption (excerpt: two of the four):
{
"data": {
"rule_id": "INV-VAT-D01",
"rule_summary": "Intra-EU B2B dispatch with a proven VAT number: exempt supply, customer self-accounts in the Member State of arrival (Art. 138, 40, 200)",
"liable": "customer",
"mechanism": "reverse_charge",
"place_of_supply": {
"country": "DE",
"basis": {
"celex": "32006L0112",
"article": "40",
"title": "Place of an intra-Community acquisition of goods: where dispatch ends",
"query": "/legal/eu-act?celex=32006L0112&article=40"
}
},
"rate": {
"country": "DE",
"kind": "standard",
"percentage": "19",
"reduced_rate_candidates": ["7"],
"super_reduced_rate_candidates": [],
"has_regional_rates": false
},
"required_invoice_mentions": [
{
"text": "Reverse charge",
"language": "en",
"basis": [
{ "celex": "32006L0112", "article": "226", "title": "Compulsory particulars on invoices", "query": "/legal/eu-act?celex=32006L0112&article=226" }
]
},
{
"text": "Exonération de TVA, article 262 ter I du CGI",
"language": "fr",
"basis": [
{ "celex": "32006L0112", "article": "138", "title": "Exemption of intra-Community supplies of goods", "query": "/legal/eu-act?celex=32006L0112&article=138" },
{ "celex": "32006L0112", "article": "226", "title": "Compulsory particulars on invoices", "query": "/legal/eu-act?celex=32006L0112&article=226" }
],
"national_basis": { "jurisdiction": "FR", "reference": "CGI, art. 262 ter I" }
}
],
"computed": {
"currency": "EUR",
"taxable_amount": "12500.00",
"rate_percentage": "0",
"vat_amount": "0.00",
"total_amount": "12500.00",
"rounding": "rounded to 2 decimals with fn:round(x*100) div 100, the rounding of the EN 16931 engine (the minor unit of every Member State currency is the hundredth)",
"note": "The supplier charges no VAT: the customer accounts for it under the reverse charge, so the invoice total carries no VAT and must bear the mention above."
},
"customer_vat_check": {
"status": "declared_valid",
"how_to_verify": "GET /company/vat?vat_number=<the customer's VAT number, e.g. DE…> (company_grid, live VIES lookup)"
},
"caveats": [
"The rate returned is the one the customer self-assesses in the Member State of arrival; the supplier invoices no VAT.",
"The exemption is conditional on evidence that the goods left the Member State of dispatch (Art. 138(1), 131) and on filing a recapitulative statement: neither is checked here."
]
}
}
All examples on this page are real service responses, abridged: the
constant limits[], the full legal_basis[] and the remaining caveats[]
are served in full by the endpoint.
The two rates in that answer say different things: rate.percentage is 19 %
(what the German customer self-assesses), computed.rate_percentage is 0
(what the French supplier charges). Pass this determination to
/invoice/generate through its vat_treatment
field and the mention is written into the invoice for you.
Example - the same supply, without a proven VAT number
Same transaction, customer_vat_status left at unknown. The exemption falls
away and the French supplier charges French VAT - and the response says
why, in rationale:
{
"data": {
"rule_id": "INV-VAT-D02",
"rule_summary": "Intra-EU B2B dispatch without a proven VAT number: no exemption, the supplier charges its own VAT (Art. 138(1)(b))",
"liable": "supplier",
"mechanism": "standard",
"place_of_supply": { "country": "FR", "basis": { "celex": "32006L0112", "article": "32", "title": "Place of supply of goods dispatched or transported", "query": "/legal/eu-act?celex=32006L0112&article=32" } },
"rate": {
"country": "FR",
"kind": "standard",
"percentage": "20",
"reduced_rate_candidates": ["5.5", "10"],
"super_reduced_rate_candidates": ["2.1"],
"has_regional_rates": true
},
"required_invoice_mentions": [],
"rationale": "A VAT identification number of the customer, issued by another Member State and valid at the time of the supply, is a substantive condition of the exemption (Art. 138(1)(b)). It is declared as invalid or unknown here, so the exemption is not granted and the supply is taxed in the Member State of dispatch.",
"computed": {
"currency": "EUR",
"taxable_amount": "12500.00",
"rate_percentage": "20",
"vat_amount": "2500.00",
"total_amount": "15000.00",
"note": "VAT is charged by the supplier at the standard rate of the place of supply."
},
"customer_vat_check": {
"status": "not_checked",
"how_to_verify": "GET /company/vat?vat_number=<the customer's VAT number, e.g. DE…> (company_grid, live VIES lookup)"
}
}
}
invalid and unknown are treated identically: the Directive does not
distinguish “we checked and it is wrong” from “we did not check”. Both make
the exemption fall away. The way out is not a flag - it is
GET /company/vat, which asks VIES.
Example - undetermined, a result rather than a failure
An importation. The determination is refused, the refusal is motivated,
and the answer is a paid 200:
{
"data": {
"rule_id": "INV-VAT-I01",
"rule_summary": "Import of goods into the Union: customs procedure, out of v1 scope (Art. 60, 201)",
"liable": "undetermined",
"mechanism": "undetermined",
"place_of_supply": { "country": null },
"required_invoice_mentions": [],
"legal_basis": [
{ "celex": "32006L0112", "article": "60", "title": "Place of importation of goods", "query": "/legal/eu-act?celex=32006L0112&article=60" },
{ "celex": "32006L0112", "article": "201", "title": "VAT on importation is payable by the person designated as liable", "query": "/legal/eu-act?celex=32006L0112&article=201" }
],
"rationale": "VAT on importation is due in the Member State where the goods enter the Union (Art. 60), which is not derivable from the countries of the parties. Import VAT and customs regimes are outside the scope of this service.",
"caveats": [
"Import VAT is assessed under customs procedures, and who pays it depends on the customs status of the parties, on the incoterms and on national deferment or reverse-charge schemes (Art. 201)."
]
}
}
Note what is not there: no rate, no computed, and
place_of_supply.country is null rather than the country of one of the
parties. An undetermined verdict never leaks a plausible-looking number.
Other real undetermined answers, each with its own rationale: a
services_land_related supply without property_country (INV-VAT-L01); the
same supply with it, because who accounts for the tax on immovable
property is not harmonised - Art. 194 leaves it to each Member State
(INV-VAT-L03); a services_b2b_general claimed for a consumer
(INV-VAT-S06); a B2C service to a customer outside the Union, where Art. 59
moves only a closed list of services (INV-VAT-C02).
Example - export outside the Union
{
"data": {
"rule_id": "INV-VAT-X01",
"rule_summary": "Export of goods out of the Union: exempt with credit (Art. 146)",
"liable": "supplier",
"mechanism": "exempt_with_credit",
"place_of_supply": { "country": "FR", "basis": { "celex": "32006L0112", "article": "32", "title": "Place of supply of goods dispatched or transported", "query": "/legal/eu-act?celex=32006L0112&article=32" } },
"required_invoice_mentions": [
{ "text": "VAT exempt export of goods - Article 146 of Directive 2006/112/EC", "language": "en", "basis": [ { "celex": "32006L0112", "article": "146", "title": "Exemption of exports of goods outside the Union", "query": "/legal/eu-act?celex=32006L0112&article=146" } ] },
{ "text": "Exonération de TVA, article 262 I du CGI", "language": "fr", "basis": [ { "celex": "32006L0112", "article": "146", "title": "Exemption of exports of goods outside the Union", "query": "/legal/eu-act?celex=32006L0112&article=146" } ], "national_basis": { "jurisdiction": "FR", "reference": "CGI, art. 262 I" } }
],
"computed": {
"currency": "EUR",
"taxable_amount": "5000.00",
"rate_percentage": "0",
"vat_amount": "0.00",
"total_amount": "5000.00",
"note": "The supply is exempt with a right to deduct: no VAT is charged, and the invoice must bear the exemption mention above."
},
"caveats": [
"The exemption is conditional on proof that the goods left the Union (Art. 146(1), 131): customs evidence is not checked here.",
"Import VAT and duties in the country of destination are outside the scope of this service."
]
}
}
A supply where both parties sit outside the VAT territory of the Union
comes back as out_of_scope (INV-VAT-G05), with liable
not_taxable_in_eu - and a rationale pointing at export_outside_eu or
import if goods actually cross that border.
What this endpoint will not do
The six sentences below travel in data.limits with every answer. They
are the contract, not small print.
- The one-stop-shop schemes (OSS, IOSS) and the distance-selling thresholds
of Art. 33 and 59c are not applied: a supply that falls under them is
returned as
undetermined, never guessed. - Import VAT and customs procedures (Art. 60, 201) are not determined: the person liable is designated by the Member State of importation.
- The margin schemes (second-hand goods, works of art, travel agents) and the special scheme for investment gold are not applied.
- Chain and triangular transactions involving more than the two parties declared are not judged, and no transport ascription is made.
- No product classification: the rate served is the standard rate of the place of supply, reduced rates being candidates only (Annex III and national nomenclatures).
- The verdict is rendered on the data declared in the request: no registry
or VIES lookup happens here - counterparty checks are
/company/resolveand/company/vat.
It is a determination, not tax advice, and it does not file anything: no VAT return, no recapitulative statement, no Intrastat.
Coverage of the rate snapshot
Rates come from a committed snapshot of the Commission’s TEDB covering
2016-01-01 onwards, each rate carrying its own validity, so a date in
2019 gets the rate that was in force in 2019. A date before the snapshot
is a 400 date_out_of_range naming the covered window - a rate is never
extrapolated backwards. The snapshot date is in every response
(provenance.freshness.as_of).
Errors
Typed 4xx responses are not billed. Faults of the body are accumulated:
one 400 lists every actionable issue at once, in issues[].
| Status | code | issues[].code | Case |
|---|---|---|---|
| 400 | INVALID_INPUT | - | Body is not readable JSON, an unknown field, or a supply_kind / transaction_type / customer_type / customer_vat_status outside its enumeration - the admitted values are named |
| 400 | INVALID_INVOICE_INPUT | missing_field | A required field is absent (including currency when amount is given) |
| 400 | INVALID_INVOICE_INPUT | invalid_country | Not an ISO 3166-1 alpha-2 code |
| 400 | INVALID_INVOICE_INPUT | invalid_date | Not a real calendar date in YYYY-MM-DD form |
| 400 | INVALID_INVOICE_INPUT | date_out_of_range | Date before the rate snapshot |
| 400 | INVALID_INVOICE_INPUT | invalid_decimal | amount is not a plain decimal (scientific notation is refused) |
| 400 | INVALID_INVOICE_INPUT | invalid_currency | XXX or XTS - ISO 4217 codes with no billing use |
| 500 | INTERNAL | - | Internal error (detail logged, not exposed) |
{
"code": "INVALID_INVOICE_INPUT",
"error": "invoice input is invalid: 2 actionable issue(s) listed in issues[]",
"issues": [
{ "code": "invalid_country", "message": "supplier_country must be an ISO 3166-1 alpha-2 country code, got \"FRA\"", "path": "supplier_country" },
{ "code": "invalid_country", "message": "customer_country must be an ISO 3166-1 alpha-2 country code, got \"DEU\"", "path": "customer_country" }
]
}
{
"code": "INVALID_INVOICE_INPUT",
"error": "invoice input is invalid: 1 actionable issue(s) listed in issues[]",
"issues": [
{ "code": "date_out_of_range", "message": "date 2010-01-01 predates the VAT rate snapshot, which covers 2016-01-01 onwards (rates in force before that date are not known to the service)", "path": "date" }
]
}
Every refusal names what is admitted, so a second call can succeed.
Attribution
The rules are those of Council Directive 2006/112/EC on the common system
of value added tax, as published on EUR-Lex; the articles cited are queryable
through GET /legal/eu-act. The rates come from a
committed extract of the European Commission’s Taxes in Europe Database
(TEDB). National invoice wordings cite their own domestic basis in
national_basis.
See also
POST /invoice/vat-treatment/batch- the same determination on up to 200 transactions, in one settlement.POST /invoice/generate- pass this determination asvat_treatmentand the compulsory mention is written into the invoice.POST /invoice/validate- the EN 16931 verdict on a document, plus thevat_consistencyblock that flags an invoice contradicting its own VAT logic.GET /company/vat- the live VIES check this endpoint deliberately does not perform.GET /legal/eu-act- the text of the articles cited inlegal_basis, at a date.- For agents - discovery surfaces, the live
/catalogand how settlement works.