Self-Managed Cart Calculation
Self-Managed Cart Calculation
Calculate sales tax for a cart on behalf of a self-managed merchant, using the Ziptax rate engine.
This capability is in active development. Request bodies, responses, and supported fields may change before general availability. Contact support@zip.tax for early access.
POST /merchant/cart/calculate serves both compliance models. When the
merchantId you send belongs to a self-managed merchant,
Ziptax calculates the tax itself with its own rate engine — no TaxCloud
connection and no merchant credentials are involved. When it belongs to a
TaxCloud-connected merchant, the cart is
forwarded to TaxCloud instead; that path is documented in
Cart Tax Calculation.
You do not choose the engine. Ziptax routes on the merchant’s compliance model, so you send the same request body either way and never branch on merchant type when building a request.
Calculation only
Self-managed cart calculation is stateless. Nothing is persisted: no cart is stored, no order can be created from the result, and no exemption certificates are held. It answers one question — how much tax is due on these line items, right now — and returns.
Every other Merchant Transactions endpoint returns 403 for a self-managed
merchant:
Not supported on this path in this release:
Unsupported fields are rejected with 400, not silently ignored. If you
send a 20% order discount and Ziptax quietly returned tax on the undiscounted
amount, you would have a compliance problem you could not see. Strip these
fields before calling on behalf of a self-managed merchant.
Endpoint
Request body
The request contract is identical to the TaxCloud path. The limits below are the ones enforced on the self-managed path.
Cart object
The TaxCloud path allows up to 500 line items per cart, implied by the index
bound. The self-managed engine caps a cart at 250 line items. If you build
one request payload for both merchant types, size carts to the lower limit.
Address object
US addresses only.
Line item object
Taxability codes
This is the one place where the same request body means different things for the
two merchant models. On the self-managed path, tic is interpreted as a
Ziptax taxability code — the same vocabulary the
rate endpoints and the
TIC catalog use. TaxCloud’s own
codes are not interpreted here.
Migrating a merchant between models changes what tic means. TaxCloud’s
shipping codes (11010–11015) and the Colorado retail delivery fee code
(11098) carry no special meaning on the self-managed path. Send 11010 for a
shipping line on a self-managed cart and it is treated as an ordinary product
code and taxed at the general rate, rather than run through the shipping rules
below. Use 10001 and 11000 for self-managed merchants.
Shipping and handling
Whether freight and handling are taxable depends on the state the tax is sourced
to. Ziptax resolves this from the state’s freight taxability rule, the same
shipping.taxable value the rate endpoints return.
A non-taxable shipping line is still returned, with tax.rate and tax.amount
set to 0. Do not infer non-taxability from a missing line — every line item you
send comes back.
Which address the tax is based on
Ziptax picks the sourcing address from the cart’s origin and destination:
- Origin and destination are in different states → tax is based on the destination.
- Same state, and that state is origin-sourced (the state’s
sourcingRules.valueisO) → tax is based on the origin. - Otherwise → tax is based on the destination.
Most states are destination-sourced. A handful source intrastate sales to the origin, which is why an in-state sale can be taxed at the ship-from rate. See Collection Requirements and Nexus for background.
If one of the two addresses cannot be resolved, Ziptax falls back to the other and continues. If neither resolves, the request fails with the corresponding error code.
Rounding
For each line item:
Both tax.rate and tax.amount are rounded independently to 5 decimal
places (half away from zero). Rates are returned as decimal fractions, so
0.08875 means 8.875%.
Ziptax returns tax at 5 decimal places per line item rather than rounding to cents. Round to your currency’s precision at the point you total the cart, not per line, so the displayed total matches the sum of what you charge.
Example
Response
Returns the submitted carts with tax computed per line item.
How the response differs from the TaxCloud path
originalPrice is kept and set equal to price deliberately, so a parser that
handles the TaxCloud response works unchanged on a self-managed response.
cartId is not durable
Nothing is stored. cartId exists so you can correlate a response with the
request that produced it and trace it in your own logs. It is not a handle
to a saved cart:
- You cannot pass it to
order/create-from-cart— that endpoint returns403for self-managed merchants. - Recalculating the same cart produces a fresh calculation, not a lookup.
If you need the tax figures later, store the response on your side.
Worked examples
The rates below are illustrative. Actual rates come from the live rate tables at the time of the request.
Interstate sale, general goods plus shipping. Ship-from Mountain View, CA to New York, NY. The states differ, so tax is based on the destination. New York treats freight as taxable, so the shipping line is taxed at the same combined rate.
Intrastate sale in an origin-sourced state. Ship-from Austin, TX to Houston, TX. Same state, and Texas sources this sale to the origin, so the Austin rate applies even though the goods ship to Houston.
Non-taxable freight with separately stated handling. Destination in a state
whose freight rule is L — freight is exempt when separately stated, but the
labor portion is taxable.
Errors
A single invalid cart fails the whole request — partial results are not
returned, since a checkout cannot act on a half-calculated cart. Errors identify
the failing cart by cartId (or its index) and, where it applies, the itemId.
502 and 504 cannot occur on this path. There is no upstream service call to
be unreachable or time out — a distinction worth keeping in mind if your retry
logic keys on those statuses for the TaxCloud path.
Error codes
Failures from the rate engine map to these response codes:
Usage, limits, and entitlements
Geo request limits include the standard overage allowance. See Account Metrics to track usage against your plan, and Rate Limiting & Errors for backoff guidance.
Nexus is your responsibility on this path
Ziptax returns the tax rate for the sourced address whether or not the merchant has an obligation to collect there. It does not check the merchant’s registrations or nexus footprint before calculating.
Deciding where a merchant must collect stays with you and the merchant. Use Nexus Management and Economic Thresholds to track where a merchant has physical or economic nexus, and call this endpoint for the states where they have determined they need to collect. See Collection Requirements and Nexus for how obligations arise.
This is the same division of responsibility as the general-purpose rate endpoints. If you want registration, filing, and remittance handled for the merchant as well, that is the TaxCloud-connected model.
Moving a merchant between models
A self-managed merchant can be invited to TaxCloud later, which converts them to the connected model. Two things change for cart calculation when that happens, and neither is retroactive:
- The engine changes. Calculations run through TaxCloud from that point on. Rates for the same cart may differ from what the Ziptax engine returned before, which shows up as a discontinuity in your own reporting. Keep the calculation results you stored under each model distinguishable.
ticvalues change meaning. Switch shipping lines from10001to TaxCloud’s shipping codes when the merchant becomes connected. See Taxability codes.
Merchants are never migrated between models automatically.
Related
Create merchants that handle their own registration, filing, and remittance.
The same endpoint for a TaxCloud-connected merchant.
The Ziptax code catalog this path calculates against.
Shared conventions, errors, and usage.