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" }
  ]
}
FieldTypeRequiredDescription
itemsarrayyesTransactions 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" }
  }
}
FieldDescription
resultsOne entry per submitted item, in input order, each carrying its own index
summaryitems, ok, errors and the count of the five mechanisms, zeros included
sharedDeduplicated sets of legal_basis and caveats, cited by key from each result
limitsThe six constant sentences, served once for the batch

results[]

FieldDescription
indexPosition in the submitted items[]
statusok or error
verdictOn ok: the data of the single call minus legal_basis, caveats and limits
legal_basis_refOn ok: index into shared.legal_basis
caveats_refOn ok: index into shared.caveats
errorOn 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 error at its position, with the same issues[] vocabulary a single call would have used, and the other four were judged;
  • the importation is ok with mechanism: "undetermined" - a judged line whose judgement is a motivated refusal, counted as such in summary. It is not an error, and the distinction is deliberate;
  • summary.mechanisms carries all five keys, zeros included - an absent key would read as an omission. Here out_of_scope: 0 states 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.

Statuscodeissues[].codeCase
400INVALID_INPUT-Body is not readable JSON, or items is absent
400INVALID_INVOICE_INPUTinvalid_batch_sizeitems[] is empty, or carries more than 200 transactions
500INTERNAL-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 /catalog and how settlement works.