# Migrating from Avalara/TaxJar to Kintsugi (2026-07-21)

> Map your existing Avalara or TaxJar integration to Kintsugi's endpoints, move your data, and cut over without breaking a filing period

Source: https://docs.trykintsugi.com/docs/2026-07-21/guides/migrating-from-avalara-taxjar

Migrating tax providers is mostly a mapping exercise plus one judgment call. The mapping is small: four concerns, four replacements. The judgment call is how much you want to find out before the old system is switched off. This guide covers both, then the data you need to bring with you.

This page maps your integration to the Tenanted API, release 2026-07-21. Send Api-Key: <key> and Api-Version: 2026-07-21 on every request. Paths carry no /v1 prefix, and every field is camelCase.

Cut over at the start of a filing period and keep the old system's records. A period split across two engines is the one thing that makes a return genuinely hard to reconcile.

## Understanding Key Differences

Three differences change how you build, rather than just which URL you call.

Estimating versus recording

All three providers can quote tax without recording it, but they express it differently, and this is the difference that shapes your integration.

Avalara uses one endpoint for both, switched by DocumentType. A type ending in Order (such as SalesOrder) is a temporary estimate that is not preserved; a type ending in Invoice is a permanent recorded transaction.

TaxJar uses two endpoints: POST /v2/taxes calculates and stores nothing, POST /v2/transactions/orders records.

Kintsugi also uses two: POST /tax-estimations stores nothing, POST /transactions is the record. The record answers 202 Accepted and calculates tax afterwards, so the first response shows processingStatus QUEUED with the calculated tax at "0.00".

If you are coming from Avalara, the cleanest mental translation is that your SalesOrder calls become POST /tax-estimations and your SalesInvoice calls become POST /transactions. If you are coming from TaxJar, the two calls you already make map one to one.

Data model mapping

Kintsugi keys records on your identifiers through externalId, so your own IDs stay the source of truth. Avalara scopes most objects to a company and identifies transactions by a transaction code; TaxJar identifies orders by transaction_id. In all cases, put your existing identifier in Kintsugi's externalId and the reconciliation stays trivial. Creating a customer or product is idempotent on externalId and source, and re-sending a transaction with the same externalId updates it rather than creating a second.

Product classification

## Understanding Key Differences

Avalara assigns tax codes to items you create under a company. TaxJar has no product records at all: you send a product_tax_code on each line item, chosen from its category list.

Kintsugi keeps a product catalog, and each product carries a productCategory and a productSubcategory drawn from Kintsugi's own taxonomy. Kintsugi maintains what each category means in every jurisdiction, so you classify once rather than tracking rule changes.

Tax codes are not portable. An Avalara tax code or a TaxJar product_tax_code has no Kintsugi equivalent, both category fields are required on every product you create, and a pair the catalog does not recognize returns 400. Budget a classification pass over the catalog as real migration work, not a data copy.

## API Endpoint Mapping

Find the call you make today in the left two columns and read across.

Avalara TaxJar Kintsugi

Tax calculation Quote tax for a cart

POST / api/ v2/ transactions/ create DocumentType: SalesOrder POST / v2/ taxes POST / tax-estimations

All three quote without recording. Avalara does it on the same endpoint that records, switched by document type: a type ending in Order is a temporary estimate that is not preserved. TaxJar and Kintsugi use a separate call that stores nothing.

Transaction recording Commit the completed sale

POST / api/ v2/ transactions/ create DocumentType: SalesInvoice POST / v2/ transactions/ orders POST / transactions

This is the call that changes your liability, so if you port one thing exactly, port this one. An Avalara type ending in Invoice is the recorded counterpart of the row above.

Customer management Buyers and their exemptions

POST / api/ v2/ companies/ {companyId}/ customers POST / v2/ customers POST / customers

Key it on the customer ID you already use, so the mapping stays obvious during a parallel run. Exemptions are their own object in Kintsugi: POST /exemptions carries the customerId, and the certificate is uploaded to the exemption.

Product management Catalog and tax categories

POST / api/ v2/ companies/ {companyId}/ items GET / v2/ categories Read-only list POST / products

TaxJar has no catalog to export: it takes a product_tax_code per line item, so this row is a build rather than a migration. Kintsugi wants its own productCategory and productSubcategory, listed by GET /products/categories.

## API Endpoint Mapping

Endpoints map cleanly; tax codes do not. Avalara tax codes and TaxJar product_tax_code values have no Kintsugi equivalent, every product you create needs a productCategory and productSubcategory, and a pair the catalog does not recognize returns 400. Budget a classification pass over the catalog before you rely on Kintsugi's rates.

Kintsugi paths are verified against the spec that generates this site's API Reference. Competitor paths are current as of publication and taken from Avalara's and TaxJar's own SDKs; check them against your provider's reference before you write the mapping into code, since only they control those.

## Migration Strategy

