KintsugiKintsugi
API Guides

5. Sales Tax Calculations

The tax estimate endpoint (POST /v1/tax/estimate) prices sales tax before you take payment. It is the core of the Level 2 (L2) tax engine, enabled once transaction sync (L1) is in place. Rates reflect your nexus, your product taxability, your customer's exemptions, and the address you are shipping to. This guide covers when to call it, how to shape the request, and how to use what comes back.

Understanding Tax Estimates

A tax estimate is a real-time calculation with no permanent transaction record behind it. Each estimate:

  • Prices tax against your current registrations
  • Applies the taxability rules for each product
  • Honors customer exemptions
  • Resolves rates for the destination jurisdiction
  • Returns a per-line-item tax breakdown

Because estimates create no transaction record, you can call the endpoint as often as customers change their cart or their address.

Estimates do not sync transactions: /v1/tax/estimate calculates tax; it does not record a sale. For full compliance (L2), sync the transaction separately through /v1/transactions once payment is confirmed. Kintsugi determines nexus from those synced transactions, which is what makes accurate calculation possible in the first place, even when Kintsugi is not handling your filing and remittance.

When to Calculate Tax

Calculate during checkout or billing, after address entry and before payment processing. Common integration points:

  • Shopping cart pages: When customers review their order
  • Checkout flows: Once the shipping address is entered
  • Subscription billing: When pricing a recurring charge
  • Quote generation: When quoting a total to a customer

Validate the address first: Run addresses through the address validation API before calculating. A verified address means a correct jurisdiction, and a correct jurisdiction means a correct rate.

Tax Estimate Request Structure

An estimate request mirrors the shape of a transaction sync request.

Required Fields

  • external_id: Your unique identifier for the transaction being priced
  • date: Transaction date in ISO 8601 format
  • currency: ISO 4217 currency code, for example USD
  • addresses: At least one address, where SHIP_TO sets the jurisdiction
  • transaction_items: The line items to price

Optional Fields

  • customer: The buyer. Send external_id to match a customer on file and pick up their exemptions
  • description: A label for the estimate
  • source: Where the transaction originated
  • marketplace: Whether the sale runs through a marketplace. Defaults to false

Transaction Items

Each line item requires:

  • date: Item date, normally matching the transaction date
  • amount: Total amount for the line, after discounts

And should carry:

  • external_product_id: The product to price. Required unless you classify the item inline
  • external_id: Your identifier for the line item
  • quantity: Defaults to 1.0
  • exempt: Whether this specific line is exempt. Defaults to false

Classifying an item inline: If external_product_id is missing or does not match a product on file, send both product_category and product_subcategory so Kintsugi can classify the item, optionally with product_name and product_description. Without either a known product or a category pair, the request fails. Pre-built catalogs keep classification consistent, so treat inline classification as a fallback rather than the default path.

Addresses

Every address entry needs a type of SHIP_TO or BILL_TO, plus street_1, street_2, city, county, state, postal_code, and country (ISO 3166-1 alpha-2).

The SHIP_TO address decides which rates apply. When no SHIP_TO address is present, Kintsugi falls back to BILL_TO.

Tax Estimate Response

The response echoes your request and adds the calculation.

At the top level:

  • total_tax_amount_calculated: Total tax for the transaction
  • taxable_amount: Total amount subject to tax
  • tax_rate_calculated: Combined effective rate applied
  • has_active_registration: Whether you hold an active registration for this transaction

On each entry in transaction_items:

  • tax_amount: Tax for that line
  • taxable_amount: The portion of the line subject to tax
  • tax_rate: Combined rate applied to the line
  • exempt and exempt_reason: Whether the line was exempt, and why
  • tax_items: The rate components that make up the line's tax, each with a name, rate, amount, and exempt flag

Amounts come back as strings: Monetary values and rates are returned as decimal strings, for example "20.00" and "0.08". Parse them with a decimal-safe type rather than a float so cents do not drift, and note that nexus_met is deprecated in favor of has_active_registration.

Using Tax Amounts

Take the figures from the response to:

  • Show tax to customers during checkout
  • Calculate the final total
  • Carry tax amounts into the transaction you sync later
  • Sanity-check the calculation before you charge

Keep the estimate: Store the response so the transaction you sync afterward carries the same tax amounts the customer saw. Your records and your receipts then agree.

Tax Calculation Workflow

The Three Calls

Each one gates the next. A bad address gives a wrong rate; an unrecorded sale never reaches your filings.

CALL 1
Validate the address
Destination decides the rate, down to the local jurisdiction. Recommended rather than required, but it is what fills in the county.
/v1/address_validation/search
CALL 2
Quote the tax
Returns amounts and the jurisdictions they belong to. Nothing is recorded.
/v1/tax/estimate
CALL 3
Record the sale
Only after payment succeeds, carrying the tax you quoted.
/v1/transactions

Checkout Sequence

Solid step numbers are the Kintsugi calls. Everything else happens in your storefront.

