Tax rules define the tax rates applied to charges based on jurisdiction, product category, and customer location. Kontier evaluates tax rules at invoicing time and adds the appropriate tax line items to each invoice.
Key concepts:
- Jurisdiction - country and optional region the rule applies to
- Tax type -
vat,sales_tax,gst,hst, etc. - Inclusive vs. exclusive - whether the tax is included in the listed price or added on top
- Compound tax - whether this tax is calculated on the pre-tax amount or on the amount including other taxes
- Apply order - controls the sequence when multiple tax rules apply
List tax rules
Lists tax rules with cursor-based pagination and the unified field.op=value filter grammar (jurisdiction_country, jurisdiction_region, tax_type, status, rate, valid_from, valid_to, version, created_at, updated_at, published_at, archived_at). Each row shows the rule's live version. head_version names the live version on every answer; include=scheduled_versions adds the versions scheduled to publish.
Required scope: tax.profile.read (Read tax profile).
query Parameters
includeExtra data to add to the response: scheduled_versions. Send a comma-separated list or repeat the parameter. An unknown name is refused with 400. scheduled_versions is sent without being asked until 2026-11-30; after that date it is sent only when named here.
limitPage size. Values above the server-side cap are clamped (standard default 50, cap 200; a few document-heavy lists use larger windows). Invalid values fall back to the default.
cursorOpaque continuation token from the previous response's pagination.cursor. Omit for the first page. Cursors are stateless and do not expire, but are only valid for the list and filters that produced them.
sortOrdering: created_at, with an optional :asc/:desc suffix (default created_at:desc). An unknown value is rejected. Cursors are bound to the ordering that issued them, so changing sort mid-walk needs a fresh first page.
jurisdiction_countryFilter by jurisdiction country (eq/in)
jurisdiction_regionFilter by jurisdiction region (eq/ne/in)
tax_typeFilter by tax type (eq/in)
statusFilter by status (eq/ne/in). Filterable fields accept apifilter operator suffixes, e.g. status__in=A,B
rateFilter by rate (eq/ne/gt/gte/lt/lte)
valid_fromFilter by valid_from (eq/gt/gte/lt/lte)
valid_toFilter by valid_to (eq/gt/gte/lt/lte)
created_atFilter by created_at (eq/gt/gte/lt/lte)
has_scheduled_versionFilter by whether the entity has a version scheduled to publish (true or false). A scheduled version is a FROZEN version row a pending catalog.publish_version Change is bound to; the entity itself stays draft, published or archived and its working draft stays free for immediate changes.
Headers
X-Workspace-IdSelects the workspace this request operates in, by workspace UUID or slug (e.g. a sandbox workspace for test integrations). Omitted: the organization's default live workspace. Unknown workspace: 404; workspace outside your organization: 403. Discover workspaces via GET /workspaces.
Workspace UUID or slug.
List tax rules › Responses
OK
Create a tax rule
Creates a tax rule for the organization with a working draft, and publishes nothing: it taxes nothing until a publish (POST /changes with catalog.publish_version). Answers the rule with its working draft's values (rate, is_compound, metadata, version, version_id) until 2026-11-30; from that date it answers the rule as GET /tax-rules/{id} does, and the draft is read at GET /tax-rules/{id}/versions/draft. head_version names the live version either way.
Required scope: tax.profile.write (Write tax profile).
query Parameters
includeExtra data to add to the response: scheduled_versions. Send a comma-separated list or repeat the parameter. An unknown name is refused with 400. scheduled_versions is sent without being asked until 2026-11-30; after that date it is sent only when named here.
Headers
Idempotency-KeyUnique key that makes this POST safe to retry: repeats with the same key replay the first response instead of re-executing. Replays are scoped to the retrying principal (same API key / user) and kept for 24 hours. Required on every POST.
Client-generated idempotency key (e.g. a UUID).
X-Workspace-IdSelects the workspace this request operates in, by workspace UUID or slug (e.g. a sandbox workspace for test integrations). Omitted: the organization's default live workspace. Unknown workspace: 404; workspace outside your organization: 403. Discover workspaces via GET /workspaces.
Workspace UUID or slug.
Create a tax rule › Request Body
jurisdiction_countryrate^-?\d+(\.\d+)?$ · requiredDecimal number encoded as a string to preserve precision.
tax_typeRequests also accept the new spelling: each value in lower case (custom for CUSTOM). The listed spelling is deprecated in requests and accepted until 2026-11-30.
applies_to_customer_typesapplies_to_product_categoriesapplies_to_supply_typesapply_orderis_compoundjurisdiction_regionrequires_continuous_coverageCreate a tax rule › Responses
Created
Get a tax rule
Returns the rule as it stands, from its live version, with its lifecycle status. A rule that was archived or has ended reports the last version that was live; one that was never published has no head_version. An unknown id, or one from another organization, answers 404. head_version names the live version on every answer; include=scheduled_versions adds the versions scheduled to publish.
Required scope: tax.profile.read (Read tax profile).
path Parameters
idTax rule UUID
query Parameters
includeExtra data to add to the response: scheduled_versions. Send a comma-separated list or repeat the parameter. An unknown name is refused with 400. scheduled_versions is sent without being asked until 2026-11-30; after that date it is sent only when named here.
Headers
X-Workspace-IdSelects the workspace this request operates in, by workspace UUID or slug (e.g. a sandbox workspace for test integrations). Omitted: the organization's default live workspace. Unknown workspace: 404; workspace outside your organization: 403. Discover workspaces via GET /workspaces.
Workspace UUID or slug.
Get a tax rule › Responses
OK
Update a tax rule
Deprecated: use PATCH /tax-rules/{id} instead. Removed on 2026-11-30.
Merge-updates a tax rule: a field left out stays as it is. It publishes nothing. A billing-field edit (rate, is_compound, and before the first publish the jurisdiction, tax type, apply order and applies_to sets) stages on the rule's working draft, so what is being invoiced does not move; metadata and requires_continuous_coverage land on the rule at once. Read the staged values at GET /tax-rules/{id}/versions/draft and discard them with DELETE /tax-rules/{id}/draft. Taking them live, now or on a date, is POST /changes with operation catalog.publish_version and subject_kind tax_rule. {"status": "archived"}, sent alone, ends the rule now: it bills nothing from this instant and keeps its history; a rule that is already archived is answered unchanged. Answers the working draft's values (see version_id); PATCH /tax-rules/{id} answers the rule as GET does.
Required scope: tax.profile.write (Write tax profile).
path Parameters
idTax rule UUID
query Parameters
includeExtra data to add to the response: scheduled_versions. Send a comma-separated list or repeat the parameter. An unknown name is refused with 400. scheduled_versions is sent without being asked until 2026-11-30; after that date it is sent only when named here.
Headers
X-Workspace-IdSelects the workspace this request operates in, by workspace UUID or slug (e.g. a sandbox workspace for test integrations). Omitted: the organization's default live workspace. Unknown workspace: 404; workspace outside your organization: 403. Discover workspaces via GET /workspaces.
Workspace UUID or slug.
Update a tax rule › Request Body
applies_to_customer_typesapplies_to_product_categoriesapplies_to_supply_typesapply_orderis_compoundjurisdiction_countryjurisdiction_regionrate^-?\d+(\.\d+)?$Decimal number encoded as a string to preserve precision.
requires_continuous_coveragestatusarchived ends the rule now: it bills nothing from this instant, and its history stays. Send it alone. A rule that is already archived is answered unchanged. Publishing is POST /changes with catalog.publish_version.
tax_typeRequests also accept the new spelling: each value in lower case (custom for CUSTOM). The listed spelling is deprecated in requests and accepted until 2026-11-30.
Update a tax rule › Responses
OK
Delete a tax rule
Removes the rule from the list and archives its versions: it bills nothing from now on. GET /tax-rules/{id} still answers it, with lifecycle archived. To end a rule and keep it listed, send PATCH /tax-rules/{id} with status archived.
Required scope: tax.profile.write (Write tax profile).
path Parameters
idTax rule UUID
Headers
X-Workspace-IdSelects the workspace this request operates in, by workspace UUID or slug (e.g. a sandbox workspace for test integrations). Omitted: the organization's default live workspace. Unknown workspace: 404; workspace outside your organization: 403. Discover workspaces via GET /workspaces.
Workspace UUID or slug.
Delete a tax rule › Responses
No Content
Update a tax rule
Merge-updates a tax rule: a field left out stays as it is. It publishes nothing. A billing-field edit (rate, is_compound, and before the first publish the jurisdiction, tax type, apply order and applies_to sets) stages on the rule's working draft, so what is being invoiced does not move; metadata and requires_continuous_coverage land on the rule at once. Read the staged values at GET /tax-rules/{id}/versions/draft and discard them with DELETE /tax-rules/{id}/draft. Taking them live, now or on a date, is POST /changes with operation catalog.publish_version and subject_kind tax_rule. {"status": "archived"}, sent alone, ends the rule now: it bills nothing from this instant and keeps its history; a rule that is already archived is answered unchanged. Answers the rule as GET /tax-rules/{id} does.
Required scope: tax.profile.write (Write tax profile).
path Parameters
idTax rule UUID
query Parameters
includeExtra data to add to the response: scheduled_versions. Send a comma-separated list or repeat the parameter. An unknown name is refused with 400. scheduled_versions is sent without being asked until 2026-11-30; after that date it is sent only when named here.
Headers
X-Workspace-IdSelects the workspace this request operates in, by workspace UUID or slug (e.g. a sandbox workspace for test integrations). Omitted: the organization's default live workspace. Unknown workspace: 404; workspace outside your organization: 403. Discover workspaces via GET /workspaces.
Workspace UUID or slug.
Update a tax rule › Request Body
applies_to_customer_typesapplies_to_product_categoriesapplies_to_supply_typesapply_orderis_compoundjurisdiction_countryjurisdiction_regionrate^-?\d+(\.\d+)?$Decimal number encoded as a string to preserve precision.
requires_continuous_coveragestatusarchived ends the rule now: it bills nothing from this instant, and its history stays. Send it alone. A rule that is already archived is answered unchanged. Publishing is POST /changes with catalog.publish_version.
tax_typeRequests also accept the new spelling: each value in lower case (custom for CUSTOM). The listed spelling is deprecated in requests and accepted until 2026-11-30.
Update a tax rule › Responses
OK
Archive a tax rule
Deprecated: use PATCH /tax-rules/{id} instead. Removed on 2026-11-30.
Ends a live tax rule now. Later invoice runs use the next applicable rule, if any. Send PATCH /tax-rules/{id} with {"status": "archived"} instead: it answers the rule.
Required scope: tax.profile.write (Write tax profile).
path Parameters
idTax rule UUID
Headers
Idempotency-KeyUnique key that makes this POST safe to retry: repeats with the same key replay the first response instead of re-executing. Replays are scoped to the retrying principal (same API key / user) and kept for 24 hours. Required on every POST.
Client-generated idempotency key (e.g. a UUID).
X-Workspace-IdSelects the workspace this request operates in, by workspace UUID or slug (e.g. a sandbox workspace for test integrations). Omitted: the organization's default live workspace. Unknown workspace: 404; workspace outside your organization: 403. Discover workspaces via GET /workspaces.
Workspace UUID or slug.
Archive a tax rule › Responses
No Content
Copy a tax rule version into the working draft
Makes the rule's working draft carry the rate and is_compound of the version whose id is in the path, and publishes nothing. Use it to bring back what an older version said. The body's metadata is not read. Answers the rule with its working draft's values (rate, is_compound, metadata, version, version_id) until 2026-11-30; from that date it answers the rule as GET /tax-rules/{id} does, and the draft is read at GET /tax-rules/{id}/versions/draft. head_version names the live version either way.
Required scope: tax.profile.write (Write tax profile).
path Parameters
idSource tax rule version UUID
query Parameters
includeExtra data to add to the response: scheduled_versions. Send a comma-separated list or repeat the parameter. An unknown name is refused with 400. scheduled_versions is sent without being asked until 2026-11-30; after that date it is sent only when named here.
Headers
Idempotency-KeyUnique key that makes this POST safe to retry: repeats with the same key replay the first response instead of re-executing. Replays are scoped to the retrying principal (same API key / user) and kept for 24 hours. Required on every POST.
Client-generated idempotency key (e.g. a UUID).
X-Workspace-IdSelects the workspace this request operates in, by workspace UUID or slug (e.g. a sandbox workspace for test integrations). Omitted: the organization's default live workspace. Unknown workspace: 404; workspace outside your organization: 403. Discover workspaces via GET /workspaces.
Workspace UUID or slug.
Copy a tax rule version into the working draft › Responses
Created
Get the open working draft
The entity's ONE working draft: its row id, the version it publishes as, when it was opened, the version it is authored on top of, and a counted summary of what is staged. data is null when nothing is staged — a state, not an error.
Required scope: tax.profile.read (Read tax profile).
path Parameters
idEntity UUID
Headers
X-Workspace-IdSelects the workspace this request operates in, by workspace UUID or slug (e.g. a sandbox workspace for test integrations). Omitted: the organization's default live workspace. Unknown workspace: 404; workspace outside your organization: 403. Discover workspaces via GET /workspaces.
Workspace UUID or slug.
Get the open working draft › Responses
OK
Open the working draft on a chosen base
Opens the working draft if none is open, and records which version it is authored on top of: the live head (omit base_version_id) or one of the entity's scheduled versions. Re-basing an open draft keeps its edits — the delta is the author's intent and is never rewritten — and re-materialises them over the new base. A publish of a draft based on a scheduled version cannot be scheduled to fire before that version does.
Required scope: tax.profile.write (Write tax profile).
path Parameters
idEntity UUID
Headers
Idempotency-KeyUnique key that makes this POST safe to retry: repeats with the same key replay the first response instead of re-executing. Replays are scoped to the retrying principal (same API key / user) and kept for 24 hours. Required on every POST.
Client-generated idempotency key (e.g. a UUID).
X-Workspace-IdSelects the workspace this request operates in, by workspace UUID or slug (e.g. a sandbox workspace for test integrations). Omitted: the organization's default live workspace. Unknown workspace: 404; workspace outside your organization: 403. Discover workspaces via GET /workspaces.
Workspace UUID or slug.
Open the working draft on a chosen base › Request Body
base_version_idThe version the working draft is authored on top of — the live head, or one of the entity's scheduled versions. Omit for the head.
Open the working draft on a chosen base › Responses
OK
Discard the open working draft
Archives the working draft with reason 'discarded'. The live entity is untouched: nothing pointed at the draft and it was never on the billing timeline. 404 when nothing is staged, which is a different answer from a successful discard and is reported as one.
Required scope: tax.profile.write (Write tax profile).
path Parameters
idEntity UUID
Headers
X-Workspace-IdSelects the workspace this request operates in, by workspace UUID or slug (e.g. a sandbox workspace for test integrations). Omitted: the organization's default live workspace. Unknown workspace: 404; workspace outside your organization: 403. Discover workspaces via GET /workspaces.
Workspace UUID or slug.
Discard the open working draft › Responses
No Content
Replace what the working draft contains
States what the draft should CONTAIN; the server derives the change against the draft's base and validates the result. A child the body omits is removed from the draft. Opens the working draft on the head when none is open.
Required scope: tax.profile.write (Write tax profile).
path Parameters
idEntity UUID
Headers
X-Workspace-IdSelects the workspace this request operates in, by workspace UUID or slug (e.g. a sandbox workspace for test integrations). Omitted: the organization's default live workspace. Unknown workspace: 404; workspace outside your organization: 403. Discover workspaces via GET /workspaces.
Workspace UUID or slug.
Replace what the working draft contains › Request Body
is_compoundWhether this rule's base is the running total of the rules before it in the stack, rather than the line amount.
rate^-?\d+(\.\d+)?$ · requiredThe rate as a fraction: 19% is 0.19.
The operator's own annotation of this version.
Replace what the working draft contains › Responses
OK
List a tax-rule's version timeline
The entity's versions in the order they fired and will fire: history, head, scheduled releases, working draft. Published and scheduled rows are returned by default; pass include=all for the draft and the archived rows. Same row shape as /versions.
Required scope: tax.profile.read (Read tax profile).
path Parameters
idEntity UUID
query Parameters
includeSet to 'all' to include the working draft and archived rows.
limitPage size (default 100, max 200)
cursorOpaque continuation token from the previous response's pagination.cursor. Omit for the first page. Cursors are stateless and do not expire, but are only valid for the list and filters that produced them.
Headers
X-Workspace-IdSelects the workspace this request operates in, by workspace UUID or slug (e.g. a sandbox workspace for test integrations). Omitted: the organization's default live workspace. Unknown workspace: 404; workspace outside your organization: 403. Discover workspaces via GET /workspaces.
Workspace UUID or slug.
List a tax-rule's version timeline › Responses
OK
List tax-rule versions
Returns version rows for the entity, keyset-paginated (default 100, max 200 per page). The status filter accepts draft, scheduled, published, archived; the default excludes draft. include=usage_count adds how many records use each version; sort=version:desc lists the newest first.
Required scope: tax.profile.read (Read tax profile).
path Parameters
idEntity UUID
query Parameters
includeExtra data to add to the response: usage_count. Send a comma-separated list or repeat the parameter. An unknown name is refused with 400.
statusLifecycle status filter (repeatable / comma-separated): draft, scheduled, published, archived. Default excludes draft.
archived_reasonNarrow archived rows by reason (repeatable / comma-separated): canceled, discarded, errored, superseded
sortOrder: version:asc (the default) lists the versions in the order they fired and will fire; version:desc lists the highest number first, versions without a number (the working draft, archived rows) last.
limitPage size (default 100, max 200)
cursorOpaque continuation token from the previous response's pagination.cursor. Omit for the first page. Cursors are stateless and do not expire, but are only valid for the list and filters that produced them.
Headers
X-Workspace-IdSelects the workspace this request operates in, by workspace UUID or slug (e.g. a sandbox workspace for test integrations). Omitted: the organization's default live workspace. Unknown workspace: 404; workspace outside your organization: 403. Discover workspaces via GET /workspaces.
Workspace UUID or slug.
List tax-rule versions › Responses
OK
Get a tax-rule version snapshot
The version's own fields plus the common version header. {version} is the number, or the word 'draft' for the open working draft.
Required scope: tax.profile.read (Read tax profile).
path Parameters
idEntity UUID
versionThe version number, or the word 'draft' for the open working draft.
Headers
X-Workspace-IdSelects the workspace this request operates in, by workspace UUID or slug (e.g. a sandbox workspace for test integrations). Omitted: the organization's default live workspace. Unknown workspace: 404; workspace outside your organization: 403. Discover workspaces via GET /workspaces.
Workspace UUID or slug.
Get a tax-rule version snapshot › Responses
OK
Name or annotate a tax-rule version
Sets the version's release_name and/or release_note. These are display metadata, not content: editing them never creates a new version and is allowed on published versions. An absent field is left unchanged; null clears it; an empty string is refused. It answers the version row without the data envelope; from 2026-11-30 it answers {"data": …} like every other operation.
Required scope: tax.profile.write (Write tax profile).
path Parameters
idEntity UUID
versionThe version number, or the word 'draft'.
Headers
X-Workspace-IdSelects the workspace this request operates in, by workspace UUID or slug (e.g. a sandbox workspace for test integrations). Omitted: the organization's default live workspace. Unknown workspace: 404; workspace outside your organization: 403. Discover workspaces via GET /workspaces.
Workspace UUID or slug.
Name or annotate a tax-rule version › Request Body
release_nameNew name; null clears it. Absent leaves it unchanged.
release_noteNew note; null clears it. Absent leaves it unchanged.
Name or annotate a tax-rule version › Responses
OK
created_atWhen the row was opened — for a draft when editing started, for a release when it was armed.
effective_fromeffective_toidThe catalog_versions row id. A client selecting a version writes this, never the number.
originWho made this version: manual (an operator published it), scheduled_release (a scheduled publish fired), cascade (republished automatically because something it depends on published).
published_atstatusThe row's own state: draft, scheduled, published or archived.
versionThe version's display number, or 0 for a row that is not on the chain (a working draft, an archived row).
archived_reasonWhy an archived row left the timeline: canceled, discarded or superseded.
For origin=cascade: what caused this version.
What the version does differently from the version it replaced; from is absent for the entity's first version. Sent only with include=diff (plans and products).
Who published the version — the same actor the audit trail names: an operator, an API key, or the system for a cascade. Absent on a version not yet published and on one published before publishers were recorded.
rank1-based position in the entity's queue of scheduled versions — the order they will fire. Present only on scheduled rows.
recorded_atWhen the publish was captured, on the wall clock (its audit row). published_at is the operation's instant — a sandbox's test clock — and effective_from when it governs. Sent with include=diff on published versions that have an audit row.
release_nameOperator-given name of this version, shown beside its number.
release_noteFree-text note about this version.
scheduled_change_idThe pending catalog.publish_version Change that will publish this version — reschedule or cancel it there.
scheduled_forWhen this scheduled version is due to publish. Present only on a scheduled row.
usage_countPresent only with ?include=usage_count.
Compare two tax-rule versions
Every cell that differs between two versions, with the value on each side. ?against= takes 'head' (the default), another version number, or 'draft'. The comparison is of the two versions' STORED grids — nothing is recomputed at read time.
Required scope: tax.profile.read (Read tax profile).
path Parameters
idEntity UUID
versionThe version number, or the word 'draft'.
query Parameters
againstWhat to compare against: 'head' (the default) for the live version, or another version number, or 'draft'.
Headers
X-Workspace-IdSelects the workspace this request operates in, by workspace UUID or slug (e.g. a sandbox workspace for test integrations). Omitted: the organization's default live workspace. Unknown workspace: 404; workspace outside your organization: 403. Discover workspaces via GET /workspaces.
Workspace UUID or slug.
Compare two tax-rule versions › Responses
OK
Preview what else publishing this version would publish
The entities that will publish BECAUSE this one does — a plan that tracks this product's latest, say — each with the cells its next head would carry that its current one does not. Derived on every read and never frozen: something that starts following tomorrow appears tomorrow.
Required scope: tax.profile.read (Read tax profile).
path Parameters
idEntity UUID
versionThe version number, or the word 'draft' for the open working draft.
Headers
X-Workspace-IdSelects the workspace this request operates in, by workspace UUID or slug (e.g. a sandbox workspace for test integrations). Omitted: the organization's default live workspace. Unknown workspace: 404; workspace outside your organization: 403. Discover workspaces via GET /workspaces.
Workspace UUID or slug.
Preview what else publishing this version would publish › Responses
OK
Forecast the impact of superseding this tax-rule version
Who is using the version in force today, and what a publish over it would move. pinnable reports whether holding a version is possible for this entity at all — it is false for tax rules, which are dated rather than pinned, so a caller must not render a pinned count for them. republishes_consumers is the cross-entity fan-out (publishing a price formula republishes every ACTIVE product whose head tiers reference it), and live_readers counts consumers billing off the live head, who move as soon as the publish lands. Read-only, and uncached: this is read at the moment money changes.
Required scope: tax.profile.read (Read tax profile).
path Parameters
idEntity UUID
versionVersion number (positive integer)
Headers
X-Workspace-IdSelects the workspace this request operates in, by workspace UUID or slug (e.g. a sandbox workspace for test integrations). Omitted: the organization's default live workspace. Unknown workspace: 404; workspace outside your organization: 403. Discover workspaces via GET /workspaces.
Workspace UUID or slug.
Forecast the impact of superseding this tax-rule version › Responses
OK
List tax-rule version referrers
Offset-paginated drill-down of the referrers of one type for one version of the entity.
Required scope: tax.profile.read (Read tax profile).
path Parameters
idEntity UUID
versionVersion number (positive integer)
query Parameters
typeReferrer type to list (required)
limitPage size (default 50, max 200)
cursorOpaque continuation token from the previous response's pagination.cursor. Omit for the first page. Cursors are stateless and do not expire, but are only valid for the list and filters that produced them.
Headers
X-Workspace-IdSelects the workspace this request operates in, by workspace UUID or slug (e.g. a sandbox workspace for test integrations). Omitted: the organization's default live workspace. Unknown workspace: 404; workspace outside your organization: 403. Discover workspaces via GET /workspaces.
Workspace UUID or slug.
List tax-rule version referrers › Responses
OK
Get tax-rule version usage summary
Referrer counts for one version of the entity, grouped by referrer type. Powers the version-switcher hover card.
Required scope: tax.profile.read (Read tax profile).
path Parameters
idEntity UUID
versionVersion number (positive integer)
Headers
X-Workspace-IdSelects the workspace this request operates in, by workspace UUID or slug (e.g. a sandbox workspace for test integrations). Omitted: the organization's default live workspace. Unknown workspace: 404; workspace outside your organization: 403. Discover workspaces via GET /workspaces.
Workspace UUID or slug.
Get tax-rule version usage summary › Responses
OK