New France e-invoicing mandate goes live September 2026 — our implementation is ready. See all mandates →
Tax Calculations

Tax Calculations

The Tax Calculation API determines the correct tax amount for any transaction — VAT, GST, sales tax, and other indirect taxes — across 100+ countries in a single API call. You send a seller, a customer, and a list of line items; Clearvo resolves the applicable jurisdiction, classifies each product, applies the correct rate band, and returns a fully broken-down tax result.

Key capabilities

  • 100+ countries — EU multi-band VAT (standard, reduced, second-reduced, super-reduced), US sales tax (all 50 states, 13k+ jurisdictions), Canadian GST/HST/PST, and more
  • AI-powered product classification — describe a product in plain language; no HS codes or custom slug mapping required
  • Jurisdiction resolution — determines where tax is owed based on shipping address, billing address, IP geolocation, or card BIN country (2-of-3 rule for B2C digital goods)
  • B2B reverse charge and EU IOSS — applies the correct treatment automatically based on seller and buyer tax IDs
  • EN16931 tax codes — output maps directly to Clearvo e-invoicing fields with no additional transformation
  • Flexible input — send country names, ISO alpha-3 codes, currency names, US state names, or informal customer type labels; all normalised server-side before validation
  • Graceful degradation — never errors on rate-service failures; returns a result with degraded: true and a zero rate
  • Ephemeral and committed calculations — calculate without recording (for quoting), or commit the result to your audit trail
Tax Calculation uses the same base URL as the core API (https://api.clearvo.io/v1) and the same csk_live_* / csk_test_* key system. No separate credentials are needed — the same key you use for e-invoicing and TIN validation works here.
Tax Calculations

Authentication

All Tax Calculation endpoints require an x-api-key header. Both entity-scoped and account-scoped keys are accepted on all endpoints. Account-scoped keys must include an X-Entity-Id header on every request to identify the target entity — entity-scoped keys do not require this header.

http
x-api-key: csk_live_...

Use a csk_test_* key for sandbox requests — rate lookups and jurisdiction resolution run normally, but no external rate-authority calls are made and results are not billed. See the Sandbox section for details.

Tax Calculations

Quickstart

The simplest possible calculation: a B2C digital product sold to a customer in Germany. Clearvo classifies the product, resolves the jurisdiction, and returns the German VAT rate (19%) with an EN16931 tax code ready for e-invoicing.

curl
curl https://api.clearvo.io/v1/tax/calculate \
  -X POST \
  -H "x-api-key: csk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "currency": "EUR",
    "seller": {
      "country": "IE",
      "taxId": "IE1234567T"
    },
    "customer": {
      "address": { "country": "DE" }
    },
    "lineItems": [
      {
        "id": "line-1",
        "amount": 100.00,
        "productName": "Annual SaaS subscription"
      }
    ]
  }'
JSON Response
{
  "calculationId": "cl_calc_01j4...",
  "committed": false,
  "sandbox": false,
  "degraded": false,
  "currency": "EUR",
  "jurisdiction": {
    "country": "DE",
    "region": null,
    "method": "BILLING_ADDRESS",
    "precision": "COUNTRY",
    "conflicts": []
  },
  "treatment": {
    "taxTreatment": "STANDARD",
    "taxCode": "S",
    "rate": 0.19,
    "rateBand": "STANDARD"
  },
  "lineItems": [
    {
      "id": "line-1",
      "taxCode": "S",
      "rate": 0.19,
      "taxableAmount": 100.00,
      "taxAmount": 19.00,
      "totalAmount": 119.00,
      "rateBand": "STANDARD",
      "classification": {
        "slug": "digital-services",
        "confidence": 0.97,
        "status": "APPROVED",
        "fromCache": false
      }
    }
  ],
  "totals": {
    "subtotal": 100.00,
    "totalTax": 19.00,
    "total": 119.00
  },
  "rateSource": {
    "provider": "CLEARVO",
    "asOf": "2026-06-21",
    "cacheHit": true
  }
}

The taxCode: "S" and rate: 0.19 on each line item flow directly into a Clearvo e-invoice submission — no mapping required. See E-Invoicing Integration.

Tax Calculations

Input Formats

Clearvo normalises incoming field values server-side before validation runs. You can send data in the format your billing system or platform already produces — no pre-processing or mapping layer needed on your end.

Countries

The country field on any address accepts ISO 3166-1 alpha-2, alpha-3, or an English country name. All forms are resolved to alpha-2 internally.

What you sendResolved toNotes
"DE""DE"Already canonical — passed through unchanged
"DEU""DE"ISO 3166-1 alpha-3
"GBR""GB"As returned by Apple StoreKit storefront
"USA""US"
"Germany""DE"English country name
"United States""US"
"Netherlands" / "Holland""NL"Common informal names are also recognised
"Czechia" / "Czech Republic""CZ"

Currencies

The currency and reportingCurrency fields accept ISO 4217 codes or English currency names.

What you sendResolved to
"EUR" / "eur""EUR"
"Euro" / "euros""EUR"
"US Dollar" / "Dollar""USD"
"British Pound" / "Pound Sterling""GBP"
"Japanese Yen""JPY"
"Canadian Dollar""CAD"

Regions (US states and Canadian provinces)

The region field on US and Canadian addresses accepts full state or province names as well as the standard 2-letter codes.

What you sendResolved toNotes
"California""CA"As returned by Google Play buyerState
"New York""NY"
"District of Columbia" / "Washington DC""DC"
"British Columbia""BC"
"Ontario""ON"
"Québec" / "Quebec""QC"Accented forms accepted

B2B override

By default, Clearvo infers B2B treatment from a valid customer.taxId — no additional flag is needed for standard B2B checkouts. For cases where you know the buyer is a business but don't have their VAT number at checkout time, set customer.b2bOverride: true.

ScenarioWhat to send
B2C consumer saleOmit taxId and b2bOverride — B2C treatment is the default
B2B sale, VAT number knownSupply customer.taxId — Clearvo verifies it and applies reverse charge or zero-rating automatically
B2B sale, VAT number not availableSet customer.b2bOverride: true — skips VAT number validation and applies full B2B treatment for the buyer's location

Platform integration examples

