POST /invoice/vat-treatment/batch
A sales ledger or an order book judged in one call: up to 200
transactions, one VAT determination each, settled with one x402 payment.
Every item runs through the same engine as
POST /invoice/vat-treatment - place of
supply, person liable, mechanism, the rate in force at the date of that
line, the compulsory invoice mentions and the articles of
Directive 2006/112/EC they rest on.
One settlement for N transactions fits the job it exists for: closing a month of sales, re-judging an order book after a customer’s VAT number was verified, or checking a supplier ledger before it is booked.
The batch is settled as one x402 payment for the whole call and priced
per transaction by the gateway (the first item is roughly the price of the
single call, then each additional item adds to the total) - see the live
/catalog for the authoritative price.
x402 golden rule: the agent pays for the answer to its question. A
well-formed batch is a successful answer -> 200, even when some of its
items are malformed (each flagged at its position) or undetermined (each
with its own rationale). The 4xx range is reserved for what the service cannot
answer - missing or unreadable body, empty list, batch over the cap. Those
are never charged.
Scope and limits
Identical to the single call: the customer’s VAT number is taken as
declared (zero network - verify it with
GET /company/vat), and the uncovered regimes
(OSS/IOSS and distance-selling thresholds, import VAT, margin schemes, chains
and triangulars, product classification to reduced rates) come back as a
motivated undetermined, never a plausible default. The six constant
sentences are served once for the whole batch in data.limits. See
POST /invoice/vat-treatment.
Request
POST with a JSON body. Set Content-Type: application/json.
POST /invoice/vat-treatment/batch
Content-Type: application/json
{
"items": [
{ "supplier_country": "FR", "customer_country": "FR", "transaction_type": "goods", "customer_type": "business", "supply_kind": "general", "date": "2026-07-01", "amount": "1200.00", "currency": "EUR" },
{ "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" },
{ "supplier_country": "FR", "customer_country": "US", "transaction_type": "goods", "customer_type": "business", "supply_kind": "export_outside_eu", "date": "2026-07-01", "amount": "5000.00", "currency": "EUR" },
{ "supplier_country": "US", "customer_country": "FR", "transaction_type": "goods", "customer_type": "business", "supply_kind": "import", "date": "2026-07-01" },
{ "supplier_country": "FRA", "customer_country": "DE", "transaction_type": "goods", "customer_type": "business", "supply_kind": "general", "date": "2026-07-01" }
]
}
| Field | Type | Required | Description |
|---|---|---|---|
items | array | yes | Transactions judged in one settlement, 1 to 200 entries |
Each item is exactly the body of the single endpoint - same required
fields, same enumerations, same optional customer_vat_status,
property_country, amount and currency. See
its parameter table.
An empty or oversized items[] is the only thing that fails the
batch: a 400 invalid_batch_size, not billed. A malformed item does not -
it is reported at its position.
200 response - UnifiedResponse
{
"data": {
"results": [ ... ],
"summary": { ... },
"shared": { "legal_basis": [ ... ], "caveats": [ ... ] },
"limits": [ ... ]
},
"provenance": {
"source": "directive-2006-112 + tedb",
"fetched_at": "2026-07-30T18:09:12Z",
"freshness": { "kind": "snapshot", "as_of": "2026-07-27T00:00:00Z" }
}
}
| Field | Description |
|---|---|
results | One entry per submitted item, in input order, each carrying its own index |
summary | items, ok, errors and the count of the five mechanisms, zeros included |
shared | Deduplicated sets of legal_basis and caveats, cited by key from each result |
limits | The six constant sentences, served once for the batch |
results[]
| Field | Description |
|---|---|
index | Position in the submitted items[] |
status | ok or error |
verdict | On ok: the data of the single call minus legal_basis, caveats and limits |
legal_basis_ref | On ok: index into shared.legal_basis |
caveats_ref | On ok: index into shared.caveats |
error | On error: what a single call would have returned as a 400, in the exact same shape (code, message, issues[]) |
The verdict object is the unit verdict field for field - rule_id,
rule_summary, mechanism, liable, place_of_supply, rate,
required_invoice_mentions, customer_vat_check, notes, plus computed
when the item carried an amount and rationale when there is something to
explain.
shared - the same articles are not repeated 200 times
Over a ledger mixing the five mechanisms, the distinct sets of legal bases and
caveats can be counted on one hand. They are served once in shared, and each
result points at its set by key. In the five-line example below, four distinct
sets were used - one per determined mechanism.
Example - a five-line ledger, real response
The body above, judged in one call. One domestic sale, one intra-EU dispatch, one export, one importation that cannot be determined, and one item whose country code is malformed. Abridged - the verdicts are those documented on the single-call page:
{
"data": {
"results": [
{ "index": 0, "status": "ok", "legal_basis_ref": 0, "caveats_ref": 0,
"verdict": { "rule_id": "INV-VAT-G01", "mechanism": "standard", "liable": "supplier",
"place_of_supply": { "country": "FR", "basis": { "celex": "32006L0112", "article": "31", "title": "Place of supply of goods not dispatched or transported", "query": "/legal/eu-act?celex=32006L0112&article=31" } },
"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": [],
"computed": { "currency": "EUR", "taxable_amount": "1200.00", "rate_percentage": "20", "vat_amount": "240.00", "total_amount": "1440.00", "note": "VAT is charged by the supplier at the standard rate of the place of supply." } } },
{ "index": 1, "status": "ok", "legal_basis_ref": 1, "caveats_ref": 1,
"verdict": { "rule_id": "INV-VAT-D01", "mechanism": "reverse_charge", "liable": "customer",
"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" } ] } ],
"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)" } } },
{ "index": 2, "status": "ok", "legal_basis_ref": 2, "caveats_ref": 2,
"verdict": { "rule_id": "INV-VAT-X01", "mechanism": "exempt_with_credit", "liable": "supplier" } },
{ "index": 3, "status": "ok", "legal_basis_ref": 3, "caveats_ref": 3,
"verdict": { "rule_id": "INV-VAT-I01", "mechanism": "undetermined", "liable": "undetermined",
"place_of_supply": { "country": null },
"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." } },
{ "index": 4, "status": "error",
"error": {
"code": "INVALID_INVOICE_INPUT",
"message": "VAT treatment input is invalid: 1 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" } ]
} }
],
"summary": {
"items": 5,
"ok": 4,
"errors": 1,
"mechanisms": { "standard": 1, "reverse_charge": 1, "exempt_with_credit": 1, "out_of_scope": 0, "undetermined": 1 }
}
}
}
Three things in that response are the whole point of the batch:
- the malformed line did not sink the call - item 4 carries its
errorat its position, with the sameissues[]vocabulary a single call would have used, and the other four were judged; - the importation is
okwithmechanism: "undetermined"- a judged line whose judgement is a motivated refusal, counted as such insummary. It is not anerror, and the distinction is deliberate; summary.mechanismscarries all five keys, zeros included - an absent key would read as an omission. Hereout_of_scope: 0states that no line fell outside the Union’s territorial scope.
An item that is unreadable altogether (not an object, an unknown field) comes
back with code: "INVALID_INPUT"; an item that is readable but wrong comes
back with INVALID_INVOICE_INPUT listing every fault of that item at once.
Errors
Only the batch envelope can fail. Typed 4xx responses are not billed.
| Status | code | issues[].code | Case |
|---|---|---|---|
| 400 | INVALID_INPUT | - | Body is not readable JSON, or items is absent |
| 400 | INVALID_INVOICE_INPUT | invalid_batch_size | items[] is empty, or carries more than 200 transactions |
| 500 | INTERNAL | - | Internal error (detail logged, not exposed) |
{
"code": "INVALID_INVOICE_INPUT",
"error": "invoice input is invalid: 1 actionable issue(s) listed in issues[]",
"issues": [
{ "code": "invalid_batch_size", "message": "items[] carries 201 transactions, above the cap of 200 per batch: split the journal into batches of at most 200", "path": "items" }
]
}
Attribution
Same as the single call: Council Directive 2006/112/EC as published on EUR-Lex for the rules, a committed extract of the European Commission’s Taxes in Europe Database (TEDB) for the rates.
See also
POST /invoice/vat-treatment- the single determination, with the full field reference and the five mechanisms.POST /invoice/generate- turn a determination into an invoice carrying its compulsory mention.GET /company/vat- the live VIES check for the customer VAT numbers a batch takes as declared.- For agents - discovery surfaces, the live
/catalogand how settlement works.