Cart assembled, address entered
Your storefront. No Kintsugi call yet.
Validate the address
POST/v1/address_validation/searchRecommended
Use the returned response_address from here on. It is the standardized version, and enrich_fields tells you what it added: county is the usual one, and local rates depend on it. verification_status tells you how far it got.
Assemble the estimate request
dateexternal_idcurrencyaddressestransaction_items
Line items carry the tax category, so each one needs an existing product record or an inline product_category and product_subcategory pair.
Calculate tax
POST/v1/tax/estimateReturns a quote
RETURNSTax amounts broken out by the jurisdictions that levy them, on each line item's tax_items. A quote only: nothing is stored, and there is no estimate id or expiry to track.
Show the total with tax
Display the quoted amount before the customer pays, not after.
Process payment
DECLINED →Send the customer back to the cart. The estimate was never recorded, so there is nothing to reverse in Kintsugi.
Sync the transaction with the tax amounts
POST/v1/transactionsstatus: COMMITTED
Send the tax you actually charged on total_tax_amount_imported, and per line on tax_amount_imported. That is the collected figure; Kintsugi weighs it against its own calculation to set your liability, so a recalculation here can silently disagree with the customer's receipt. See transaction statuses for what COMMITTED does.
Order complete
The sale counts toward nexus and appears in filings.
An estimate is not a record. If step 7 never runs, the sale is invisible to nexus and filings even though the customer was charged tax.

Nexus and Tax Calculation

Kintsugi calculates tax where you hold an active registration. The has_active_registration flag on the response tells you which side of that line the transaction fell.

When Tax Is Calculated

Tax applies when:

  • You hold an active registration in the customer's jurisdiction
  • The product is taxable there
  • No valid exemption covers the sale

When Tax Is Zero

Tax is zero when:

  • You hold no active registration in that jurisdiction
  • The product is exempt there
  • A valid customer or line-level exemption applies

In the exempt cases, exempt_reason on the line item tells you which rule zeroed it out, whether that was the product, the customer, the region, or something else.

Estimates reflect the present: Each estimate uses your registration and nexus status at the moment of the request. Register in a new jurisdiction and subsequent estimates will price tax there, without any change on your side.

Customer Exemptions

To have an exempt customer's status applied, reference the customer on the estimate:

{
  "date": "2026-01-15T10:00:00Z",
  "external_id": "EST-2026-001",
  "currency": "USD",
  "customer": {
    "external_id": "CUST-789",
    "name": "Northwind Nonprofit"
  },
  "transaction_items": [
    {
      "external_id": "ITEM-001",
      "date": "2026-01-15T10:00:00Z",
      "external_product_id": "PROD-12345",
      "quantity": 1,
      "amount": 100.00
    }
  ],
  "addresses": [
    {
      "type": "SHIP_TO",
      "street_1": "123 Main St",
      "city": "Seattle",
      "state": "WA",
      "postal_code": "98101",
      "country": "US"
    }
  ]
}

When the external_id matches a customer on file, Kintsugi applies that customer's exemptions. If the customer is not found, the details are ignored for exemption purposes and the estimate still returns. For one-off exemptions that are not tied to a customer record, set exempt: true on the relevant line items instead.

See the Product & Customer Records guide for how to create exempt customers.

Error Handling

Common failures when calculating tax:

  • Product cannot be classified: Send a known external_product_id, or both product_category and product_subcategory
  • Invalid address: Validate addresses before calculating
  • Missing required fields: date, external_id, currency, addresses, and transaction_items are all required
  • Missing authentication headers: Every request needs both x-api-key and x-organization-id
  • Rate limiting: Retry with exponential backoff

See the Error Handling guide for detailed strategies.

Best Practices

Request Structure

  • Use consistent external IDs: Keep one identifier across the estimate and the transaction that follows it
  • Validate addresses first: Verified addresses resolve to the right jurisdiction
  • Reference products precisely: external_product_id values must match your product records
  • Send complete line items: Every item needs a date and an amount

Response Handling

  • Store the response: Carry the same tax amounts into transaction sync
  • Handle zero tax: No registration and valid exemptions are normal outcomes, not errors
  • Show the breakdown: Surface tax_items so customers and your support team can see how tax was composed
  • Parse decimals safely: Amounts and rates arrive as strings

Performance

  • Cache short-lived estimates: Reuse a result while the cart and address are unchanged
  • Debounce address input: Wait for typing to settle before calling
  • Fail gracefully: Decide in advance what checkout shows if an estimate fails
  • Watch your call volume: Track usage so you see rate pressure before your customers do

Integration Patterns

E-Commerce Checkout

  1. Customer adds items to the cart
  2. Customer enters a shipping address
  3. Validate the address
  4. Calculate tax with /v1/tax/estimate
  5. Display the total with tax
  6. Process payment
  7. Sync the transaction with those tax amounts

Subscription Billing

  1. Customer selects a plan
  2. Customer provides a billing address
  3. Calculate tax for the first billing cycle
  4. Store the tax amount for recurring charges
  5. Recalculate when the address changes or the subscription renews

Multi-Step Checkout

  1. Calculate tax once the shipping address step is complete
  2. Recalculate when the customer changes address
  3. Recalculate when the customer changes the cart
  4. Update the displayed total after each recalculation

Reading the Tax Breakdown

Each line item's tax_items array shows the rate components behind its tax, for example a state rate and a county rate, each with its own name, rate, and amount. Together with the line's tax_rate and tax_amount, and the transaction-level total_tax_amount_calculated, that gives you a complete picture of the calculation.

Use it to:

  • Show customers how their tax was composed
  • Produce receipts and invoices with real tax detail
  • Debug unexpected results against a specific rate component
  • Reconcile totals before you charge

Next Steps

With tax calculation integrated:

  1. Sync transactions: After payment, sync the sale with the tax amounts from the estimate. See the Syncing Transaction Records guide.

  2. Handle errors: Build the failure path before you need it. See the Error Handling guide.

  3. Tune performance: Cache and debounce to cut calls and keep checkout fast.

For endpoint-level detail, see: