Taxes
The tax engine resolves the right rate, EN 16931 category code, and exemption text for every invoice line - automatically, from a small set of inputs (your org's tax regime, the customer's billing country and tax IDs, the product's tax category, and the invoice date). Compliance with §14 UStG and EN 16931 is built into the determination flow; PDF and XRechnung exports render the codes and exemption notices unchanged.
Determination flow
Inputs:
| Input | Source |
|---|---|
| Seller country, regime | Org tax profile (STANDARD, KLEINUNTERNEHMER, OSS, NON_EU) |
| Buyer country | Customer billing address |
| Buyer is a business with valid VAT | Customer has a VIES-validated eu_vat ID |
| Product tax category | DEFAULT / REDUCED / ZERO / EXEMPT on the product |
| Date | Invoice transaction date - picks the active TaxRule version |
Decision matrix (first match wins):
| Case | Result |
|---|---|
Seller is KLEINUNTERNEHMER (§19 UStG) | 0% tax, exemption code vatex-eu-132, Kleinunternehmer notice rendered on the line |
| Seller country == buyer country | Domestic rate from the EU VAT registry (or your TaxRule for the country) at the right tax category |
| Intra-EU + buyer has valid VAT (B2B) | Reverse charge: 0% tax, code AE, exemption vatex-eu-ae, reverse_charge: true |
| Intra-EU + buyer is consumer (B2C) | Destination country's VAT rate (OSS regime) |
| EU seller → non-EU buyer | Zero-rated export, code G, exemption vatex-eu-g |
| Non-EU seller → EU business buyer | Reverse charge (same shape as intra-EU B2B) |
Each line records the resolved tax_rate, tax_type, tax_amount, tax_category_code, tax_exemption_reason, supply_type, and the pinned tax_rule_id so any later-superseded rate replays bit-exact.
Tax rules
A TaxRule is one row per (jurisdiction_country, jurisdiction_region, tax_type) natural key. The same key can stack on a timeline via valid_from / valid_to.
| Field | Notes |
|---|---|
jurisdiction_country | ISO 3166-1 alpha-2 (DE, FR, US, …) |
jurisdiction_region | Optional sub-jurisdiction (US state, Canadian province) - left null for country-level rates |
tax_type | VAT (default for EU), SALES_TAX (US-style), GST (AU/CA/IN), or CUSTOM |
rate | Percentage as a float (19.0 = 19%) |
inclusive | When true the rate is already baked into displayed prices (gross-pricing markets); when false it's added on top |
is_compound / apply_order | For stacked taxes (e.g. CA province on top of GST) - is_compound: true applies the rate to the already-taxed subtotal; apply_order sets the sequence |
valid_from / valid_to | Time window the row is active |
status | draft → active → archived (one-way; the unified archiving model) |
version | Bumps each time the natural key gets a new row |
Rules are created in draft, edited freely, then promoted with POST /v1/tax-rules/{id}/publish. Publishing is what makes them visible to invoice resolution. To retire one cleanly, use POST /v1/tax-rules/{id}/archive - existing invoices keep their pinned tax_rule_id and replay correctly even after the rule is archived.
The EU VAT rates registry (next-but-one section) ships with current standard / reduced / super-reduced rates for every EU-27 country. For a domestic EU sale you typically don't need to author a
TaxRuleat all - only for non-EU jurisdictions, custom taxes, or when overriding a rate.
Product tax category vs. EN 16931 output code
Two different things, often confused:
| Where it lives | Values | Purpose | |
|---|---|---|---|
product.tax_category | Set on each product | DEFAULT, REDUCED, ZERO, EXEMPT | Tells the engine "for this product, use the standard rate / reduced rate / zero / exempt" - the engine then resolves the actual percentage from the destination country's rate registry |
EN 16931 tax_category_code | Stamped on each invoice line at finalize | S, Z, E, AE, K, G, O | The EN 16931 / Peppol BIS output code for the line - what shows up on XRechnung exports and what tax authorities consume |
You set tax_category on the product. The engine derives the tax_category_code from the determination flow above. Don't try to set the EN 16931 code directly.
Reverse charge
When the supply type resolves to intra-EU B2B (or non-EU seller → EU business buyer), reverse-charge treatment applies:
- Line carries
tax_rate: 0,tax_amount: 0,tax_category_code: AE reverse_charge: trueon the line- Exemption text is rendered on PDF and XRechnung (
"VAT reverse charge - tax liability transfers to the recipient", with §13b UStG variants for German sellers) - The buyer's VAT ID is included on the line for both invoice formats
Reverse charge requires the customer to have a passing eu_vat verification - the engine drops to the standard intra-EU B2C path if VIES validation has expired.
VIES validation
EU VAT IDs are validated through the EU's VIES service via the eu_vat verification type:
- VIES is queried at customer-create / VAT-ID-update time, then re-checked periodically.
- A passing verification has a
valid_untilwindow - until then, B2B reverse charge applies for that customer. - An expired check downgrades the customer to consumer (B2C) treatment until re-verification succeeds. There's no dedicated
verification.expiredevent today - poll the verification or watch the next invoice's tax treatment if you need to react.
Tax export
Code
Formats:
datev- DATEV-compatible CSV for German bookkeepingxrechnung_summary- per-invoice summary with VAT IDs and totalsoss_quarterly- One-Stop-Shop quarterly return rows
Verification IDs
Customer tax IDs ride a verification flow: the eu_vat type runs against VIES periodically, and only verified IDs unlock reverse-charge treatment. An expired VIES check downgrades the customer to local-rate treatment until reverification succeeds.
EU VAT rates table
EUVATRate is a maintained reference table (per country, per category, with effective dates) you can consult to seed your tax rules. Updates land via Kontorion's reference-data feed and don't auto-apply - the org must opt in by creating tax rule rows.
Code
| Param | Notes |
|---|---|
from, to | Required, YYYY-MM-DD. to is inclusive (treated as end-of-day). |
format | csv (default) or json. |
jurisdiction | Optional ISO country code filter - only return lines for sales into this jurisdiction. |
CSV columns: invoice_id, invoice_number, line_description, product_id, subtotal, tax_type, tax_rate, tax_amount, is_compound, jurisdiction_country, period_start, period_end. JSON returns the same fields as objects.
EU VAT rates
GET /v1/eu-vat-rates returns the current EU-27 reference table - standard, reduced, and super-reduced rates per country, with effective dates. The registry is maintained centrally and the determination engine reads from it for any EU domestic / B2C resolution; you usually don't need to author your own TaxRule for these.
Code
Endpoints
- Tax Rules - CRUD + publish / archive / copy + the version timeline + scheduling
- Tax Compliance - EU VAT registry (
/eu-vat-rates) - Tax Export -
/tax/export
Related
- Verifications -
eu_vatverification drives reverse-charge eligibility - Products - where
tax_categoryis set - Invoices - line-level tax fields and replay behaviour
- Customers - billing address + tax IDs that feed determination
- Organization Settings - your org's tax regime and country