These patterns work without any field transformation on your side.

Google Play — buyerCountry + buyerState direct
// Google Play Orders API returns buyerCountry (alpha-2) + buyerState (full name)
{
  "currency": "USD",
  "customer": {
    "type": "consumer",
    "billingAddress": {
      "country": "US",
      "region": "California"
    }
  },
  "lineItems": [{ "id": "1", "amount": 9.99, "productName": "App subscription" }]
}
// "consumer" → B2C, "California" → "CA" — both resolved before validation
Apple StoreKit — alpha-3 storefront code direct
// Apple StoreKit 2 returns storefront as ISO 3166-1 alpha-3
{
  "currency": "USD",
  "customer": {
    "billingAddress": { "country": "GBR" }
  },
  "lineItems": [{ "id": "1", "amount": 4.99, "productName": "In-app purchase" }]
}
// "GBR" → "GB" — UK VAT (20%) applied
Tax Calculations

Calculate

Calculates tax for a single transaction. By default the result is ephemeral — not recorded in your account. Set "commit": true to persist it to your audit trail and expose it in the dashboard.

Endpoint

POST https://api.clearvo.io/v1/tax/calculate

Request body

curl
curl https://api.clearvo.io/v1/tax/calculate \
  -X POST \
  -H "x-api-key: csk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "currency": "EUR",
    "commit": true,
    "idempotencyKey": "order-abc-123",
    "seller": {
      "country": "IE",
      "taxId": "IE1234567T"
    },
    "customer": {
      "taxId": "DE987654321",
      "address": {
        "country": "DE",
        "postalCode": "10115"
      }
    },
    "lineItems": [
      {
        "id": "line-1",
        "amount": 500.00,
        "quantity": 5,
        "productName": "Enterprise software licence",
        "taxCategory": "digital-services"
      },
      {
        "id": "line-2",
        "amount": 120.00,
        "quantity": 2,
        "productName": "Printed user manual"
      }
    ],
    "vatUnverifiableFallback": "conservative"
  }'

Request fields

FieldTypeRequiredDescription
currencystringYesISO 4217 currency code for all amounts in the request (e.g. "EUR", "USD", "GBP").
commitbooleanNoDefault: false. When true, the calculation is persisted to your account's audit trail and appears in the dashboard. Committed calculations are billed. Ephemeral calculations are not recorded and are only intended for quoting.
idempotencyKeystringNoA unique key you provide (e.g. your order ID). If a committed calculation already exists for this key under your account, the original result is returned and no new calculation is performed. Ignored for ephemeral calculations (commit: false).
seller.countrystringYesISO 3166-1 alpha-2 country code of the seller's registered address.
seller.taxIdstringNoSeller's VAT number or tax ID. Used to determine whether reverse charge or IOSS rules apply.
seller.iossNumberstringNoSeller's IOSS (Import One-Stop Shop) registration number. When present, IOSS treatment is applied to qualifying B2C cross-border goods shipments (≤€150) instead of standard import VAT.
customer.b2bOverridebooleanNoWhen true, applies B2B treatment immediately without requiring a VAT number. Use when the buyer is a known business but their VAT ID is not available at the time of the request. Omit for all normal B2C and B2B-with-taxId flows.
customer.taxIdstringNoCustomer's VAT number. When supplied, Clearvo verifies it against VIES (with a 300ms timeout) and applies B2B treatment — reverse charge, intra-community zero-rating, or export zero-rating — based on the seller and buyer locations.
customer.address.countrystringYesISO 3166-1 alpha-2 country code of the customer's address. This is the primary jurisdiction signal for physical goods and B2B transactions.
customer.address.regionstringYes (US)State code (e.g. "CA" for California, "TX" for Texas). Required for US (country: "US") — no country-level US rate exists. For Canada, optional; supply the province code (e.g. "ON", "AB") for accurate provincial rate lookup; country-level GST (5%) is applied when region is omitted.
customer.address.postalCodestringNoPostal code. Used for special territory detection — for example, Canary Islands postal codes (35xxx–38xxx) are treated as outside the EU VAT area despite using an ES country code.
customer.ipAddressstringNoCustomer's IP address. Used as a secondary jurisdiction signal for B2C digital services under the EU 2-of-3 rule.
customer.binCountrystringNoISO country code derived from the customer's card BIN. Used as a third jurisdiction signal for B2C digital services under the EU 2-of-3 rule.
lineItems[].idstringYesYour identifier for this line item. Echoed back in the response — use it to match results to your order lines.
lineItems[].amountnumberYesLine item amount in the transaction currency. Whether this is tax-exclusive or tax-inclusive depends on the seller's pricingModel configured on their entity.
lineItems[].quantitynumberNoDefault: 1. The quantity of units. The amount field should represent the total line amount (not the per-unit price), so quantity is informational only and does not affect the tax calculation.
lineItems[].productNamestringYesPlain-language product name or description. Clearvo uses this to classify the product and select the correct VAT rate band (e.g. digital services vs printed books). No HS codes required.
lineItems[].taxCategorystringNoExplicit product category slug to override AI classification (e.g. "digital-services", "books", "food"). Use when you know the correct category and want deterministic results. See Product Classification for available slugs.
vatUnverifiableFallback"conservative" | "permissive"NoDefault: "conservative". Controls behaviour when VIES cannot verify a customer's VAT ID within the timeout. "conservative" treats the customer as B2C (applies standard local rate). "permissive" applies B2B reverse charge rules anyway and marks the result as degraded: true.
vatValidation"full" | "format" | "none"NoDefault: "full". Controls how a B2B customer's VAT ID is validated. "full" — live VIES lookup with a 300ms timeout (default; most accurate, adds ~100–300ms latency). "format" — format check only, no network call; a structurally valid ID is trusted without a degraded flag. "none" — no validation at all; the VAT ID is trusted entirely. Use "format" or "none" when VIES latency is a concern or you are processing at high volume. Can also be set at account level via Account Settings.
transactionType"invoice" | "credit_note"NoDefault: "invoice". Set to "credit_note" when issuing a credit note against a prior invoice (e.g. from SAP or NetSuite). The engine negates all line item amounts before calculation — the same jurisdiction, treatment, and rate logic applies, but all resulting amounts are negative. See Credit Notes & Refunds.
relatedCalculationIdstringConditionally requiredThe calculationId of the original invoice being reversed. Required when transactionType is "credit_note" and commit is true. The referenced calculation must belong to the same entity. Maximum 50 characters.

