Alpha testing: all current functionality is free while VAT Engine is in active development

Calculate VAT

Calculate EU VAT with explicit amount basis, optional idempotency, resolved transaction dates, and rate and calculation evidence.

Endpoint

Treatment results depend on applicable reviewed coverage, not merely a matching CN/CPA code or rate. Unresolved source conditions remain reviewable uncertainty; they are not silently replaced by a broader rate. Historical stored decisions retain their original evidence when reviewed interpretations change.

POST /v1/vat/calculate

Required scope: full or calculate-only

Optional Headers

HeaderTypeDescription
X-Source-IDstringOptional headless storefront or custom checkout tag for reporting. Not required for calculation.
Idempotency-KeystringOptional authenticated retry key, 1-255 visible ASCII characters. Requires an explicit transaction_date.

X-Source-ID does not affect the VAT rate, net amount, VAT amount, or gross amount. It is captured with the logged VAT calculation event when transaction logging is enabled, so later reports can filter or export rows by checkout/source. Use a stable, non-sensitive source tag from 1 to 100 characters using letters, digits, _, -, or / (for example headless-checkout, custom-store/eu, or mobile-checkout). Invalid values return 400 invalid_source_id. Do not put customer names, emails, order numbers, API credentials, or other personal/secrets data in this header.

Use Idempotency-Key when a client may retry the same authenticated calculation. The same key and canonical request and source tag returns the original calculation; reusing it for a different request body or normalized X-Source-ID returns 409 idempotency_conflict. The mapping remains valid for the lifetime of that calculation. Do not put personal data, credentials, or business payloads in the key.

Request Body

ParameterTypeRequiredDescription
countrystringYesISO 3166-1 alpha-2 code (e.g., DE, FR, IT)
currencystringYesISO 4217 code (e.g., EUR, USD)
tax_class_idstringYesTax class identifier (e.g., standard, books, food)
gross_amount_minorintegerYesInput amount in minor currency units (e.g., €119.00 = 11900)
price_includes_vatbooleanYesSend explicitly: true for a gross input; false for a net input
transaction_datestringNoRFC 3339 or YYYY-MM-DD format. Defaults to current date.

Example Request

curl -X POST https://api.vat-engine.app/v1/vat/calculate \-H "X-API-Key: YOUR_API_KEY" \-H "Idempotency-Key: checkout-attempt-7f49" \-H "Content-Type: application/json" \-d '{  "country": "DE",  "currency": "EUR",  "gross_amount_minor": 11900,  "price_includes_vat": true,  "tax_class_id": "standard",  "transaction_date": "2026-01-28"}'

To tag the logged calculation for multi-store or multi-channel reports, add the optional source header:

-H "X-Source-ID: headless-checkout-eu"

Response

{
  "calculation_id": "e2d0940e-b123-47b7-80ad-8a3673fa4680",
  "country": "DE",
  "currency": "EUR",
  "vat_rate_bps": 1900,
  "gross_amount_minor": 11900,
  "net_amount_minor": 10000,
  "vat_amount_minor": 1900,
  "transaction_date": "2026-01-28T00:00:00Z",
  "rate_provenance": {
    "schema_version": "rate-evidence/v1",
    "evidence_status": "legacy_unverifiable",
    "source": "legacy_recorded_window",
    "effective_at": "2026-01-28T00:00:00Z",
    "source_evidence": []
  },
  "calculation_provenance": {
    "schema_version": "calculation-evidence/v1",
    "calculation_version": "vat-calculator-half-up/v1",
    "execution_digest": "...",
    "algorithm_manifest_version": "vat-algorithm-manifest/v1",
    "algorithm_manifest_digest": "...",
    "ownership": "tenant_owned",
    "evidence_status": "captured",
    "replay_status": "ineligible"
  }
}

Reviewed-rate responses additionally include their approved version, mapping and row digests, and one or more bounded public-safe snapshot references. Use the OpenAPI schema for the complete response contract.

Response Fields

FieldTypeDescription
calculation_idstring | nullStored transaction UUID. Null only for an unowned compatibility response that is not persisted.
countrystringISO country code
currencystringISO currency code
vat_rate_bpsintegerVAT rate in basis points (1900 = 19.00%)
gross_amount_minorintegerGross amount in minor units
net_amount_minorintegerNet amount (excluding VAT) in minor units
vat_amount_minorintegerVAT amount in minor units
transaction_datestringResolved UTC transaction time
rate_provenanceobjectEvidence status and reviewed or legacy rate-source references
calculation_provenanceobjectOwnership, captured-evidence state, calculation version, and replay status

Notes

  • All amounts are in minor currency units (cents) to avoid floating-point errors.
  • VAT rates are in basis points (1/100th of a percent). 1900 bps = 19.00%.
  • When price_includes_vat is true, the API extracts VAT from the gross amount. When false, it adds VAT on top.
  • Send price_includes_vat explicitly, including false for net input. Omitted or null values return 400 missing_price_includes_vat.
  • Date-only input resolves to midnight UTC. When Idempotency-Key is present, omitting transaction_date returns 400 transaction_date_required_for_idempotency.
  • calculation_id is the same UUID exposed as id by the Transactions API; VAT Engine does not create a second calculation identity.
  • captured evidence does not by itself make a calculation replayable. Only a record whose transaction detail reports replay_status: available can use the exact replay endpoint.
  • execution_digest identifies the calculation-version contract; it is not a hash of executable code. algorithm_manifest_digest binds the compile-time registered arithmetic, validation, currency-scale, rounding, and frozen-fixture contract. Calculation records store the manifest version and digest rather than a separate copy of the manifest.
  • X-Source-ID is optional reporting metadata, but it is part of idempotent request identity so a retry cannot silently change the stored calculation's source attribution. Omit it if you do not need store/channel filtering in transaction reports.
  • Managed source profiles can also register the same source key through POST /v1/sources, but the header itself stays a raw stable integration tag.
  • This endpoint records calculation history only. It does not add a sale to OSS threshold monitoring, Filing Prep, or other compliance reports.
  • After a governed treatment release is activated, v1 uses it only when the request is sufficient for an explicitly context-free numeric sales-VAT rule. It never assumes customer type, establishment, dispatch, liability, or a product classification. If a relevant exception cannot be resolved from v1 inputs, the endpoint returns 422 with guidance to use v2.

New authenticated calculations retain the available reviewed-rate lineage and calculation evidence before success is returned. Historical or fallback rate windows remain explicitly legacy_unverifiable; VAT Engine does not assign provenance merely because their numeric rate matches a reviewed rate. Calculations backed by complete sealed reviewed-rate evidence can report replay_status: available and use exact replay. Caller-selected historical alternatives are counterfactual simulations and are not accepted by the production calculation or replay endpoints.

Governed component-aware calculation (v2)

POST /v2/vat/calculate

Required scope: full or calculate-only

Required header: Idempotency-Key

The v2 endpoint evaluates four obligations independently: sales VAT, import VAT, customs duty, and fees. It preserves jurisdiction, liable party, collection point, amount status, legal basis, and provenance for each component. A component that cannot be established remains incomplete or unsupported; “outside scope here” never means “no tax anywhere.”

country is your jurisdiction assertion. If treatment_context.destination_country disagrees, the response is a durable 409 conflicting decision. A supplied catalog edition must already be part of the production treatment release selected for your tenant; callers cannot use it to bypass cutover authority.

curl -X POST https://api.vat-engine.app/v2/vat/calculate -H "X-API-Key: YOUR_API_KEY" -H "Idempotency-Key: order-2026-1042-tax-v1" -H "Content-Type: application/json" -d '{  "country": "DE",  "currency": "EUR",  "amount_minor": 10000,  "price_includes_vat": false,  "transaction_date": "2026-06-01",  "classification": {    "scheme": "cn",    "code": "49090000",    "catalog_version": "2026"  },  "treatment_context": {    "customer_type": "b2c",    "supplier_establishment_country": "DE",    "dispatch_country": "DE",    "destination_country": "DE"  }}'

For a tax-class-only evaluation, replace classification with the reviewed class used by your integration:

curl -X POST https://api.vat-engine.app/v2/vat/calculate -H "X-API-Key: YOUR_API_KEY" -H "Idempotency-Key: order-2026-1043-tax-v1" -H "Content-Type: application/json" -d '{  "country": "DE",  "currency": "EUR",  "amount_minor": 10000,  "price_includes_vat": false,  "transaction_date": "2026-06-01",  "tax_class_id": "standard"}'

When both selectors are supplied, VAT Engine verifies their governed mapping. A missing or different mapping is retained as a durable 409 classification_conflict; one selector never silently overrides the other.

For an authorized managed source profile, add its dedicated selector. The selected immutable revision is sealed into the decision, but ordinary profile defaults are configuration—not date-bounded legal evidence—and therefore cannot decide a legal predicate. The selector does not grant connector identity, verified confidence, or a different rollout:

-H "X-Evidence-Profile: storefront-eu"

The profile must be active and owned by the authenticated tenant. Unknown, foreign, inactive, or ambiguous selectors produce a durable 422 evidence_profile_unavailable decision. X-Source-ID remains reporting metadata and never selects this profile or a rollout.

Conflict example

This request makes DE the asserted jurisdiction but supplies FR as its destination. The conflict is persisted so an identical retry returns the same decision:

curl -X POST https://api.vat-engine.app/v2/vat/calculate -H "X-API-Key: YOUR_API_KEY" -H "Idempotency-Key: order-2026-1044-conflict-v1" -H "Content-Type: application/json" -d '{  "country": "DE",  "currency": "EUR",  "amount_minor": 10000,  "price_includes_vat": false,  "transaction_date": "2026-06-01",  "treatment_context": { "destination_country": "FR" }}'
{
  "status": "conflicting",
  "components": [
    {
      "kind": "sales_vat",
      "treatment": null,
      "treatment_status": "conflicting",
      "amount_status": "conflicting",
      "findings": ["jurisdiction_conflict"]
    }
  ]
}

The actual response includes all required component fields and exactly four components.

Unsupported example

The same endpoint fails closed when no activated treatment release covers the asserted jurisdiction and legal date:

curl -X POST https://api.vat-engine.app/v2/vat/calculate -H "X-API-Key: YOUR_API_KEY" -H "Idempotency-Key: order-2026-1045-unsupported-v1" -H "Content-Type: application/json" -d '{  "country": "DE",  "currency": "EUR",  "amount_minor": 10000,  "price_includes_vat": false,  "transaction_date": "2026-06-01"}'

The durable response uses HTTP 422, aggregate status unsupported, and the finding treatment_coverage_unavailable. This outcome is different from an operational 503, which is not cached as a business decision.

The same idempotency key and canonical request returns the original stored decision, even after a cutover. Reusing the key with changed input returns 409 idempotency_conflict. After remediation, use a new key and optionally pass the tenant-owned supersedes_decision_id. Retry a transient 503 with the same key; a lost response does not prove the database rolled back.

{
  "decision_id": "7c0f4165-fb85-47c6-9594-2e623535ada9",
  "schema_version": "governed-calculation/v2",
  "status": "partial",
  "components": [
    {
      "kind": "sales_vat",
      "treatment": "taxable",
      "treatment_status": "resolved",
      "amount_status": "calculated",
      "jurisdiction": "DE",
      "liable_party": "merchant",
      "collection_point": "checkout",
      "rate_category": "reduced",
      "vat_rate_bps": 700,
      "amount_minor": 700,
      "currency": "EUR",
      "calculation_id": "ab9c7869-84a4-4cce-93dc-f8ae1c5f8362",
      "legal_basis": "EU TEDB VAT rate evidence",
      "provenance": ["treatment-release:…", "rule:…"],
      "deduction_status": "unknown",
      "findings": []
    },
    {
      "kind": "import_vat",
      "treatment": null,
      "treatment_status": "unsupported",
      "amount_status": "unsupported",
      "rate_category": null,
      "vat_rate_bps": null,
      "amount_minor": null,
      "currency": "EUR",
      "provenance": [],
      "deduction_status": "unknown",
      "findings": ["unsupported_scope"]
    }
  ],
  "legal_at": "2026-06-01T00:00:00Z",
  "knowledge_at": "2026-09-06T20:00:00Z",
  "resolver_version": "governed-treatment/v2",
  "findings": []
}

The real response always contains exactly four components; the shortened example shows two. 200 and partial do not grant filing permission. Filing consumers must require the exact obligation, amount, liability, collection point, registration, route, and current-authority evidence they need. 409 decisions retain component conflicts; 422 decisions retain incomplete or unsupported findings with a durable decision ID.

Aggregate statusMeaning
completeEvery obligation has a calculated amount, an evidence-proven zero, or an evidence-proven not_applicable outcome.
partialAt least one obligation is complete and no component conflicts, while another remains incomplete or unsupported.
incompleteNo obligation is complete and at least one requires additional supported facts or authority.
conflictingAt least one component contains mutually inconsistent evidence or selectors.
unsupportedNo obligation is complete and the required legal flow or authority is not implemented.

The initial governed release builder publishes sales-VAT rules only. Import VAT, customs duty, and fees therefore remain explicit unsupported components, so current deployments normally return partial, incomplete, conflicting, or unsupported. The complete contract is reserved for a future reviewed release that establishes all four obligations; VAT Engine does not fabricate a complete example from missing authority.