Tax
Tax rates are configured per region and applied to orders at checkout. Use these endpoints to manage rate definitions and inspect tax breakdowns on orders.
List tax rates for the store
Admin API key (server-to-server). Issue via the dashboard at Settings → Authentication → API Keys. Plain-text key is shown once — store in a secret manager. Format: Authorization: Bearer brainerce_xxxxxxxxx.
In: header
curl -X GET "https://api.brainerce.com/api/v1/tax/rates"[
{
"id": "string",
"accountId": "string",
"storeId": "string",
"name": "California Sales Tax",
"country": "string",
"region": "string",
"postalCode": "string",
"rate": "8.25",
"taxType": "string",
"isInclusive": true,
"priority": 0,
"stackable": true,
"exceptionCountries": [
"string"
],
"isActive": true,
"taxClassId": "string",
"createdAt": "2019-08-24T14:15:22Z",
"updatedAt": "2019-08-24T14:15:22Z"
}
]{
"statusCode": 401,
"code": "UNAUTHORIZED",
"message": "Invalid or expired credential",
"timestamp": "2026-08-23T10:15:00.000Z",
"path": "/api/v1/tax/rates"
}{
"statusCode": 403,
"code": "INSUFFICIENT_SCOPE",
"message": "API key does not have the required scope: products:write",
"timestamp": "2026-08-23T10:15:00.000Z",
"path": "/api/v1/tax/rates"
}Create a new tax rate
Supports Idempotency-Key header for safe retries.
Admin API key (server-to-server). Issue via the dashboard at Settings → Authentication → API Keys. Plain-text key is shown once — store in a secret manager. Format: Authorization: Bearer brainerce_xxxxxxxxx.
In: header
Header Parameters
Client-supplied key (a UUID v4 is the recommended form, max 255 characters) that makes this mutation safe to retry.
- Replay window: 24 hours. The first response for a key is cached; a retry inside the window with the same request body returns the original status and body without re-running the handler.
- Reusing a key with a different body returns
409 Conflictwithcode: "IDEMPOTENCY_KEY_REUSED". The request fingerprint (method + path + body hash) is compared against the stored one; a mismatch is refused rather than served the old response. - Keys are scoped to the calling credential and store — two API keys may safely use the same key value.
- Error responses are cached too, so a retry of a request that failed validation returns the same 400 immediately.
- Sending this header on a
GETreturns400withcode: "IDEMPOTENCY_KEY_NOT_SUPPORTED"— GETs are already idempotent. - Support is per-route and this parameter is the authoritative signal: a key sent to a route that does not declare it is accepted and silently ignored.
length <= 255Display name shown on invoices and the storefront tax line.
Tax rate as a percentage (e.g. 8.5 for 8.5%). Range 0 to 100.
0 <= value <= 100ISO 3166-1 alpha-2 country code this rate applies to. REQUIRED — there is no global fallback rate. TaxRate.country is NOT NULL in the schema, so a country-less rate cannot be stored at all; omitting this is a 400, not a catch-all. A rate that must cover several countries names one here and lists the rest in exceptionCountries.
State/province code (ISO 3166-2 subdivision part, e.g. CA for California). Narrows the match within country.
Exact postal code, compared case-insensitively with spaces and hyphens ignored. Wildcards, prefixes (941*) and ranges (94100-94199) are NOT supported, and match nothing. Narrows the match within region.
Tax category label (e.g. sales, vat, gst). Shown on invoices and used for reporting; does not affect calculation.
When true, prices are tax-inclusive (VAT-style): the rate is backed out of the displayed price instead of added on top.
falseTiebreak between rates of EQUAL specificity: the LOWER number wins. Specificity is decided first (a postal-code rate beats a region rate, which beats a country-wide rate), so priority only matters between rates scoped the same way.
Whether this rate participates in calculation. Defaults to true.
trueAdditional ISO country codes where this rate also applies (in addition to the same-country rule). Useful for trade blocs that share a tax (e.g. EU reverse-charge schemes).
Charge this rate TOGETHER WITH the other stackable rates that match the same address, instead of the single most specific one. This is how Canada works: GST 5% (country-level) + PST/QST (province-level), both stackable, both charged on the same pre-tax base — the buyer sees two tax lines. Leave false (the default) and the historic rule applies: the most specific matching rate wins alone and every other match is discarded. An HST province is a single non-stackable province row, so it wins at 13% without adding GST. Stacking is additive, never tax-on-tax, and never crosses a tax class: a class-specific rate replaces the Standard rates rather than adding to them, so a class that needs GST + reduced QST needs both rows entered in that class.
falseTax class this rate applies to. Omit (or null) for the Standard rate that applies to products without an explicit class.
curl -X POST "https://api.brainerce.com/api/v1/tax/rates" \ -H "Idempotency-Key: string" \ -H "Content-Type: application/json" \ -d '{ "name": "California Sales Tax", "rate": 8.5, "country": "US" }'{
"id": "string",
"accountId": "string",
"storeId": "string",
"name": "California Sales Tax",
"country": "string",
"region": "string",
"postalCode": "string",
"rate": "8.25",
"taxType": "string",
"isInclusive": true,
"priority": 0,
"stackable": true,
"exceptionCountries": [
"string"
],
"isActive": true,
"taxClassId": "string",
"createdAt": "2019-08-24T14:15:22Z",
"updatedAt": "2019-08-24T14:15:22Z"
}{
"statusCode": 400,
"code": "VALIDATION_FAILED",
"message": "name should not be empty, price must be a positive number",
"timestamp": "2026-08-23T10:15:00.000Z",
"path": "/api/v1/tax/rates"
}{
"statusCode": 401,
"code": "UNAUTHORIZED",
"message": "Invalid or expired credential",
"timestamp": "2026-08-23T10:15:00.000Z",
"path": "/api/v1/tax/rates"
}{
"statusCode": 403,
"code": "INSUFFICIENT_SCOPE",
"message": "API key does not have the required scope: products:write",
"timestamp": "2026-08-23T10:15:00.000Z",
"path": "/api/v1/tax/rates"
}Get a single tax rate by ID
Admin API key (server-to-server). Issue via the dashboard at Settings → Authentication → API Keys. Plain-text key is shown once — store in a secret manager. Format: Authorization: Bearer brainerce_xxxxxxxxx.
In: header
Path Parameters
Tax Rate ID
curl -X GET "https://api.brainerce.com/api/v1/tax/rates/string"{
"id": "string",
"accountId": "string",
"storeId": "string",
"name": "California Sales Tax",
"country": "string",
"region": "string",
"postalCode": "string",
"rate": "8.25",
"taxType": "string",
"isInclusive": true,
"priority": 0,
"stackable": true,
"exceptionCountries": [
"string"
],
"isActive": true,
"taxClassId": "string",
"createdAt": "2019-08-24T14:15:22Z",
"updatedAt": "2019-08-24T14:15:22Z"
}{
"statusCode": 401,
"code": "UNAUTHORIZED",
"message": "Invalid or expired credential",
"timestamp": "2026-08-23T10:15:00.000Z",
"path": "/api/v1/tax/rates/{rateId}"
}{
"statusCode": 403,
"code": "INSUFFICIENT_SCOPE",
"message": "API key does not have the required scope: products:write",
"timestamp": "2026-08-23T10:15:00.000Z",
"path": "/api/v1/tax/rates/{rateId}"
}{
"statusCode": 404,
"code": "RESOURCE_NOT_FOUND",
"message": "Resource not found",
"timestamp": "2026-08-23T10:15:00.000Z",
"path": "/api/v1/tax/rates/{rateId}"
}Update a tax rate
Supports Idempotency-Key header for safe retries.
Admin API key (server-to-server). Issue via the dashboard at Settings → Authentication → API Keys. Plain-text key is shown once — store in a secret manager. Format: Authorization: Bearer brainerce_xxxxxxxxx.
In: header
Path Parameters
Tax Rate ID
Header Parameters
Client-supplied key (a UUID v4 is the recommended form, max 255 characters) that makes this mutation safe to retry.
- Replay window: 24 hours. The first response for a key is cached; a retry inside the window with the same request body returns the original status and body without re-running the handler.
- Reusing a key with a different body returns
409 Conflictwithcode: "IDEMPOTENCY_KEY_REUSED". The request fingerprint (method + path + body hash) is compared against the stored one; a mismatch is refused rather than served the old response. - Keys are scoped to the calling credential and store — two API keys may safely use the same key value.
- Error responses are cached too, so a retry of a request that failed validation returns the same 400 immediately.
- Sending this header on a
GETreturns400withcode: "IDEMPOTENCY_KEY_NOT_SUPPORTED"— GETs are already idempotent. - Support is per-route and this parameter is the authoritative signal: a key sent to a route that does not declare it is accepted and silently ignored.
length <= 255Display name shown on invoices and the storefront tax line.
Tax rate as a percentage (0 to 100).
0 <= value <= 100ISO 3166-1 alpha-2 country code. Optional only in the PATCH sense: omit it and the rate keeps the country it already has. It cannot be cleared. TaxRate.country is NOT NULL, there is no global fallback rate, and sending null is rejected. A rate covering several countries names one here and lists the rest in exceptionCountries.
State/province subdivision code.
Exact postal code (case-insensitive, spaces and hyphens ignored). Prefixes and ranges are NOT supported.
Tax category label (e.g. sales, vat, gst).
Treat prices as tax-inclusive (VAT-style).
Tiebreak between rates of EQUAL specificity: the LOWER number wins. More specific rates (postal > region > country) win regardless of priority.
Whether this rate participates in calculation.
Additional ISO country codes where this rate also applies.
Charge this rate together with the other stackable rates matching the same address (Canada GST + PST/QST) instead of the single most specific one. Flipping an existing rate to true changes what future checkouts collect — check the other rates on the store first, because rates that used to be mutually exclusive will start being charged together.
Tax class this rate applies to. Null/omit = Standard (applies to unclassed products).
curl -X PATCH "https://api.brainerce.com/api/v1/tax/rates/string" \ -H "Idempotency-Key: string" \ -H "Content-Type: application/json" \ -d '{}'{
"id": "string",
"accountId": "string",
"storeId": "string",
"name": "California Sales Tax",
"country": "string",
"region": "string",
"postalCode": "string",
"rate": "8.25",
"taxType": "string",
"isInclusive": true,
"priority": 0,
"stackable": true,
"exceptionCountries": [
"string"
],
"isActive": true,
"taxClassId": "string",
"createdAt": "2019-08-24T14:15:22Z",
"updatedAt": "2019-08-24T14:15:22Z"
}{
"statusCode": 400,
"code": "VALIDATION_FAILED",
"message": "name should not be empty, price must be a positive number",
"timestamp": "2026-08-23T10:15:00.000Z",
"path": "/api/v1/tax/rates/{rateId}"
}{
"statusCode": 401,
"code": "UNAUTHORIZED",
"message": "Invalid or expired credential",
"timestamp": "2026-08-23T10:15:00.000Z",
"path": "/api/v1/tax/rates/{rateId}"
}{
"statusCode": 403,
"code": "INSUFFICIENT_SCOPE",
"message": "API key does not have the required scope: products:write",
"timestamp": "2026-08-23T10:15:00.000Z",
"path": "/api/v1/tax/rates/{rateId}"
}{
"statusCode": 404,
"code": "RESOURCE_NOT_FOUND",
"message": "Resource not found",
"timestamp": "2026-08-23T10:15:00.000Z",
"path": "/api/v1/tax/rates/{rateId}"
}Delete a tax rate
Supports Idempotency-Key header for safe retries.
Admin API key (server-to-server). Issue via the dashboard at Settings → Authentication → API Keys. Plain-text key is shown once — store in a secret manager. Format: Authorization: Bearer brainerce_xxxxxxxxx.
In: header
Path Parameters
Tax Rate ID
Header Parameters
Client-supplied key (a UUID v4 is the recommended form, max 255 characters) that makes this mutation safe to retry.
- Replay window: 24 hours. The first response for a key is cached; a retry inside the window with the same request body returns the original status and body without re-running the handler.
- Reusing a key with a different body returns
409 Conflictwithcode: "IDEMPOTENCY_KEY_REUSED". The request fingerprint (method + path + body hash) is compared against the stored one; a mismatch is refused rather than served the old response. - Keys are scoped to the calling credential and store — two API keys may safely use the same key value.
- Error responses are cached too, so a retry of a request that failed validation returns the same 400 immediately.
- Sending this header on a
GETreturns400withcode: "IDEMPOTENCY_KEY_NOT_SUPPORTED"— GETs are already idempotent. - Support is per-route and this parameter is the authoritative signal: a key sent to a route that does not declare it is accepted and silently ignored.
length <= 255curl -X DELETE "https://api.brainerce.com/api/v1/tax/rates/string" \ -H "Idempotency-Key: string"{
"statusCode": 401,
"code": "UNAUTHORIZED",
"message": "Invalid or expired credential",
"timestamp": "2026-08-23T10:15:00.000Z",
"path": "/api/v1/tax/rates/{rateId}"
}{
"statusCode": 403,
"code": "INSUFFICIENT_SCOPE",
"message": "API key does not have the required scope: products:write",
"timestamp": "2026-08-23T10:15:00.000Z",
"path": "/api/v1/tax/rates/{rateId}"
}{
"statusCode": 404,
"code": "RESOURCE_NOT_FOUND",
"message": "Resource not found",
"timestamp": "2026-08-23T10:15:00.000Z",
"path": "/api/v1/tax/rates/{rateId}"
}