Skip to content

Explain a tax decision

The tax.quotes.explain operation: GET /api/v1/tax/quotes/{id}/explanation in the REST API and the explain_tax_decision tool in the MCP server.

GET/api/v1/tax/quotes/{id}/explanation

Explains a stored tax quote in plain words: why the customer counts as a business or a consumer, where they are located and how sure that is, which tax regime applies and why, where the sale is taxed, what the registration state allowed (charged, below the threshold, reverse charge, blocked), which rate applies, and how the amounts were computed (spec §9.1). notes add what else matters: the invoice wording needed, unverified rules, registration changes the sale would trigger.

The text is written only from Ownrecord's own templates and the quote's codes, never from text a customer or integrator typed: lines are named by their position (lines[].index), with their id beside the text. ruleVersions names every rule version used, so the decision can be replayed. The full rule trace is in tax.quotes.get.

At a glance

  • REST: GET /api/v1/tax/quotes/{id}/explanation
  • MCP tool: explain_tax_decision
  • Operation ID: tax.quotes.explain
  • Scope: Brand
  • Permission: checkout:read
  • Risk tier: read
  • Dry run: Not supported
  • Idempotency: Not needed (read only)
  • Modes: Test and live
  • Authentication: API key with the checkout:read scope
  • Since: M10

Request

Path parameters

FieldTypeDescription
idtq_… IDThe quote's ID (tq_…).

Query parameters

This operation takes no query parameters.

Response

200 OK with a JSON object:

FieldTypeDescription
object"tax_explanation"
quoteIdtq_… ID
livemodeboolean
decisionone of charge, reverse_charge, exempt, out_of_scope, blocked
summarystring
stepsarray of object
steps[].stepinteger (1 to 9)
steps[].nameone of customer, location, regime, place_of_supply, registration, rate, amounts
steps[].titlestring
steps[].textstring
linesarray of object
lines[].indexinteger (at least 0)
lines[].idstring
lines[].textstring
notesarray of string
ruleVersionsobject
ruleVersions.rateIdsarray of string
ruleVersions.regimeIdstring or null
ruleVersions.regimeVersionstring or null
ruleVersions.knowledgeCommitstring

Examples

A consumer in Germany, below the EU threshold

HTTP
GET /api/v1/tax/quotes/tq_01m4tx5nj8emwbyf7y9vcf47xq/explanation
Authorization: Bearer or_test_…

Response (200):

JSON
{
  "object": "tax_explanation",
  "quoteId": "tq_01m4tx5nj8emwbyf7y9vcf47xq",
  "livemode": false,
  "decision": "charge",
  "summary": "Tax is charged: EUR 25.00 on a net EUR 100.00 (EUR 125.00 in total), under Croatia, domestic VAT (HR_DOMESTIC).",
  "steps": [
    {
      "step": 1,
      "name": "customer",
      "title": "Who the customer is",
      "text": "The customer gave no tax ID, so the sale is treated as a sale to a consumer (business-to-consumer)."
    },
    {
      "step": 2,
      "name": "location",
      "title": "Where the customer is",
      "text": "The customer is located in DE: all the location evidence agrees (2 evidence items: billing address, IP address)."
    },
    {
      "step": 3,
      "name": "regime",
      "title": "Which tax regime applies",
      "text": "The regime for this sale is One-Stop Shop, Union scheme (EU_OSS_UNION), chosen from where the seller is established, where the customer is and the customer's type."
    },
    {
      "step": 5,
      "name": "place_of_supply",
      "title": "Where the sale is taxed",
      "text": "Every line is taxed where the customer is."
    },
    {
      "step": 6,
      "name": "registration",
      "title": "What the registration state allows",
      "text": "The entity's sales under the regime are below its registration threshold, so the seller's home-country rules apply instead. The tax is charged under Croatia, domestic VAT (HR_DOMESTIC) instead of One-Stop Shop, Union scheme (EU_OSS_UNION)."
    },
    {
      "step": 7,
      "name": "rate",
      "title": "Which rate applies",
      "text": "Line 1: the standard rate of 25% in HR for the tax category `saas`. The rates are the ones valid at the tax point."
    },
    {
      "step": 8,
      "name": "amounts",
      "title": "How the amounts were computed",
      "text": "Prices exclude tax, so the tax is added to the net amount: net EUR 100.00, tax EUR 25.00, gross EUR 125.00."
    }
  ],
  "lines": [
    {
      "index": 0,
      "id": "pro_monthly",
      "text": "Line 1 (tax category `saas`): tax is charged; net EUR 100.00, tax EUR 25.00, gross EUR 125.00."
    }
  ],
  "notes": [],
  "ruleVersions": {
    "rateIds": ["kn_01m4tx5nj8emwbyf7y9vcf47xs"],
    "regimeId": "EU_OSS_UNION",
    "regimeVersion": "10fc4013168eb593",
    "knowledgeCommit": "f101d33bdd0b1b9257b6b78826836646a30e4fa5"
  }
}