Successful response

See the Quickstart for a full response example. For the complete field reference, see Response Reference.

Error responses

422 vs 400. Clearvo uses 422 Unprocessable Entity for request bodies that are valid JSON but fail field-level validation (missing fields, wrong types, constraint violations). The error field in the response body identifies the failing field and reason — e.g. "customer: Required" or "merchantRef: String must contain at most 100 character(s)". Fix the field named in error to resolve it. 400 Bad Request is reserved for scope-level errors (e.g. using an account-scoped key on an entity-only endpoint).
HTTPWhen it occursHow to fix
422Missing required field (customer, lineItems, currency, or a line item id / amount)Check the error field — it names the failing path (e.g. "customer: Required"). Add the missing field.
422currency is not exactly 3 characters, or lineItems is an empty arraySupply a valid ISO 4217 currency code (e.g. "EUR") and at least one line item.
422merchantRef exceeds 100 charactersTruncate your reference to 100 characters or fewer.
422customer.billingAddress.region (or shippingAddress.region) is missing when country is "US"Supply the state code (e.g. "CA", "TX"). No country-level US tax rate exists — state is mandatory for all US addresses.
400An account-scoped key was used without an X-Entity-Id headerAdd X-Entity-Id: <entityId> to identify the target entity, or use an entity-scoped key.
403The entity in X-Entity-Id does not belong to this accountCheck that the entity ID belongs to the account associated with this API key.
401The x-api-key header is missing or the key is unrecognisedInclude a valid x-api-key header on every request.
409A committed calculation already exists for this idempotencyKey with a different request bodyEach idempotencyKey is permanently bound to the first request body that used it. Use a fresh key for a different transaction.
422transactionType is "credit_note", commit is true, but relatedCalculationId is absentSupply the calculationId of the original invoice in relatedCalculationId.
422relatedCalculationId is provided but the referenced calculation does not exist or belongs to a different entityVerify the ID — it must be a committed calculation for this entity. Ephemeral (uncommitted) calculations are not valid targets.
Tax Calculations

Credit Notes & Refunds

Clearvo supports two distinct patterns for reversing a committed transaction, chosen based on how your billing or ERP system models reversals.

Credit notes (B2B / ERP path)

ERP systems such as SAP, NetSuite, and Oracle issue a formal credit note document when a sale is reversed. Submit this as a new calculation with transactionType: "credit_note". Clearvo negates all line item amounts before the calculation pipeline runs — the same jurisdiction, treatment, and rate logic applies unchanged, but every amount comes out negative. This keeps the credit note fully audit-traceable as its own document with its own calculationId.

curl — credit note
curl https://api.clearvo.io/v1/tax/calculate \
  -X POST \
  -H "x-api-key: csk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "currency": "EUR",
    "commit": true,
    "transactionType": "credit_note",
    "relatedCalculationId": "cl_calc_01j4...",
    "seller": { "country": "IE", "taxId": "IE1234567T" },
    "customer": {
      "taxId": "DE987654321",
      "address": { "country": "DE" }
    },
    "lineItems": [
      {
        "id": "line-1",
        "amount": 500.00,
        "productName": "Enterprise software licence"
      }
    ]
  }'

The response is identical in shape to a regular calculation — all amounts are negative (e.g. taxableAmount: -500.00, taxAmount: -95.00). The response includes transactionType: "credit_note" and relatedCalculationId.

relatedCalculationId is required when committing a credit note. It must reference a previously committed invoice calculation belonging to the same entity. Ephemeral (uncommitted) calculations cannot be referenced. When commit: false, the field is optional — useful for quoting the credit note amount before committing it.

Refunds (B2C / billing system path)

B2C billing systems such as Stripe, Chargebee, and Recurly process a payment refund without issuing a separate credit note document. Use the refund endpoint to mark the original calculation as refunded.

Endpoint

POST https://api.clearvo.io/v1/tax/calculate/{id}/refund

curl — refund
curl https://api.clearvo.io/v1/tax/calculate/cl_calc_01j4.../refund \
  -X POST \
  -H "x-api-key: csk_live_..."
JSON Response
{
  "ok": true,
  "calculationId": "cl_calc_01j4...",
  "refundedAt": "2026-06-24T10:30:00.000Z"
}

Error responses

HTTPWhen it occursHow to fix
404The calculation does not exist or belongs to a different entityCheck that the ID is correct and the API key is for the entity that owns this calculation.
409The calculation has already been marked as refundedNo action needed — the refund was already processed. The response body includes the original refundedAt timestamp.
422The calculation is a sandbox calculationSandbox calculations cannot be marked as refunded — they are not billed and do not affect compliance thresholds.
422The calculation is a credit note (not an invoice)Credit notes cannot be refunded. If the credit note was issued in error, issue a correcting invoice instead.

Compliance threshold impact

Both patterns exclude the reversed amount from your compliance obligation totals immediately:

  • Credit notes — the negative total_amount reduces the threshold SUM automatically, because the SUM aggregates all committed amounts for the entity and country.
  • Refunds — the refunded calculation is excluded from the threshold SUM as soon as refunded_at is set. The Compliance Radar reflects this in real time.
Tax Calculations

Batch Calculation

Calculate tax for up to 50 transactions in one request. Each item in the batch is processed independently and in parallel — an error on one item does not affect the others. Per-item errors are returned inline at the item's index.

Endpoint

POST https://api.clearvo.io/v1/tax/calculate/batch

curl
curl https://api.clearvo.io/v1/tax/calculate/batch \
  -X POST \
  -H "x-api-key: csk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "items": [
      {
        "currency": "EUR",
        "seller": { "country": "IE", "taxId": "IE1234567T" },
        "customer": { "address": { "country": "DE" } },
        "lineItems": [{ "id": "l1", "amount": 100.00, "productName": "SaaS subscription" }]
      },
      {
        "currency": "GBP",
        "seller": { "country": "GB", "taxId": "GB123456789" },
        "customer": { "address": { "country": "GB" } },
        "lineItems": [{ "id": "l1", "amount": 49.99, "productName": "Printed guidebook" }]
      }
    ]
  }'
