# EU VAT determination for a sales ledger, 200 lines per call > One VAT determination per line — place of supply, mechanism, rate, compulsory mentions — for up to 200 transactions in a single paid call, each sourced article by article. Published 2026-08-10 · Updated 2026-08-15 · HTML version: https://invoket.com/blog/vat-treatment-for-a-sales-ledger-in-one-settlement --- VAT is decided **transaction by transaction**, but it is worked on **ledger by ledger**. An agent closing a month of sales, re-judging an order book, or checking a supplier journal before it is booked needs one determination per line — who charges the tax, under which mechanism, where it is taxed, at which rate, and what the invoice must say. One paid call — [`POST /invoice/vat-treatment/batch`](/docs/api/invoice-vat-treatment-batch) — judges up to **200 transactions** under Directive 2006/112/EC, each sourced article by article, settled as **one x402 payment** for the whole call. ## The problem: an EN 16931 validator judges the document, not the transaction An invoice can satisfy every normative `BR-*` rule of EN 16931 and still be refused, because the rules that make it wrong are not in the document schema. An intra-EU B2B dispatch invoiced without its reverse-charge mention is structurally perfect and substantively broken. That is the split between the two endpoints: [`/invoice/validate`](/docs/api/invoice-validate) judges the **document**, [`/invoice/vat-treatment`](/docs/api/invoice-vat-treatment) judges the **transaction**. The judgement is also line-local. Two rows of the same ledger, same supplier, same month, differ on one declared field — a customer VAT number known valid versus unknown — and land on opposite mechanisms: exempt with the customer self-accounting, or French VAT charged at the standard rate. There is no ledger-level verdict to compute; there are N verdicts to collect. ## Why this is a 2026 problem and not a 2030 one The regulatory clock is what turns this from a spreadsheet chore into an agent's job. - **France**: from **1 September 2026**, every VAT-registered business established in France must be able to **receive** electronic invoices, and large and intermediate-sized enterprises must **issue** them; micro, small and medium enterprises follow by **1 September 2027** ([impots.gouv.fr](https://www.impots.gouv.fr/professionnel/je-passe-la-facturation-electronique)). - **European Union**: the *VAT in the Digital Age* package — [Council Directive (EU) 2025/516 of 11 March 2025](https://eur-lex.europa.eu/eli/dir/2025/516/oj), in force since 14 April 2025 — makes e-invoicing the default and imposes digital reporting of cross-border B2B supplies from **1 July 2030**, with domestic reporting systems to be aligned by **1 January 2035** ([European Commission](https://taxation-customs.ec.europa.eu/taxation/vat/vat-digital-age-vida_en)). Structured invoices in, structured reporting out. What stops being tolerable in that pipeline is a mechanism guessed by a template. ## The call: one array, up to 200 transactions The body carries a single field, `items`, holding 1 to 200 entries. Each entry is **exactly** the body of the single call — same required fields, same closed enumerations, same optional `customer_vat_status`, `amount` and `currency`. ```http POST /invoice/vat-treatment/batch Content-Type: application/json ``` ```json { "items": [ { "supplier_country": "FR", "customer_country": "IT", "transaction_type": "services", "customer_type": "business", "customer_vat_status": "valid", "supply_kind": "services_b2b_general", "date": "2026-07-15", "amount": "3200.00", "currency": "EUR" }, { "supplier_country": "FR", "customer_country": "ES", "transaction_type": "goods", "customer_type": "business", "customer_vat_status": "unknown", "supply_kind": "intra_eu_dispatch", "date": "2026-07-15", "amount": "4000.00", "currency": "EUR" }, { "supplier_country": "FR", "customer_country": "CH", "transaction_type": "goods", "customer_type": "business", "supply_kind": "export_outside_eu", "date": "2026-07-15", "amount": "9000.00", "currency": "EUR" } ] } ``` `supply_kind` is never inferred: it is one of eight declared values (domestic, intra-EU dispatch, export, import, B2B services, B2C services, land-related services, electronic B2C services). The **date of each line** selects the rate in force on that line — a July 2019 row is rated with the 2019 rate, not today's. ## What comes back: one verdict per line, one snapshot for the ledger The response is the standard `UnifiedResponse`. Four properties make it usable as a month-close primitive: - **`results[]` is in input order**, each element carrying its own `index`, so verdicts re-pair with ledger rows without bookkeeping. - **Each `verdict` is the unit determination** field for field: `rule_id`, `mechanism`, `liable`, `place_of_supply` with its cited article, `rate`, `required_invoice_mentions`, plus `computed` when the line carried an amount and `rationale` when there is something to explain. - **`shared` deduplicates the legal work.** Over 200 lines mixing five mechanisms, the distinct sets of legal bases and caveats can be counted on one hand; they are served once, and each result points at its set through `legal_basis_ref` and `caveats_ref`. - **One `provenance` covers the whole call** — `directive-2006-112 + tedb`, with `freshness.as_of` dating the committed rate snapshot. Every line of the ledger was judged against the same stated state of the rates. Here is the three-line ledger above, abridged from a real response: ```json { "data": { "count": 3, "results": [ { "index": 0, "status": "ok", "legal_basis_ref": 0, "caveats_ref": 0, "verdict": { "rule_id": "INV-VAT-S02", "mechanism": "reverse_charge", "liable": "customer", "place_of_supply": { "country": "IT", "basis": { "celex": "32006L0112", "article": "44", "title": "Place of supply of services to a taxable person: where the customer is established", "query": "/legal/eu-act?celex=32006L0112&article=44" } }, "rate": { "country": "IT", "kind": "standard", "percentage": "22" }, "required_invoice_mentions": [ { "text": "Reverse charge", "language": "en" }, { "text": "Autoliquidation", "language": "fr", "national_basis": { "jurisdiction": "FR", "reference": "CGI, art. 283-2 (mention obligatoire, art. 242 nonies A ann. II)" } } ], "computed": { "taxable_amount": "3200.00", "rate_percentage": "0", "vat_amount": "0.00", "total_amount": "3200.00" } } }, { "index": 1, "status": "ok", "legal_basis_ref": 1, "caveats_ref": 1, "verdict": { "rule_id": "INV-VAT-D02", "mechanism": "standard", "liable": "supplier", "place_of_supply": { "country": "FR" }, "rate": { "country": "FR", "kind": "standard", "percentage": "20" }, "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": { "taxable_amount": "4000.00", "rate_percentage": "20", "vat_amount": "800.00", "total_amount": "4800.00" } } }, { "index": 2, "status": "ok", "legal_basis_ref": 2, "caveats_ref": 2, "verdict": { "rule_id": "INV-VAT-X01", "mechanism": "exempt_with_credit", "liable": "supplier" } } ], "summary": { "items": 3, "ok": 3, "errors": 0, "mechanisms": { "standard": 1, "reverse_charge": 1, "exempt_with_credit": 1, "out_of_scope": 0, "undetermined": 0 } } }, "provenance": { "source": "directive-2006-112 + tedb", "freshness": { "kind": "snapshot", "as_of": "2026-07-27T00:00:00Z" } } } ``` Two rates are in play on line 0 and they say different things: `rate.percentage` is what the Italian customer self-assesses, `computed.rate_percentage` is what the French supplier charges — zero. An agent filling the invoice reads `computed`; an agent booking the acquisition reads `rate`. ### `summary` is the ledger-level read `summary.mechanisms` carries all **five** keys, zeros included — an absent key would read as an omission rather than a count of zero. It is what an agent scans first: how many lines carry a compulsory mention, how many left the Union's territorial scope, and how many came back `undetermined` and therefore need a human or a different source. ## The x402 golden rule, applied to a ledger The agent pays for the *answer to its question*, and "197 lines judged, 2 need your VAT numbers verified, 1 row is malformed" is a successful answer. A well-formed batch returns **200** whatever the mix inside it: - A **malformed item does not sink the call.** It comes back at its position with `status: "error"` and the exact `code` / `message` / `issues[]` a single call would have produced — an unknown `supply_kind`, for instance, is refused by name with the admitted values listed back. - An **`undetermined` line is `ok`, not an error.** It is a judged line whose judgement is a motivated refusal, counted as such in `summary`. An uncovered regime answered with a plausible default would be worse than no answer. - Only the **envelope** can fail: an unreadable body, an absent or empty `items`, or more than 200 transactions — a `400 invalid_batch_size` checked before any judging, and **not billed**. The batch settles as one x402 payment and is **priced per transaction** by the gateway, so the cost tracks the size of the ledger rather than the number of round-trips. The authoritative price and the accepted rails are served live by the [catalog](https://api.invoket.com/catalog) — never hardcoded here. The payment loop is the usual discover → `402` → pay → replay cycle; the [Quickstart](/docs/quickstart) walks it with runnable snippets and [For agents](/docs/for-agents) covers the discovery surfaces. ## A concrete shape: the month-close loop 1. **Project** the sales journal onto the item shape: two countries, goods or services, business or consumer, the declared `supply_kind`, the chargeability date, and the amount when you want VAT computed. 2. **Judge** the ledger: one `POST /invoice/vat-treatment/batch`, one `402`, one signature, one replay, up to 200 lines. 3. **Split on `summary`.** Lines with `mechanism: "undetermined"` and lines with `status: "error"` leave the automated path; the rest carry their mentions. 4. **Verify what was taken as declared.** Every `customer_vat_check` block whose status is `not_checked` points at [`GET /company/vat`](/docs/api/company-vat), the live VIES lookup — a valid customer VAT number is a *substantive* condition of the intra-EU exemption (Art. 138(1)(b)), so line 1 above flips from `standard` to `reverse_charge` only once the number checks out. Re-judge the corrected lines. 5. **Emit.** Pass each determination to [`POST /invoice/generate`](/docs/api/invoice-generate) through its `vat_treatment` field and the compulsory mention is written into the invoice for you. 6. **Log** `provenance.freshness.as_of` next to the closed period — the audit answer to "judged against which rates?". Steps 4 and 5 are why the batch is a *pre-emission* gate rather than a report: the correction happens before the invoice exists, not after a tax authority reads it. ## What this batch will not do The scope is exactly the single call's scope, and the six sentences that state it travel in `data.limits` with **every** answer: - **OSS and IOSS**, and the distance-selling thresholds of Art. 33 and 59c, are not applied — a line falling under them comes back `undetermined`. - **Import VAT and customs procedures** (Art. 60, 201) are not determined: the person liable is designated by the Member State of importation. - **Margin schemes**, investment gold, **chain and triangular transactions** beyond the two declared parties: not judged, no transport ascribed. - **No product classification.** The rate served is the **standard** rate of the place of supply; reduced rates come back as candidates only, and `has_regional_rates` flags States with territory-specific rates outside the snapshot. - **The verdict rests on what the request declares.** The runtime performs no registry or VIES lookup — that is [`/company/vat`](/docs/api/company-vat) and [`/company/resolve`](/docs/api/company-resolve). It is a determination, not tax advice, and it files nothing: no VAT return, no recapitulative statement, no Intrastat. The articles it cites are readable at a date through [`GET /legal/eu-act`](/docs/api/legal-eu-act), and the rates come from a committed extract of the Commission's Taxes in Europe Database covering 2016 onwards — a date before it is refused, never extrapolated. Used for what it is — one settled call that renders a sourced verdict on every line of a ledger against one dated snapshot — the batch makes VAT determination cheap enough to run **before** the invoices are cut. For the request contract, the field tables and the error codes, see the [`POST /invoice/vat-treatment/batch` documentation](/docs/api/invoice-vat-treatment-batch).