All three approaches end in the same place. They differ in how much you find out before the old system is gone.

Option A Big bang Switch everything on one date.

How it goes

Point every call at Kintsugi Turn the old system off You learn what broke in production

Risk Highest Time Shortest

Option B Parallel run Run both, compare, then switch.

How it goes

Call both, charge the old one Diff the amounts, chase the gaps Switch once the diff is explainable

Risk Low Time Medium

Option C Gradual rollout Move one slice at a time.

How it goes

Start with one region or state Then one product line Widen until nothing is left

Risk Lowest Time Longest

Whichever you pick, cut over at the start of a filing period and keep the old system's records. A period split across two engines is the one thing that makes a return hard to reconcile.

We recommend the parallel run for anything already in production. It is the only option that lets you compare real amounts on real orders before the old system stops being your safety net, and the cost is a few weeks of double-calling rather than a rewrite.

## What to Bring With You

Nexus thresholds are measured over rolling windows, so Kintsugi needs history to tell you where you already have obligations. Starting cold means starting with an empty exposure map.

Products and customers

Create these first: each transaction item names its product by externalProductId. Creates are idempotent on externalId and source, so a batch job can retry a failed request without leaving a duplicate. Store the Kintsugi id against your own record as you go.

Exemption certificates

An exemption is its own object. POST /exemptions carries the customerId it belongs to, along with the exemptionType, the startDate, and the buyer's registration details such as fein and salesTaxId. The certificate itself is a PDF of up to 10 MB, uploaded to the exemption with POST /exemptions/{exemptionId}/certificates. To move many at once, POST /exemptions/bulk creates up to 100 in one all-or-nothing request.

Historical transactions

Sync enough history to cover each state's nexus measurement window, and send each transaction's real date: it decides which filing period the transaction lands in. For a bulk backfill, CSV upload is usually faster than replaying records through the API.

Registrations

Record where you are already registered with POST /registrations, sending the registrationDate each permit takes effect. Kintsugi calculates tax where a registration is active, so a missing registration reads as a state you do not collect in.

Keep externalId values identical to the ones your old integration used. This is what makes a parallel run comparable order by order, and what makes reconciliation possible afterwards.

## Common Migration Challenges

Address validation

Rates resolve to the local level, so an unresolved address produces a rate you cannot defend. Both providers offer address validation and so does Kintsugi, through POST /addresses/validate and POST /addresses/suggestions.

Solution: Validate the address before you estimate, and estimate against the corrected version. The estimate validates its addresses too, and one that cannot be validated returns 400. See Sales Tax Calculations for where this sits in the checkout sequence.

Exemption certificates

Exemption workflows differ more than the endpoints suggest. Kintsugi models an exemption as a separate object owned by a customer rather than a flag on one.

Solution: Create the customer first, then the exemption against its customerId, then attach the certificate. A one-off exemption that belongs to no customer record rides on the estimate line instead, as exempt: true.

Product tax code mapping

This is the part of the migration that is not mechanical. Neither provider's codes carry over.

Solution: Pull the current taxonomy from GET /products/categories and map your catalog to it rather than hardcoding values. Start with the SKUs that carry the most revenue, since a misclassification there costs the most, and check the result against the old system's rates during a parallel run.

Reconciling the two systems

During a parallel run you need to know which differences matter. Rounding and jurisdiction rollup differences are expected; a different taxability decision is not.

## Common Migration Challenges

Solution: Diff at the line level rather than the order total, so a difference points at a product or an exemption rather than at a number. Each estimate line echoes the externalId you sent, so lines match up one to one. Investigate any line where one engine taxes and the other does not before you switch traffic.

## Validation Checklist

Before cutover:

[ ] Estimation implemented, with address validation ahead of it

[ ] Transaction creation implemented, keyed on your existing order IDs, with the 202 handled as accepted rather than finished

[ ] Customers synced, with exemptions and certificates attached

[ ] Catalog synced, every product classified against Kintsugi's taxonomy

[ ] Historical transactions backfilled across each state's measurement window

[ ] Existing registrations recorded with their effective dates

[ ] Api-Version: 2026-07-21 sent on every request

[ ] Retry logic in place, and error logging that keeps each error's requestId

[ ] Parallel-run diffs explained at the line level, not just the total

[ ] Rollback plan documented, with the old system's records retained

## Post-Migration

Stop maintaining tax rules

Kintsugi tracks jurisdiction rule changes against your product categories, so a rate or taxability change needs nothing from you. Update a product only when your own classification changes.

Estimate freely

Because estimates record nothing, you can call the endpoint every time the cart or the address changes, then create the transaction once payment clears. See Integrating Kintsugi's API for where each call belongs.

## Next Steps

API Reference

Request formats and response structures in the API Reference.

Support

Migrating a large or unusual integration? Our Support Team has done this before.

---

Index of every page: https://docs.trykintsugi.com/llms.txt