JSON Response
{
  "results": [
    {
      "index": 0,
      "calculationId": "cl_calc_01j4...",
      "totals": { "subtotal": 100.00, "totalTax": 19.00, "total": 119.00 },
      /* … full TaxCalculateResponse … */
    },
    {
      "index": 1,
      "error": "invalid_country: 'ZZ' is not a valid ISO country code"
    }
  ],
  "processedAt": "2026-06-21T12:00:00.000Z"
}

Each result includes an index matching its position in the items array. Successful items contain the full TaxCalculateResponse shape. Items that failed contain only index and error. The top-level HTTP status is always 200 if the batch request itself was valid — check each item's error field individually.

The batch endpoint is limited to 50 items per call. For larger volumes, split into multiple requests. All items in a batch are processed in parallel, but AI product classification calls share a per-account rate limit — using explicit taxCategory slugs on line items eliminates this bottleneck.
Tax Calculations

Exemptions

Store tax exemption records for specific customers. Use these endpoints to log that a customer holds a valid exemption certificate for a jurisdiction and product category. To apply an exemption in a calculation, pass "taxCode": "E" on the relevant line items — the engine processes it as exempt. Automated certificate matching during calculation is on the roadmap.

Endpoints

MethodPathDescription
GET/v1/tax/exemptionsList all exemptions. Filter by country, customerTaxId, or taxCategorySlug.
POST/v1/tax/exemptionsCreate a new exemption.
GET/v1/tax/exemptions/{id}Retrieve a single exemption.
DELETE/v1/tax/exemptions/{id}Delete an exemption (e.g. when a certificate expires).
curl — create exemption
curl https://api.clearvo.io/v1/tax/exemptions \
  -X POST \
  -H "x-api-key: csk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "country": "US",
    "region": "TX",
    "customerTaxId": "12-3456789",
    "taxCategorySlug": "software",
    "reason": "Non-profit exemption certificate",
    "certificateRef": "TX-NPO-2026-00123",
    "validFrom": "2026-01-01",
    "validTo": "2026-12-31"
  }'

Exemption fields

FieldTypeRequiredDescription
countrystringYesISO country code the exemption applies to.
regionstringNoState or province code. Leave null for a country-wide exemption.
customerTaxIdstringYesThe customer's tax ID that this exemption is issued to.
taxCategorySlugstringNoLimit the exemption to a specific product category. If null, the exemption applies to all categories in the jurisdiction.
reasonstringNoHuman-readable description for your records (e.g. "Non-profit exemption certificate").
certificateRefstringNoYour reference number for the exemption certificate. Stored for audit purposes.
validFromstringYesYYYY-MM-DD. Date from which the exemption is valid.
validTostringNoYYYY-MM-DD. Expiry date. If omitted, the exemption does not expire.
Tax Calculations

Tax Obligations

Track where your entity has a tax registration obligation — based on economic nexus thresholds, physical presence, or a manual override. The calculation engine uses obligation records to determine whether to apply seller-not-registered treatment.

Endpoints

MethodPathDescription
GET/v1/tax/obligationsList all obligations. Filter by country, registrationStatus, or obligationStatus.
GET/v1/tax/obligations/{id}Retrieve a single obligation.
PATCH/v1/tax/obligations/{id}Update registration status, registration number, or obligation status.
curl — update obligation
curl https://api.clearvo.io/v1/tax/obligations/oblig_01j4... \
  -X PATCH \
  -H "x-api-key: csk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "registrationStatus": "REGISTERED",
    "registrationNumber": "DE987654321",
    "obligationStatus": "COMPLIANT"
  }'

Obligation fields

FieldTypeDescription
countrystringISO country code this obligation applies to.
regionstring | nullState or province. Null for country-level obligations.
registrationStatus"REGISTERED" | "PENDING" | "NOT_REGISTERED"Current registration status with the tax authority in this jurisdiction. Controls whether the engine applies local tax rates or seller-not-registered treatment.
registrationNumberstring | nullThe tax registration number issued by the authority. Set when registrationStatus is REGISTERED.
obligationStatus"COMPLIANT" | "MONITORING" | "ACTION_REQUIRED"Overall compliance posture. ACTION_REQUIRED surfaces in the dashboard as a priority item.
thresholdAmountnumber | nullThe economic nexus threshold for this jurisdiction (in local currency). Informational only — Clearvo does not automatically track sales against this threshold.
currentPeriodAmountnumber | nullYour current period sales amount in this jurisdiction. Update this via PATCH to keep the obligation status current.
estimatedExposurenumber | nullEstimated tax exposure if registered (computed field — not updateable directly).
Tax Calculations

Product Catalogue

Every product that passes through /v1/tax/calculate is classified by AI and cached per account. The Product Catalogue endpoints let you pre-classify your full product catalogue, inspect AI-assigned slugs, approve or correct them, and bulk-load classifications from an ERP export — all before go-live, so the first real transaction is never slowed by an AI round-trip.

Endpoints

MethodPathDescription
GET/v1/tax/productsList classified products. Filter by status (APPROVED | NEEDS_REVIEW), entityId (account-scoped keys only), page, limit (max 100).
POST/v1/tax/productsClassify a single product. Providing taxCategory bypasses AI and saves immediately as APPROVED.
PATCH/v1/tax/products/{id}Approve or correct a classification. Accepts slug and status (APPROVED | NEEDS_REVIEW | MANUAL).
DELETE/v1/tax/products/{id}Soft-delete a product. Sets deleted_at and hides it from all reads — classification history is preserved. Use the restore endpoint to reactivate.
POST/v1/tax/products/{id}/restoreReactivate a soft-deleted product. Body: { "reason": "string" } (required — logged to audit trail).
POST/v1/tax/products/bulkClassify up to 500 products in one call. Accepts text/csv or application/json.
curl — classify a product
curl https://api.clearvo.io/v1/tax/products \
  -X POST \
  -H "x-api-key: csk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "productCode": "PLAN-BIZ-ANNUAL",
    "productName": "Business plan — annual subscription",
    "productDescription": "Cloud software subscription, B2B"
  }'
curl — bulk classify (CSV)
curl https://api.clearvo.io/v1/tax/products/bulk \
  -X POST \
  -H "x-api-key: csk_live_..." \
  -H "Content-Type: text/csv" \
  --data-binary 'product_code,product_name,product_description,tax_category
PLAN-BIZ,Business plan,,saas_business
EBOOK-01,EU Tax Guide 2026,Comprehensive EU VAT reference,
CONSULT-HR,HR transformation workshop,Half-day onsite session,'

Classification fields

FieldTypeDescription
productCodestringYour SKU or ERP code. Used as the cache key — the same product is classified at most once per account.
productNamestringPlain-language name fed to the AI classifier. Required unless productCode is supplied.
productDescriptionstringAdditional context for the classifier. Include when the product name alone is ambiguous.
taxCategorystringExplicit slug override (e.g. "saas_business", "ebooks"). Bypasses AI — confidence is set to 1.0 and status is APPROVED immediately.
statusAPPROVED | NEEDS_REVIEWAI confidence ≥ 0.85 → APPROVED automatically. Below 0.85 → NEEDS_REVIEW until approved via PATCH or the dashboard.
Tax Calculations

Tax Categories

The Tax Categories endpoint returns the complete, live taxonomy of product category slugs — the same set the engine uses to classify line items. Call this endpoint to discover valid values for taxCategory on calculate requests and defaultTaxCategorySlug in account settings.

Slugs are an open string taxonomy — new categories are added non-breakingly as the engine is updated. Fetch this endpoint rather than hardcoding slug strings to ensure your integration handles new additions gracefully.

Endpoint

GET https://api.clearvo.io/v1/tax/categories

Accepts both entity-scoped and account-scoped API keys.

curl
curl https://api.clearvo.io/v1/tax/categories \
  -H "x-api-key: csk_live_..."
JSON Response
{
  "object": "list",
  "count": 48,
  "categories": [
    {
      "slug": "api_data_services",
      "name": "API Access / Data Feeds / Integrations",
      "defaultTaxCode": "S",
      "eligibleSchemes": ["STANDARD", "OSS_UNION", "OSS_NON_UNION", "VOEC"]
    },
    {
      "slug": "ebooks",
      "name": "eBooks / Digital Publications",
      "defaultTaxCode": "AA",
      "eligibleSchemes": ["STANDARD", "OSS_UNION", "OSS_NON_UNION", "VOEC"]
    },
    {
      "slug": "saas_business",
      "name": "SaaS / Cloud Software (Business Use)",
      "defaultTaxCode": "S",
      "eligibleSchemes": ["STANDARD", "OSS_UNION", "OSS_NON_UNION", "VOEC"]
    },
    "..."
  ]
}

Response fields

FieldTypeDescription
slugstringThe category identifier to use in taxCategory on line items, or as defaultTaxCategorySlug in account settings.
namestringHuman-readable description of the category.
defaultTaxCodestringThe EN16931 tax code most commonly applied to this category (S = standard, AA = reduced, E = exempt). The actual code used in a calculation may differ depending on country-specific rules.
eligibleSchemesstring[]OSS/IOSS schemes this category qualifies for: STANDARD, OSS_UNION, OSS_NON_UNION, IOSS, VOEC. Physical goods include IOSS; location-specific services include STANDARD only.
Tax Calculations

Registrations

Registrations tell the calculation engine where your entity is authorised to collect tax. A jurisdiction with no registration record defaults to REGISTERED (tax collected). Set a jurisdiction to NOT_REGISTERED and the engine returns tax code O (outside scope, 0%) for all transactions there. Adding an IOSS number enables IOSS treatment for qualifying EU B2C shipments ≤ €150.

Endpoints

MethodPathDescription
GET/v1/tax/registrationsList registrations for the entity. Account-scoped keys must supply ?entityId=. Returns collectionStatus, collectFromDate, and canCollectTax on each registration when Tax Calculations is enabled.
POST/v1/tax/registrationsCreate a registration. Account-scoped keys must include X-Entity-Id.
PATCH/v1/tax/registrations/{id}Set the collection start date for a registration. Pass collectFromDate: null to collect immediately, or an ISO date string to defer. Returns the updated collectionStatus.
DELETE/v1/tax/registrations/{id}Delete a tax number registration. Account-scoped keys must include X-Entity-Id.

Collection status

When Tax Calculations is enabled for your account, each registration carries a collectionStatus that controls whether Clearvo applies tax for that jurisdiction.

StatusMeaning
COLLECTINGTax is being collected — collectFromDate is set to today or a past date.
DEFERREDCollection starts on a future collectFromDate. No tax collected until that date.
SETUP_NEEDEDTax Calculations is enabled but no collectFromDate has been set. Set one via PATCH /v1/tax/registrations/{id}.
nullTax Calculations is not enabled for this account.

The list response also includes two top-level flags: taxCalcEnabled (boolean — whether the product is active) and taxCalcSetupRequired (boolean — true when any registration is in SETUP_NEEDED state).

curl — add a VAT registration
curl https://api.clearvo.io/v1/tax/registrations \
  -X POST \
  -H "x-api-key: csk_live_ent_..." \
  -H "Content-Type: application/json" \
  -d '{
    "type": "VAT",
    "country": "DE",
    "taxNumber": "DE123456789"
  }'
curl — set collection date
# Start collecting immediately
curl https://api.clearvo.io/v1/tax/registrations/<id> \
  -X PATCH \
  -H "x-api-key: csk_live_ent_..." \
  -H "Content-Type: application/json" \
  -d '{ "collectFromDate": null }'

# Defer to a future date
curl https://api.clearvo.io/v1/tax/registrations/<id> \
  -X PATCH \
  -H "x-api-key: csk_live_ent_..." \
  -H "Content-Type: application/json" \
  -d '{ "collectFromDate": "2026-07-01" }'

Registration types

TypeDescriptionEngine effect
VATStandard national VAT registrationSets obligation to REGISTERED; tax collected at local rate
IOSSEU Import One-Stop Shop numberWrites iossNumber to the entity master; enables IOSS treatment for eligible B2C goods ≤ €150 shipped into the EU
UNION_OSSEU One-Stop Shop (Union scheme)Sets obligation to REGISTERED with OSS scheme flag
NON_UNION_OSSEU One-Stop Shop (Non-Union scheme)Sets obligation to REGISTERED with OSS scheme flag
Tax Calculations

