> For clean Markdown of any page, append .md to the page URL. > For a complete documentation index, see https://docs.zip.tax/v-6-0/guides/merchant-compliance-solutions/transactions/llms.txt. > For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.zip.tax/_mcp/server. # Merchant Transactions Merchant Transactions extend [Merchant Management](merchant-management) with the day-to-day tax operations your merchants need: calculating tax on a cart, recording and updating orders, storing exemption certificates, and issuing refunds. Every operation runs against a single [TaxCloud-connected merchant](taxcloud-connected-merchants), so your platform can drive full transaction-level compliance without asking each merchant to integrate separately. ## Before you begin You need two things: 1. **A Ziptax API key.** See [Authentication](../reference/authentication) for where to get one and how to send it. 2. **A TaxCloud-connected merchant with compliance credentials on file.** Create the merchant and store its credentials as described in [TaxCloud-connected merchants](taxcloud-connected-merchants). A merchant without valid credentials returns `404` (`credentials not found`) on every transaction call. > **Info** > > [Self-managed merchants](self-managed-merchants) (status > `external_compliance`) have no TaxCloud credentials. Cart calculation still > works for them — Ziptax calculates it directly, as described in > [Self-Managed Cart Calculation](self-managed-cart-calculation) — but every > other endpoint on this page returns `403` until the merchant is invited to > TaxCloud and [connected](taxcloud-connected-merchants). ## Common conventions All Merchant Transactions endpoints share the same shape. | Convention | Detail | | ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Method** | `POST` for every endpoint, including reads and deletes. Parameters travel in the JSON body, not the URL. | | **Base URL** | `https://api.zip-tax.com` | | **Authentication** | `X-API-KEY` header only. Unlike the rate-lookup endpoints, these routes do **not** accept a `?key=` query parameter. | | **Merchant target** | Every request body includes a `merchantId` (UUID) identifying the TaxCloud-connected merchant to act on. | | **Content type** | `application/json` on any request that sends a body. | | **Environment** | Runs against the merchant's **Live** environment by default. Set the `X-ENV: TEST` header to use the **Test** (sandbox) environment instead. See [Test vs Live environments](#test-vs-live-environments). | > **Info** > > You never send compliance credentials in the request. Ziptax resolves them > server-side from the merchant's stored TaxCloud credentials, so your > integration only ever handles your own Ziptax API key. ## Test vs Live environments Each TaxCloud-connected merchant has two environments: **Live** (production) and **Test** (sandbox). By default, every request runs against Live. To run a request against the merchant's Test environment instead — for example, to validate your integration without creating real orders or affecting tax filings — set the `X-ENV` header to `TEST`. #### cURL ```bash curl -X POST "https://api.zip-tax.com/merchant/cart/calculate" \ -H "X-API-KEY: YOUR_API_KEY" \ -H "X-ENV: TEST" \ -H "Content-Type: application/json" \ -d '{ "merchantId": "6b3c1f5e-2a8d-4c9b-9f2e-1d7a4b6c8e10", "items": [ { "customerId": "customer-453", "currency": { "currencyCode": "USD" }, "origin": { "line1": "1 Market St", "city": "San Francisco", "state": "CA", "zip": "94105" }, "destination": { "line1": "200 Spectrum Center Dr", "city": "Irvine", "state": "CA", "zip": "92618" }, "lineItems": [ { "index": 0, "itemId": "sku-1001", "price": 49.99, "quantity": 2 } ] } ] }' ``` `X-ENV` accepts `LIVE` (the default) or `TEST`, and applies to every Merchant Transactions endpoint. Omit it to run against Live. > **Info** > > The Test environment is isolated from Live: carts, orders, refunds, and > certificates created with `X-ENV: TEST` never affect Live data and are never > filed. Make sure the merchant's Test environment is configured before you use it. ## Operations #### [Cart Tax Calculation](cart) Calculate tax for a cart of items before you record an order. #### [Orders](orders) Create orders from a cart or directly, then retrieve and update them. #### [Exemption Certificates](exemption-certificates) Store, retrieve, list, and delete customer exemption certificates. #### [Refunds](refunds) Refund all or part of a previously recorded order. The full set of endpoints, and which merchants each one serves: | Operation | Method & path | Self-managed merchant | | ---------------------------- | --------------------------------------- | ------------------------------------------ | | Calculate cart tax | `POST /merchant/cart/calculate` | [Available](self-managed-cart-calculation) | | Create order from cart | `POST /merchant/order/create-from-cart` | `403` | | Create order | `POST /merchant/order/create` | `403` | | Get order | `POST /merchant/order/get` | `403` | | Update order | `POST /merchant/order/update` | `403` | | Create exemption certificate | `POST /merchant/cert/create` | `403` | | Get exemption certificate | `POST /merchant/cert/get` | `403` | | List exemption certificates | `POST /merchant/cert/list` | `403` | | Delete exemption certificate | `POST /merchant/cert/delete` | `403` | | Create refund | `POST /merchant/refund/create` | `403` | Every operation except cart calculation stores state — an order, a certificate, a refund — which a self-managed merchant has no compliance account to store it in. Those calls fail fast with `403` rather than returning a misleading `404 credentials not found`. ## Responses and errors A successful call returns the requested resource as JSON (a cart calculation, an order, a certificate, or a refund). Errors come in two shapes: **Ziptax-level errors** are returned before the operation runs, when the request can't be authorized or routed to a valid merchant. They use a simple envelope: ```json { "status": "error", "message": "merchant not found" } ``` **Operation-level errors** are returned when the request reaches the compliance service but fails validation or processing. They carry more structured detail: ```json { "status": 422, "title": "Unprocessable Entity", "detail": "One or more line items are invalid.", "error": [] } ``` | HTTP status | When it happens | | --------------------------- | --------------------------------------------------------------------------------------------------------------- | | `400` | Malformed JSON, or a missing/invalid `merchantId` or resource id. | | `401` | Missing, invalid, or inactive API key. | | `403` | Merchant not found or not owned by your account, or the operation is not available for a self-managed merchant. | | `404` | Merchant has no compliance credentials on file. | | `429` | Rate limit exceeded (response code `108`). | | `422` and other `4xx`/`5xx` | Operation-level validation or processing error (see the structured shape above). | | `502` / `504` | The compliance service was unreachable or the request timed out. | ## Usage and rate limits Merchant Transactions are metered **separately** from your tax-lookup (rate request) usage. Each transaction call counts as one merchant request against your plan's merchant allowance, and every call is subject to per-key rate limiting. Exceeding your rate returns `429` with response code `108`, the same as the rest of the API. See [Rate Limiting & Errors](../reference/rate-limiting-errors) for backoff guidance. > **Info** > > Every request that passes Ziptax's up-front checks is metered exactly once, > including calls that end in `502`/`504`. Requests rejected up front (bad key, > unknown merchant, missing credentials, malformed body, rate limit) do not > count against your merchant usage. ## Retries and idempotency Read operations are safe to retry. Write operations are **not** retried automatically, and you should not blindly retry them either: a retried create can produce a duplicate order, certificate, or refund. | Operation | Safe to retry? | | -------------------------------------------------------------------------------------- | ----------------------------------------------------------- | | `order/get`, `cert/get`, `cert/list` | Yes, these are reads. | | `cart/calculate` | Recalculates; retry only if you did not receive a response. | | `order/create-from-cart`, `order/create`, `order/update`, `cert/create`, `cert/delete` | No, may duplicate or clobber. | | `refund/create` | **No.** A duplicate refund is a financial incident. | > **Warning** > > If a write call times out (`504`) or the service is briefly unavailable > (`502`), don't immediately retry. First confirm whether the operation > succeeded, for example by fetching the order with > [`order/get`](orders#get-an-order), then retry only if it did not. `502` and `504` only arise on the TaxCloud-connected path, where Ziptax calls an upstream service. Cart calculation for a [self-managed merchant](self-managed-cart-calculation) has no upstream call, so those statuses never appear for it. ## Related #### [Merchant Management](merchant-management) Create merchants and choose between the two compliance models. #### [Self-Managed Cart Calculation](self-managed-cart-calculation) The one transaction endpoint available to a self-managed merchant. #### [TaxCloud-Connected Merchants](taxcloud-connected-merchants) Connect merchants to TaxCloud and store the credentials these operations depend on. #### [Authentication](../reference/authentication) How to obtain and send your Ziptax API key. #### [Account Metrics](../reference/account-metrics) Track your usage against plan quotas. > Calculate cart tax, record orders, manage exemption certificates, and issue refunds on behalf of your TaxCloud-connected merchants.