A business in Austria: reverse charge

HTTP
GET /api/v1/tax/quotes/tq_01m4tx5nj8emwbyf7y9vcf47xr/explanation
Authorization: Bearer or_test_…

Response (200):

JSON
{
  "object": "tax_explanation",
  "quoteId": "tq_01m4tx5nj8emwbyf7y9vcf47xr",
  "livemode": false,
  "decision": "reverse_charge",
  "summary": "No tax is charged under One-Stop Shop, Union scheme (EU_OSS_UNION): the business customer accounts for it (reverse charge).",
  "steps": [
    {
      "step": 1,
      "name": "customer",
      "title": "Who the customer is",
      "text": "The customer is a business: its tax ID from AT was checked and is valid, so the sale is business-to-business."
    },
    {
      "step": 2,
      "name": "location",
      "title": "Where the customer is",
      "text": "The customer is located in AT: it is the country of the business's validated tax ID."
    },
    {
      "step": 3,
      "name": "regime",
      "title": "Which tax regime applies",
      "text": "The regime for this sale is One-Stop Shop, Union scheme (EU_OSS_UNION), chosen from where the seller is established, where the customer is and the customer's type."
    },
    {
      "step": 5,
      "name": "place_of_supply",
      "title": "Where the sale is taxed",
      "text": "Every line is taxed where the customer is."
    },
    {
      "step": 6,
      "name": "registration",
      "title": "What the registration state allows",
      "text": "The customer is a business in another country: the customer accounts for the tax (reverse charge), and the entity needs no registration for it."
    },
    {
      "step": 7,
      "name": "rate",
      "title": "Which rate applies",
      "text": "No rate was needed: no line is charged tax."
    },
    {
      "step": 8,
      "name": "amounts",
      "title": "How the amounts were computed",
      "text": "Prices exclude tax, so the tax is added to the net amount: net EUR 100.00, tax EUR 0.00, gross EUR 100.00."
    }
  ],
  "lines": [
    {
      "index": 0,
      "id": "1",
      "text": "Line 1 (tax category `saas`): no tax is charged: the business customer accounts for it (reverse charge); net EUR 100.00, tax EUR 0.00, gross EUR 100.00."
    }
  ],
  "notes": ["The invoice must say that the customer accounts for the VAT (reverse charge)."],
  "ruleVersions": {
    "rateIds": [],
    "regimeId": "EU_OSS_UNION",
    "regimeVersion": "10fc4013168eb593",
    "knowledgeCommit": "f101d33bdd0b1b9257b6b78826836646a30e4fa5"
  }
}

Errors

CodeHTTP statusWhenWhat to do
validation_failed400id isn't a tax quote ID (tq_…).Fix the fields listed in details.issues and send the request again.
resource_not_found404This brand has no such quote in this mode (quotes belong to one brand and one mode).Check the ID and that your credentials are scoped to the right organization, entity, brand and mode.
scope_required400The request names no brand (an org or entity key without Ownrecord-Brand).Call the operation with credentials or a target at its level: an organization, a legal entity, a brand or the platform (details.required_scope).
livemode_not_allowed403You have no live-mode access, so you can't read live quotes.Use test mode, or credentials for the target's mode. Developers need live access granted by an organization owner or admin.
permission_denied403You can't read this brand's checkout data (checkout:read).Ask an organization owner or admin for a role or scope that allows this action.

It can also return these common errors: api_version_unsupported, authentication_required, api_key_invalid, dry_run_not_supported, rate_limited, internal_error.