Account Settings

Account-level defaults that apply to every calculation made with your API key. All three settings can be overridden per-request — the cascade is: request field → product cache → account setting → hardcoded default.

Endpoints

MethodPathDescription
GET/v1/tax/settingsRead current account settings. Returns defaults if no settings row has been written yet.
PATCH/v1/tax/settingsUpdate one or more settings. Partial updates — omitted fields are unchanged.
curl — update settings
curl https://api.clearvo.io/v1/tax/settings \
  -X PATCH \
  -H "x-api-key: csk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "vatValidationMode": "format",
    "vatUnverifiableTreatment": "consumer",
    "defaultPriceIncludesTax": false,
    "defaultTaxCategorySlug": "saas_business"
  }'

Settings fields

FieldTypeDefaultDescription
vatValidationMode"full" | "format" | "none""full"Account-level default for VAT ID validation. Overridden per-request by the vatValidation request field. "full" — live VIES lookup (default). "format" — format check only, no VIES call. "none" — no validation; all VAT IDs trusted.
vatUnverifiableTreatment"consumer" | "business""consumer"How to treat B2B transactions when the customer's VAT ID cannot be verified (only applies when vatValidationMode is "full"). "consumer" charges tax at B2C rates (conservative). "business" applies reverse charge and sets degraded: true.
defaultPriceIncludesTaxbooleanfalseWhether line item amounts are tax-inclusive by default. Can be overridden per line item via amountIncludesTax.
defaultTaxCategorySlugstring | nullnullFallback product category slug when AI classification fails. If null, the engine applies the standard rate.
availableTaxCategoriesarrayRead-only. Full list of active category slugs and names available in your account (GET only).
Tax Calculations

Response Reference

Full field reference for the TaxCalculateResponse object returned by both the single and batch calculation endpoints.

Top-level fields

FieldTypeDescription
calculationIdstringUnique ID for this calculation (cl_calc_* prefix). Present on both ephemeral and committed calculations.
entityIdstringThe entity (fiscal identity) that processed this calculation. Confirms which entity-scoped API key was used — useful for Merchant of Record setups where multiple entities share an account.
committedbooleanWhether this calculation was persisted to your audit trail.
sandboxbooleanTrue when the request was made with a csk_test_* key.
degradedbooleanTrue when the engine encountered a partial failure (e.g. rate service timeout, VIES unreachable). The calculation still succeeds but uses a fallback rate of 0%.
degradedReasonstring | nullHuman-readable explanation of why the result is degraded. Only present when degraded: true.
currencystringISO 4217 currency code, echoed from the request.
transactionTypestring"invoice" or "credit_note" — echoed from the request (defaults to "invoice"). Credit note responses have negative amounts on all line items and totals.
relatedCalculationIdstring | nullThe calculationId of the original invoice this credit note reverses. Only present when transactionType is "credit_note" and the field was provided in the request.
refundedAtstring | nullISO 8601 timestamp. Set when POST /v1/tax/calculate/{id}/refund has been called. null for non-refunded calculations.

jurisdiction

FieldTypeDescription
jurisdiction.countrystringISO country code of the resolved tax jurisdiction.
jurisdiction.regionstring | nullState or province of the resolved jurisdiction. Non-null for US/Canadian transactions where sub-national rates apply.
jurisdiction.methodstringHow the jurisdiction was determined: SHIPPING_ADDRESS, BILLING_ADDRESS, IP_GEOLOCATION, BIN_COUNTRY, or VAT_ID.
jurisdiction.precisionstringConfidence in the resolved jurisdiction: EXACT, POSTAL, REGION, or COUNTRY.
jurisdiction.addressPrecisionstring | undefinedUS transactions only. The address resolution tier used to look up the CCH rate: ROOFTOP (street address resolved to a zip+4 code), ZIP (postal code only), or STATE (region only). Not present for non-US transactions.
jurisdiction.conflictsarrayNon-empty when multiple jurisdiction signals disagree (e.g. billing address says DE, IP geolocation says FR). Each entry describes the conflicting signal and its country. The winning signal (used for tax) is indicated by method.
Rate format — decimal fractions throughout. Every rate, standardRate, reducedRate, and slugRate field across all Tax Calculation endpoints uses a decimal fraction: 0.19 means 19%, 0.07 means 7%, 0 means zero-rated. Never multiply or divide rates received from the API — use them directly in tax calculations (taxAmount = taxableAmount × rate).

treatment

FieldTypeDescription
treatment.taxTreatmentstringThe tax treatment applied. See Tax Treatments for all possible values and their meanings.
treatment.taxCodestringEN16931 tax code: S, AA, AE, E, K, G, Z, or O. Use this directly on invoice line items when submitting via the e-invoicing API.
treatment.ratenumberEffective tax rate as a decimal fraction (0.19 = 19%, 0.07 = 7%). Zero for exempt, reverse-charge, and out-of-scope treatments.
treatment.rateBandstringRate band selected: STANDARD, REDUCED, SECOND_REDUCED, SUPER_REDUCED, ZERO, or EXEMPT.

lineItems[]

FieldTypeDescription
idstringYour line item ID, echoed from the request.
taxCodestringEN16931 tax code for this line item.
ratenumberEffective tax rate for this line item as a decimal fraction (0.19 = 19%, 0 = zero-rated).
taxableAmountnumberAmount on which tax is calculated (always tax-exclusive, regardless of the seller's pricing model).
taxAmountnumberTax amount for this line item (taxableAmount × rate).
totalAmountnumberTotal including tax (taxableAmount + taxAmount).
rateBandstringRate band applied to this specific line item (may differ across lines if products fall into different bands).
classification.slugstringProduct category slug resolved for this line item.
classification.confidencenumberAI classification confidence score (0–1). 1.0 when an explicit taxCategory was provided.
classification.statusstringAPPROVED — used with confidence; NEEDS_REVIEW — low confidence, recommend manual override; MANUAL — explicit taxCategory supplied; PENDING — classification failed, fallback used.
classification.fromCachebooleanTrue if the classification came from a prior result cached for this product name.

totals

FieldTypeDescription
totals.subtotalnumberSum of all taxableAmount values across line items.
totals.totalTaxnumberSum of all taxAmount values across line items.
totals.totalnumberSum of all totalAmount values across line items (subtotal + totalTax).

ioss (when applicable)

FieldTypeDescription
ioss.numberstringThe IOSS registration number used for this transaction.
ioss.registrationCountrystringThe EU member state where the IOSS number is registered.
ioss.totalGoodsValuenumberNominal total value of goods in the transaction currency (used to determine the ≤€150 threshold eligibility).
ioss.currencystringCurrency of the totalGoodsValue.

rateSource

FieldTypeDescription
rateSource.providerstring"CLEARVO" for all non-US transactions. "CCH" for US transactions, where rate data comes from the CCH (Wolters Kluwer) sales tax database.
rateSource.asOfstringYYYY-MM-DD date of the rate data used.
rateSource.cacheHitbooleanTrue if the rate was served from Clearvo's in-memory rate cache. False if a live lookup was performed.
Tax Calculations

Tax Treatments

Clearvo selects a tax treatment for each transaction using a priority-ordered decision tree. The treatment determines the EN16931 tax code and effective rate applied to line items.

TreatmenttaxCodeRateWhen applied
STANDARD S Country standard or reduced rate Default B2C treatment. The buyer is in the seller's country, or the seller is registered in the buyer's country and no other rule takes priority. Correct rate band (STANDARD, REDUCED, etc.) is selected per product category.
IOSS S Destination country rate Non-EU seller with an IOSS number, selling physical goods to an EU B2C customer, where the total goods value is ≤€150. VAT is collected at the destination country's rate and declared via IOSS.
REVERSE_CHARGE AE (non-EU→EU B2B) or K (EU→EU B2B cross-border) 0% B2B transaction where the buyer has a verified VAT ID and the transaction crosses an EU border. Tax liability shifts to the buyer. No VAT is charged on the invoice.
ZERO_RATED Z 0% Product falls into a zero-rated category in the destination jurisdiction (e.g. children's clothing in the UK, basic foodstuffs in many EU countries).
EXEMPT E 0% An exemption certificate has been registered for this customer's tax ID in the relevant jurisdiction and product category. See Exemptions.
OUT_OF_SCOPE O 0% The transaction falls outside the scope of indirect tax (e.g. financial services, insurance, inter-group transactions where both parties are in the same VAT group).
SELLER_NOT_REGISTERED O 0% The seller's tax obligation record for the buyer's jurisdiction has registrationStatus: "NOT_REGISTERED". No tax is charged. The obligation status surfaces in the dashboard as MONITORING or ACTION_REQUIRED.
EXPORT G 0% EU seller, non-EU buyer — goods or services exported outside the EU VAT area. Zero-rated as an export.
The taxCode field in the response maps directly to the taxCode field accepted by the Clearvo e-invoicing /v1/send endpoint. No translation or lookup is required — the calculation output can flow straight into your invoice submission.
Tax Calculations

Jurisdiction Resolution

Clearvo determines which tax authority's rules apply to a transaction using a rule-based resolution chain. The resolved jurisdiction, resolution method, and any conflicting signals are all returned in the response.

1
Physical goods — shipping address wins

For tangible goods, the shipping destination controls the jurisdiction. If a shippingAddress.country is provided, it takes priority over all other signals.

2
B2B — VAT ID prefix then billing address

For B2B transactions, Clearvo extracts the country prefix from the customer's taxId (e.g. DE from DE987654321) and uses it as the primary signal. If the VAT ID prefix is absent or unverifiable, the billing address country is used.

3
B2C digital services — 2-of-3 rule (EU)

EU regulations require sellers of B2C digital services to collect two non-contradictory pieces of evidence of the customer's location. Clearvo evaluates up to three signals: billing address country, IP geolocation country, and card BIN country. The majority wins. If all three agree, precision is EXACT; if only two agree, it is COUNTRY. Disagreements are listed in jurisdiction.conflicts.

4
Special postal territories

Some territories use the country code of their parent state but are outside the EU VAT area. Clearvo detects these automatically from the postal code:

  • Canary Islands (ES 35xxx–38xxx) — outside EU VAT
  • Ceuta & Melilla (ES 51xxx, 52xxx) — outside EU VAT
  • Åland Islands (FI 22xxx) — outside EU VAT
  • Madeira (PT 90xxx–93xxx) — inside EU VAT
5
US address precision tiers

For US transactions, Clearvo looks up the applicable sales tax rate from the CCH database using one of three precision tiers, depending on which address fields are supplied and your account's usAddressPrecision setting:

  • ROOFTOP — a street address (line1) is present and your account setting is usAddressPrecision: "rooftop" (the default). Clearvo resolves the address to a zip+4 code and looks up the exact rate for that delivery-point range in the CCH database. "Rooftop" is the industry-standard term for address-level precision in US sales tax — the CCH rate table is keyed on zip+4 ranges, which cover approximately 10–20 delivery points.
  • ZIP — only a postal code is available, or your account setting is usAddressPrecision: "zip" (which skips street-address resolution even when line1 is present). Clearvo uses the zip5 code directly against the CCH database.
  • STATE — only a state code (region) is available with no postal code. Clearvo applies the state-level rate. Note that US region is always required — if it is missing, the API returns a 422 error.

The tier used is returned in jurisdiction.addressPrecision on every US response. You can switch your account between ROOFTOP and ZIP precision under Settings → Tax Calculations.

When the resolution chain cannot determine the jurisdiction from available signals, Clearvo falls back to the seller's country and marks the result as degraded: true with a degradedReason of "jurisdiction_unresolved".

Tax Calculations

Product Classification

Clearvo classifies each line item into a product category to determine the correct VAT rate band — for example, whether a digital product is taxed at the standard rate or a reduced "cultural goods" rate. No HS codes are required.

Classification pipeline

1
Explicit override (taxCategory)

If you pass a taxCategory slug on a line item, that category is used with confidence 1.0 and status: "MANUAL". No AI call is made. Use this for predictable, stable product catalogues where you know the correct classification.

2
Cache lookup

Clearvo checks whether a prior classification exists for this product (matched by product code or a hash of the product name). If a cached result is found with sufficient confidence, it is used immediately with fromCache: true. Cache entries are per-account and persist across sessions.

3
AI classification

For unrecognised products, Clearvo passes the productName to an AI classifier, which maps it to the closest available category slug. The result is returned with a confidence score and cached for future calls. Products with low confidence scores return status: "NEEDS_REVIEW" — you can supply an explicit taxCategory on the next call to override.

Common category slugs

A representative sample — call GET /v1/tax/categories for the complete live list.

SlugTypical rate bandExamples
saas_businessSTANDARDSaaS subscriptions (B2B use)
saas_personalSTANDARDSaaS subscriptions (consumer / personal use)
api_data_servicesSTANDARDAPI access, data feeds, webhooks
streaming_videoSTANDARDVideo streaming subscriptions
ebooksREDUCED (most EU countries)Digital books, audiobooks, online newspapers
books_physicalREDUCED (most EU countries)Printed books, newspapers, maps
online_coursesEXEMPT (many jurisdictions)E-learning, training subscriptions
food_basicREDUCED or ZEROGroceries, basic unprocessed foods
food_restaurantREDUCED or STANDARDPrepared meals, catering
medical_devicesEXEMPT or REDUCEDMedical equipment, hearing aids, spectacles
pharmaceuticalsREDUCED or ZEROPrescription drugs, licensed medications
clothing_childrenREDUCED or ZEROChildren's apparel (zero-rated in UK and IE)
financial_servicesEXEMPTBanking fees, insurance premiums, credit services
professional_servicesSTANDARDConsulting, legal, accounting, advisory
physical_goods_generalSTANDARDCatch-all for physical goods with no specific slug
digital_generalSTANDARDCatch-all for digital goods with no specific slug
nontaxableZERO / EXEMPTItems not subject to indirect tax in any jurisdiction
For production use, we recommend providing an explicit taxCategory slug for each product in your catalogue. This eliminates AI classification latency, gives you deterministic results, and avoids any risk of misclassification. Reserve the AI classification path for edge cases or exploratory integrations.
Tax Calculations

E-Invoicing Integration

Tax Calculation output is designed to feed directly into Clearvo e-invoice submissions. The taxCode and rate fields on each calculated line item match the fields expected by the POST /v1/send endpoint — no mapping layer is needed.

Example: calculate then invoice

Step 1 — calculate
{
  // POST /v1/tax/calculate response (line items)
  "lineItems": [{
    "id": "line-1",
    "taxCode": "S",
    "rate": 0.19,
    "taxableAmount": 100.00,
    "taxAmount": 19.00,
    "totalAmount": 119.00
  }]
}
Step 2 — invoice (POST /v1/send)
{
  "invoiceNumber": "INV-2026-001",
  "issueDate": "2026-06-21",
  "currency": "EUR",
  "country": "DE",
  // supplier and buyer fields …
  "lineItems": [{
    "description": "Annual SaaS subscription",
    "quantity": 1,
    "unitPrice": 100.00,
    "taxCode": "S",    // ← from calculation response
    "taxRate": 0.19,     // ← from calculation response
    "taxAmount": 19.00  // ← from calculation response
  }]
}

EN16931 tax code reference

CodeNameWhen used
SStandard rateStandard or reduced VAT/GST applied to the transaction
AALower rateA lower rate than the standard rate applies (e.g. reduced band in EU)
AEVAT Reverse ChargeNon-EU seller to EU B2B buyer — buyer accounts for VAT
KVAT exempt (intra-community)EU-to-EU cross-border B2B — intra-community supply
GFree export item, tax not chargedEU seller, non-EU buyer — export outside the EU VAT area
EExempt from taxTransaction is exempt under local rules (financial services, health, education)
ZZero-rated goodsGoods or services taxable at 0% (e.g. children's clothing in UK)
OServices outside scope of taxTransaction falls outside the indirect tax scope, or seller is not registered in the jurisdiction
Tax Calculations

Sandbox

Use a csk_test_* API key to run calculations in sandbox mode. Sandbox calculations are identical to production in every respect except that they are not billed and the sandbox field on the response is true.

What runs normally in sandbox

  • Jurisdiction resolution (all signals, postal territory detection, 2-of-3 rule)
  • Rate lookup (same rate tables as production)
  • Product classification (AI classifier and cache)
  • Tax treatment determination (all rules, including IOSS and reverse charge)
  • DB write when commit: true (written to sandbox DB, not production)
  • Idempotency key enforcement (scoped to sandbox DB)

What is skipped in sandbox

  • VIES customer VAT ID verification — the customer's VAT ID is accepted at face value; no live VIES call is made
  • Billing — sandbox calculations do not count toward usage
curl — sandbox
curl https://api.clearvo.io/v1/tax/calculate \
  -X POST \
  -H "x-api-key: csk_test_..." \
  -H "Content-Type: application/json" \
  -d '{
    "currency": "EUR",
    "seller": { "country": "IE", "taxId": "IE1234567T" },
    "customer": { "address": { "country": "FR" } },
    "lineItems": [{ "id": "l1", "amount": 99.00, "productName": "SaaS subscription" }]
  }'
JSON Response — sandbox
{
  "calculationId": "cl_calc_test_...",
  "committed": false,
  "sandbox": true,
  "degraded": false,
  "currency": "EUR",
  "jurisdiction": { "country": "FR", "method": "BILLING_ADDRESS" },
  "treatment": { "taxTreatment": "STANDARD", "taxCode": "S", "rate": 0.20 },
  "lineItems": [{
    "id": "l1",
    "taxCode": "S",
    "rate": 0.20,
    "taxableAmount": 99.00,
    "taxAmount": 19.80,
    "totalAmount": 118.80
  }],
  "totals": { "subtotal": 99.00, "totalTax": 19.80, "total": 118.80 }
}
The sandbox is the right place to validate your full calculate→invoice workflow end-to-end. Rates are real, jurisdiction resolution is real, and the taxCode output is identical to production — the only difference is that the sandbox flag is set and no charge is incurred.