API ReferenceEndpointsPublic API (v1)

Public API (v1)

The external integrator REST API under /v1/*. Stable, versioned, auth via api-key (issue from the dashboard at Settings → Authentication → API Keys). Use this when calling Brainerce from outside the dashboard — a headless storefront, ERP sync, BI pipeline, etc. Every endpoint requires a scope (products:read, orders:write, …) on the key. The /v1/ prefix is a versioning anchor — there is no v2 planned, but a future breaking change would ship under /v2/ without disrupting /v1/ integrators.

Get store information

GET
/v1/store
AuthorizationBearer <token>

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/store"
{
  "id": "string",
  "accountId": "string",
  "connectedPlatforms": [
    "string"
  ]
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/store"
}

{
  "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/store"
}

List products with pagination

GET
/v1/products
AuthorizationBearer <token>

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

Query Parameters

page?number
limit?number
search?string
status?string
type?string
sortBy?string
sortOrder?string
regionId?string
curl -X GET "https://api.brainerce.com/api/v1/products?page=0&limit=0&search=string&status=string&type=string&sortBy=string&sortOrder=string&regionId=string"
{
  "data": [
    {
      "id": "string",
      "name": "string",
      "slug": "string",
      "localeSlugs": {
        "property1": "string",
        "property2": "string"
      },
      "description": "string",
      "descriptionFormat": "text",
      "basePrice": "49.90",
      "salePrice": "string",
      "costPrice": "string",
      "priceMin": "string",
      "priceMax": "string",
      "priceVaries": true,
      "displayPrice": "string",
      "displaySalePrice": "string",
      "displayPriceMin": "string",
      "displayPriceMax": "string",
      "displayCurrency": "string",
      "gtin": "string",
      "mpn": "string",
      "salePriceStartsAt": "2019-08-24T14:15:22Z",
      "salePriceEndsAt": "2019-08-24T14:15:22Z",
      "shippingWeightValue": 0,
      "shippingWeightUnit": "string",
      "shippingLengthValue": 0,
      "shippingWidthValue": 0,
      "shippingHeightValue": 0,
      "shippingDimensionUnit": "string",
      "metaDescription": "string",
      "status": "string",
      "sku": "string",
      "type": "SIMPLE",
      "kitPricingMode": "FIXED",
      "kitDiscountValue": 0,
      "isDownloadable": true,
      "downloadData": {
        "files": [
          {
            "id": "string",
            "name": "string",
            "url": "string",
            "storageKey": "string",
            "size": 0,
            "mimeType": "string"
          }
        ],
        "downloadLimit": 0,
        "downloadExpiry": 0
      },
      "needsSync": true,
      "lastSyncedAt": "2019-08-24T14:15:22Z",
      "menuOrder": 0,
      "taxBehavior": "string",
      "createdAt": "2019-08-24T14:15:22Z",
      "updatedAt": "2019-08-24T14:15:22Z",
      "inventory": {
        "trackingMode": "TRACKED",
        "total": 42,
        "reserved": 3,
        "available": 39,
        "inStock": true,
        "canPurchase": true,
        "lastInventorySyncAt": "2019-08-24T14:15:22Z"
      },
      "variants": [
        {
          "id": "string",
          "productId": "string",
          "sku": "string",
          "name": "string",
          "attributes": {},
          "price": "19.90",
          "salePrice": "string",
          "costPrice": "string",
          "displayPrice": "string",
          "displaySalePrice": "string",
          "displayCurrency": "string",
          "image": "string",
          "position": 0,
          "status": "string",
          "inventory": {
            "trackingMode": "TRACKED",
            "total": 42,
            "reserved": 3,
            "available": 39,
            "inStock": true,
            "canPurchase": true,
            "lastInventorySyncAt": "2019-08-24T14:15:22Z"
          },
          "isDownloadable": true,
          "downloadData": {
            "files": [
              {
                "id": "string",
                "name": "string",
                "url": "string",
                "storageKey": "string",
                "size": 0,
                "mimeType": "string"
              }
            ],
            "downloadLimit": 0,
            "downloadExpiry": 0
          },
          "shippingClassId": "string",
          "createdAt": "2019-08-24T14:15:22Z",
          "updatedAt": "2019-08-24T14:15:22Z"
        }
      ],
      "images": [
        {
          "url": "string",
          "position": 0,
          "isMain": true,
          "alt": "string",
          "id": "string",
          "key": "string",
          "thumbnailUrl": "string",
          "width": 0,
          "height": 0,
          "size": 0,
          "mimeType": "string",
          "createdAt": "2019-08-24T14:15:22Z",
          "source": "string",
          "importStatus": "string",
          "importError": "string"
        }
      ],
      "channels": {},
      "translations": {},
      "categories": [
        {
          "id": "string",
          "name": "string",
          "taxBehavior": "string"
        }
      ],
      "brands": [
        {
          "id": "clx1a2b3c4d5e6f7g8h9",
          "name": "Summer Collection"
        }
      ],
      "attributes": [
        "string"
      ],
      "productAttributeOptions": [
        {
          "id": "string",
          "attributeId": "string",
          "attributeOptionId": "string",
          "platform": "string",
          "attribute": {
            "id": "string",
            "name": "string",
            "displayType": "string"
          },
          "attributeOption": {
            "id": "string",
            "name": "string",
            "value": "string",
            "swatchColor": "string",
            "swatchColor2": "string",
            "swatchImageUrl": "string"
          }
        }
      ],
      "tags": [
        "string"
      ],
      "metafields": [
        {
          "id": "string",
          "definitionId": "string",
          "definitionKey": "string",
          "definitionName": "string",
          "type": "string",
          "value": "string",
          "variantId": "string"
        }
      ],
      "customizationFields": [
        {
          "definitionId": "string",
          "name": "string",
          "key": "string",
          "description": "string",
          "type": "string",
          "required": true,
          "minLength": 0,
          "maxLength": 0,
          "minValue": 0,
          "maxValue": 0,
          "enumValues": [
            "string"
          ],
          "defaultValue": "string",
          "position": 0
        }
      ],
      "channelPublishes": [
        {
          "salesChannel": {
            "id": "clx1a2b3c4d5e6f7g8h9",
            "name": "My storefront",
            "connectionId": "vc_9f3a2b1c8d7e6f5a"
          },
          "connection": {
            "id": "clx1a2b3c4d5e6f7g8h9",
            "name": "My storefront",
            "connectionId": "vc_9f3a2b1c8d7e6f5a"
          }
        }
      ],
      "vibeCodedPublishes": [
        {
          "salesChannel": {
            "id": "clx1a2b3c4d5e6f7g8h9",
            "name": "My storefront",
            "connectionId": "vc_9f3a2b1c8d7e6f5a"
          },
          "connection": {
            "id": "clx1a2b3c4d5e6f7g8h9",
            "name": "My storefront",
            "connectionId": "vc_9f3a2b1c8d7e6f5a"
          }
        }
      ],
      "channelSync": {},
      "shippingClassId": "string",
      "shippingClass": {
        "id": "string",
        "name": "string"
      },
      "crossSells": [
        {
          "id": "clx1a2b3c4d5e6f7g8h9",
          "name": "Summer Collection"
        }
      ],
      "upsells": [
        {
          "id": "clx1a2b3c4d5e6f7g8h9",
          "name": "Summer Collection"
        }
      ],
      "related": [
        {
          "id": "clx1a2b3c4d5e6f7g8h9",
          "name": "Summer Collection"
        }
      ],
      "bundleOfferCount": 0,
      "orderBumpCount": 0,
      "avgRating": 0,
      "reviewCount": 0,
      "seoScore": {
        "score": 0,
        "band": "excellent",
        "hasUnresolvedError": true
      },
      "modifierGroups": [
        {
          "attachmentId": "string",
          "groupId": "string",
          "variantId": "string",
          "position": 0,
          "minOverride": 0,
          "maxOverride": 0,
          "freeQuantityOverride": 0,
          "requiredOverride": true,
          "freeAllocationPolicyOverride": "EXPENSIVE_FREE",
          "defaultModifierIds": [
            "string"
          ],
          "name": "string",
          "internalName": "string",
          "description": "string",
          "translations": {},
          "selectionType": "SINGLE",
          "minSelections": 0,
          "maxSelections": 0,
          "freeQuantity": 0,
          "required": true,
          "freeAllocationPolicy": "EXPENSIVE_FREE",
          "status": "string",
          "modifiers": [
            {
              "id": "string",
              "name": "string",
              "description": "string",
              "translations": {},
              "priceDelta": "2.50",
              "sku": "string",
              "image": {},
              "position": 0,
              "isDefault": true,
              "available": true,
              "referencedProductId": "string",
              "status": "string"
            }
          ]
        }
      ]
    }
  ],
  "meta": {
    "page": 1,
    "limit": 20,
    "total": 137,
    "totalPages": 7
  }
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/products"
}

{
  "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/products"
}

{
  "statusCode": 429,
  "code": "RATE_LIMITED",
  "message": "Too many requests",
  "details": {
    "retryAfterSeconds": 12
  },
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/products"
}

Create a new product

Supports Idempotency-Key header for safe retries.

POST
/v1/products
AuthorizationBearer <token>

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

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
namestring

Display name of the product. Required.

slug?string

URL slug. Letters, numbers, and hyphens only. Auto-generated from name when omitted.

sku?string

Stock keeping unit, meaning your internal product code.

gtin?string

Global Trade Item Number (universal barcode: EAN/UPC/ISBN). Required by Google Shopping, Facebook Catalog, and TikTok Shop on most categories. Validated against the checksum digit.

mpn?string

Manufacturer Part Number, paired with brand when no GTIN exists. Max 70 chars.

externalId?string

Your own stable identifier for this product in the SOURCE system (supplier feed, legacy store, ERP). Unique per store. Its purpose is safe retries on bulk imports: re-sending a batch after a timeout matches on this value and skips the product instead of creating a duplicate. Not a marketplace id: those are managed per-connection and are not settable here.

Lengthlength <= 191
salePriceStartsAt?string

Sale-price effective window start (ISO 8601). Google Merchant Center sale_price_effective_date. Both this and salePriceEndsAt must be set together, or neither applies.

salePriceEndsAt?string

Sale-price effective window end (ISO 8601). See salePriceStartsAt.

shippingWeightValue?number

Shipping weight numeric value. Paired with shippingWeightUnit.

shippingWeightUnit?string

Shipping weight unit.

Value in"kg" | "lb" | "g" | "oz"
shippingLengthValue?number

Shipping package length. Paired with shippingDimensionUnit.

shippingWidthValue?number

Shipping package width. Paired with shippingDimensionUnit.

shippingHeightValue?number

Shipping package height. Paired with shippingDimensionUnit.

shippingDimensionUnit?string

Unit shared by shippingLengthValue/shippingWidthValue/shippingHeightValue.

Value in"cm" | "in"
metaDescription?string

SEO meta description (≤160 chars). Used by Google Search snippets, social link previews (WhatsApp/Telegram/Twitter), email previews, and schema.org JSON-LD. Falls back to a stripped/truncated description when omitted.

Lengthlength <= 160
description?string

Long-form product description. Accepts HTML, and the backend strips dangerous tags/attributes (<script>, inline event handlers, <iframe>, …) before saving. Hard cap of 65 535 characters.

Lengthlength <= 65535
basePricenumber

Base list price in the store currency. Must be ≥ 0.

Range0 <= value
salePrice?number

Sale price (must be < basePrice). When present, the storefront displays it as the active price with basePrice shown struck-through.

Range0 <= value
costPrice?number

Internal cost (what you pay your supplier). Used for margin reporting only, and never shown to shoppers.

Range0 <= value
status?string

Publish state. active = visible to shoppers; draft = only visible in the dashboard.

Value in"active" | "draft"
typestring

Product structure. SIMPLE = single SKU; VARIABLE = parent of multiple variants (size, color, …); KIT = a bundle priced from component products. A KIT may be created with no components yet — it is simply not purchasable until it has at least one.

Value in"SIMPLE" | "VARIABLE" | "KIT"
kitPricingMode?string

How a KIT is priced. FIXED (default) — you set basePrice and it stays put. SUM — the kit costs exactly what its components cost, recomputed on every read, so a component going on sale lowers the kit price on its own. SUM_MINUS_PERCENT — that sum less kitDiscountValue percent. Ignored on SIMPLE and VARIABLE products.

Value in"FIXED" | "SUM" | "SUM_MINUS_PERCENT"
kitDiscountValue?number

Percent off the component sum, 0-100. Required when kitPricingMode is SUM_MINUS_PERCENT; ignored otherwise.

Range0 <= value <= 100
isDownloadable?boolean

If true, the product is a digital download, so checkout issues a signed URL after payment instead of triggering shipping.

downloadData?object

Digital-download configuration. Required when isDownloadable=true. files[] are uploaded via /v1/media. downloadLimit = max downloads per order (null = unlimited). downloadExpiry = hours until the signed URL expires (null = no expiry).

Empty Object

inventory?object

Inventory configuration for SIMPLE products. total = current stock count; trackingMode: TRACKED (default) decrements on each sale and blocks oversells; UNLIMITED accepts any order; DISABLED hides stock UI. Ignored for VARIABLE products, whose variants carry their own inventory.

Empty Object

variants?array<CreateVariantDto>

Array of variants. Required (and non-empty) for VARIABLE products, at most 500. Each variant carries its own price, SKU, and inventory.

categories?array<string>

Array of existing category IDs (from GET /categories / list_categories). Unknown/cross-store IDs are rejected with 400. To assign by name, creating the category if it does not exist, use categoryNames instead.

categoryNames?array<string>

Category names to assign. Each name is matched case-insensitively to an existing category and auto-created if none exists, so there is no need to look up IDs first. Merged with any IDs passed in categories.

brands?array<string>

Array of existing brand IDs. Unknown/cross-store IDs are rejected with 400. To assign by name, creating the brand if it does not exist, use brandNames instead.

brandNames?array<string>

Brand names to assign. Each name is matched case-insensitively to an existing brand and auto-created if none exists. Merged with any IDs passed in brands.

tags?array<string>

Free-form tags for filtering and segmentation.

images?array<string>

Product images. Each entry: { url, position, isMain }. Upload via /v1/media first to get a signed URL.

platforms?array<string>

Sales channels this product should publish to. Each platform syncs the product separately and can have per-channel overrides (see channels).

channels?object

Per-platform field overrides. Keyed by platform code, value is a partial product object that overrides the canonical fields for that channel only.

Empty Object

taxBehavior?string

Tax category. taxable is the default; exempt skips all tax calculation.

Value in"taxable" | "exempt"
translations?object

Multi-language overrides. Keyed by ISO locale code (he, ar, es, …). Each value is a partial product with translated name / description / metaDescription. Falls back to the canonical fields for missing locales.

Empty Object

curl -X POST "https://api.brainerce.com/api/v1/products" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{    "name": "Premium Wireless Headphones",    "basePrice": 299.99,    "type": "SIMPLE"  }'
{
  "id": "string",
  "name": "string",
  "slug": "string",
  "localeSlugs": {
    "property1": "string",
    "property2": "string"
  },
  "description": "string",
  "descriptionFormat": "text",
  "basePrice": "49.90",
  "salePrice": "string",
  "costPrice": "string",
  "priceMin": "string",
  "priceMax": "string",
  "priceVaries": true,
  "displayPrice": "string",
  "displaySalePrice": "string",
  "displayPriceMin": "string",
  "displayPriceMax": "string",
  "displayCurrency": "string",
  "gtin": "string",
  "mpn": "string",
  "salePriceStartsAt": "2019-08-24T14:15:22Z",
  "salePriceEndsAt": "2019-08-24T14:15:22Z",
  "shippingWeightValue": 0,
  "shippingWeightUnit": "string",
  "shippingLengthValue": 0,
  "shippingWidthValue": 0,
  "shippingHeightValue": 0,
  "shippingDimensionUnit": "string",
  "metaDescription": "string",
  "status": "string",
  "sku": "string",
  "type": "SIMPLE",
  "kitPricingMode": "FIXED",
  "kitDiscountValue": 0,
  "isDownloadable": true,
  "downloadData": {
    "files": [
      {
        "id": "string",
        "name": "string",
        "url": "string",
        "storageKey": "string",
        "size": 0,
        "mimeType": "string"
      }
    ],
    "downloadLimit": 0,
    "downloadExpiry": 0
  },
  "needsSync": true,
  "lastSyncedAt": "2019-08-24T14:15:22Z",
  "menuOrder": 0,
  "taxBehavior": "string",
  "createdAt": "2019-08-24T14:15:22Z",
  "updatedAt": "2019-08-24T14:15:22Z",
  "inventory": {
    "trackingMode": "TRACKED",
    "total": 42,
    "reserved": 3,
    "available": 39,
    "inStock": true,
    "canPurchase": true,
    "lastInventorySyncAt": "2019-08-24T14:15:22Z"
  },
  "variants": [
    {
      "id": "string",
      "productId": "string",
      "sku": "string",
      "name": "string",
      "attributes": {},
      "price": "19.90",
      "salePrice": "string",
      "costPrice": "string",
      "displayPrice": "string",
      "displaySalePrice": "string",
      "displayCurrency": "string",
      "image": "string",
      "position": 0,
      "status": "string",
      "inventory": {
        "trackingMode": "TRACKED",
        "total": 42,
        "reserved": 3,
        "available": 39,
        "inStock": true,
        "canPurchase": true,
        "lastInventorySyncAt": "2019-08-24T14:15:22Z"
      },
      "isDownloadable": true,
      "downloadData": {
        "files": [
          {
            "id": "string",
            "name": "string",
            "url": "string",
            "storageKey": "string",
            "size": 0,
            "mimeType": "string"
          }
        ],
        "downloadLimit": 0,
        "downloadExpiry": 0
      },
      "shippingClassId": "string",
      "createdAt": "2019-08-24T14:15:22Z",
      "updatedAt": "2019-08-24T14:15:22Z"
    }
  ],
  "images": [
    {
      "url": "string",
      "position": 0,
      "isMain": true,
      "alt": "string",
      "id": "string",
      "key": "string",
      "thumbnailUrl": "string",
      "width": 0,
      "height": 0,
      "size": 0,
      "mimeType": "string",
      "createdAt": "2019-08-24T14:15:22Z",
      "source": "string",
      "importStatus": "string",
      "importError": "string"
    }
  ],
  "channels": {},
  "translations": {},
  "categories": [
    {
      "id": "string",
      "name": "string",
      "taxBehavior": "string"
    }
  ],
  "brands": [
    {
      "id": "clx1a2b3c4d5e6f7g8h9",
      "name": "Summer Collection"
    }
  ],
  "attributes": [
    "string"
  ],
  "productAttributeOptions": [
    {
      "id": "string",
      "attributeId": "string",
      "attributeOptionId": "string",
      "platform": "string",
      "attribute": {
        "id": "string",
        "name": "string",
        "displayType": "string"
      },
      "attributeOption": {
        "id": "string",
        "name": "string",
        "value": "string",
        "swatchColor": "string",
        "swatchColor2": "string",
        "swatchImageUrl": "string"
      }
    }
  ],
  "tags": [
    "string"
  ],
  "metafields": [
    {
      "id": "string",
      "definitionId": "string",
      "definitionKey": "string",
      "definitionName": "string",
      "type": "string",
      "value": "string",
      "variantId": "string"
    }
  ],
  "customizationFields": [
    {
      "definitionId": "string",
      "name": "string",
      "key": "string",
      "description": "string",
      "type": "string",
      "required": true,
      "minLength": 0,
      "maxLength": 0,
      "minValue": 0,
      "maxValue": 0,
      "enumValues": [
        "string"
      ],
      "defaultValue": "string",
      "position": 0
    }
  ],
  "channelPublishes": [
    {
      "salesChannel": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      },
      "connection": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      }
    }
  ],
  "vibeCodedPublishes": [
    {
      "salesChannel": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      },
      "connection": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      }
    }
  ],
  "channelSync": {},
  "shippingClassId": "string",
  "shippingClass": {
    "id": "string",
    "name": "string"
  },
  "crossSells": [
    {
      "id": "clx1a2b3c4d5e6f7g8h9",
      "name": "Summer Collection"
    }
  ],
  "upsells": [
    {
      "id": "clx1a2b3c4d5e6f7g8h9",
      "name": "Summer Collection"
    }
  ],
  "related": [
    {
      "id": "clx1a2b3c4d5e6f7g8h9",
      "name": "Summer Collection"
    }
  ],
  "bundleOfferCount": 0,
  "orderBumpCount": 0,
  "avgRating": 0,
  "reviewCount": 0,
  "seoScore": {
    "score": 0,
    "band": "excellent",
    "hasUnresolvedError": true
  },
  "modifierGroups": [
    {
      "attachmentId": "string",
      "groupId": "string",
      "variantId": "string",
      "position": 0,
      "minOverride": 0,
      "maxOverride": 0,
      "freeQuantityOverride": 0,
      "requiredOverride": true,
      "freeAllocationPolicyOverride": "EXPENSIVE_FREE",
      "defaultModifierIds": [
        "string"
      ],
      "name": "string",
      "internalName": "string",
      "description": "string",
      "translations": {},
      "selectionType": "SINGLE",
      "minSelections": 0,
      "maxSelections": 0,
      "freeQuantity": 0,
      "required": true,
      "freeAllocationPolicy": "EXPENSIVE_FREE",
      "status": "string",
      "modifiers": [
        {
          "id": "string",
          "name": "string",
          "description": "string",
          "translations": {},
          "priceDelta": "2.50",
          "sku": "string",
          "image": {},
          "position": 0,
          "isDefault": true,
          "available": true,
          "referencedProductId": "string",
          "status": "string"
        }
      ]
    }
  ]
}

{
  "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/products"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/products"
}

{
  "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/products"
}

Get a single product by ID

GET
/v1/products/{id}
AuthorizationBearer <token>

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

idstring

Product ID

Query Parameters

regionId?string
curl -X GET "https://api.brainerce.com/api/v1/products/string?regionId=string"
{
  "id": "string",
  "name": "string",
  "slug": "string",
  "localeSlugs": {
    "property1": "string",
    "property2": "string"
  },
  "description": "string",
  "descriptionFormat": "text",
  "basePrice": "49.90",
  "salePrice": "string",
  "costPrice": "string",
  "priceMin": "string",
  "priceMax": "string",
  "priceVaries": true,
  "displayPrice": "string",
  "displaySalePrice": "string",
  "displayPriceMin": "string",
  "displayPriceMax": "string",
  "displayCurrency": "string",
  "gtin": "string",
  "mpn": "string",
  "salePriceStartsAt": "2019-08-24T14:15:22Z",
  "salePriceEndsAt": "2019-08-24T14:15:22Z",
  "shippingWeightValue": 0,
  "shippingWeightUnit": "string",
  "shippingLengthValue": 0,
  "shippingWidthValue": 0,
  "shippingHeightValue": 0,
  "shippingDimensionUnit": "string",
  "metaDescription": "string",
  "status": "string",
  "sku": "string",
  "type": "SIMPLE",
  "kitPricingMode": "FIXED",
  "kitDiscountValue": 0,
  "isDownloadable": true,
  "downloadData": {
    "files": [
      {
        "id": "string",
        "name": "string",
        "url": "string",
        "storageKey": "string",
        "size": 0,
        "mimeType": "string"
      }
    ],
    "downloadLimit": 0,
    "downloadExpiry": 0
  },
  "needsSync": true,
  "lastSyncedAt": "2019-08-24T14:15:22Z",
  "menuOrder": 0,
  "taxBehavior": "string",
  "createdAt": "2019-08-24T14:15:22Z",
  "updatedAt": "2019-08-24T14:15:22Z",
  "inventory": {
    "trackingMode": "TRACKED",
    "total": 42,
    "reserved": 3,
    "available": 39,
    "inStock": true,
    "canPurchase": true,
    "lastInventorySyncAt": "2019-08-24T14:15:22Z"
  },
  "variants": [
    {
      "id": "string",
      "productId": "string",
      "sku": "string",
      "name": "string",
      "attributes": {},
      "price": "19.90",
      "salePrice": "string",
      "costPrice": "string",
      "displayPrice": "string",
      "displaySalePrice": "string",
      "displayCurrency": "string",
      "image": "string",
      "position": 0,
      "status": "string",
      "inventory": {
        "trackingMode": "TRACKED",
        "total": 42,
        "reserved": 3,
        "available": 39,
        "inStock": true,
        "canPurchase": true,
        "lastInventorySyncAt": "2019-08-24T14:15:22Z"
      },
      "isDownloadable": true,
      "downloadData": {
        "files": [
          {
            "id": "string",
            "name": "string",
            "url": "string",
            "storageKey": "string",
            "size": 0,
            "mimeType": "string"
          }
        ],
        "downloadLimit": 0,
        "downloadExpiry": 0
      },
      "shippingClassId": "string",
      "createdAt": "2019-08-24T14:15:22Z",
      "updatedAt": "2019-08-24T14:15:22Z"
    }
  ],
  "images": [
    {
      "url": "string",
      "position": 0,
      "isMain": true,
      "alt": "string",
      "id": "string",
      "key": "string",
      "thumbnailUrl": "string",
      "width": 0,
      "height": 0,
      "size": 0,
      "mimeType": "string",
      "createdAt": "2019-08-24T14:15:22Z",
      "source": "string",
      "importStatus": "string",
      "importError": "string"
    }
  ],
  "channels": {},
  "translations": {},
  "categories": [
    {
      "id": "string",
      "name": "string",
      "taxBehavior": "string"
    }
  ],
  "brands": [
    {
      "id": "clx1a2b3c4d5e6f7g8h9",
      "name": "Summer Collection"
    }
  ],
  "attributes": [
    "string"
  ],
  "productAttributeOptions": [
    {
      "id": "string",
      "attributeId": "string",
      "attributeOptionId": "string",
      "platform": "string",
      "attribute": {
        "id": "string",
        "name": "string",
        "displayType": "string"
      },
      "attributeOption": {
        "id": "string",
        "name": "string",
        "value": "string",
        "swatchColor": "string",
        "swatchColor2": "string",
        "swatchImageUrl": "string"
      }
    }
  ],
  "tags": [
    "string"
  ],
  "metafields": [
    {
      "id": "string",
      "definitionId": "string",
      "definitionKey": "string",
      "definitionName": "string",
      "type": "string",
      "value": "string",
      "variantId": "string"
    }
  ],
  "customizationFields": [
    {
      "definitionId": "string",
      "name": "string",
      "key": "string",
      "description": "string",
      "type": "string",
      "required": true,
      "minLength": 0,
      "maxLength": 0,
      "minValue": 0,
      "maxValue": 0,
      "enumValues": [
        "string"
      ],
      "defaultValue": "string",
      "position": 0
    }
  ],
  "channelPublishes": [
    {
      "salesChannel": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      },
      "connection": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      }
    }
  ],
  "vibeCodedPublishes": [
    {
      "salesChannel": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      },
      "connection": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      }
    }
  ],
  "channelSync": {},
  "shippingClassId": "string",
  "shippingClass": {
    "id": "string",
    "name": "string"
  },
  "crossSells": [
    {
      "id": "clx1a2b3c4d5e6f7g8h9",
      "name": "Summer Collection"
    }
  ],
  "upsells": [
    {
      "id": "clx1a2b3c4d5e6f7g8h9",
      "name": "Summer Collection"
    }
  ],
  "related": [
    {
      "id": "clx1a2b3c4d5e6f7g8h9",
      "name": "Summer Collection"
    }
  ],
  "bundleOfferCount": 0,
  "orderBumpCount": 0,
  "avgRating": 0,
  "reviewCount": 0,
  "seoScore": {
    "score": 0,
    "band": "excellent",
    "hasUnresolvedError": true
  },
  "modifierGroups": [
    {
      "attachmentId": "string",
      "groupId": "string",
      "variantId": "string",
      "position": 0,
      "minOverride": 0,
      "maxOverride": 0,
      "freeQuantityOverride": 0,
      "requiredOverride": true,
      "freeAllocationPolicyOverride": "EXPENSIVE_FREE",
      "defaultModifierIds": [
        "string"
      ],
      "name": "string",
      "internalName": "string",
      "description": "string",
      "translations": {},
      "selectionType": "SINGLE",
      "minSelections": 0,
      "maxSelections": 0,
      "freeQuantity": 0,
      "required": true,
      "freeAllocationPolicy": "EXPENSIVE_FREE",
      "status": "string",
      "modifiers": [
        {
          "id": "string",
          "name": "string",
          "description": "string",
          "translations": {},
          "priceDelta": "2.50",
          "sku": "string",
          "image": {},
          "position": 0,
          "isDefault": true,
          "available": true,
          "referencedProductId": "string",
          "status": "string"
        }
      ]
    }
  ]
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/products/{id}"
}

{
  "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/products/{id}"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/products/{id}"
}

Update a product

Supports Idempotency-Key header for safe retries.

PATCH
/v1/products/{id}
AuthorizationBearer <token>

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

idstring

Product ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
name?string

Display name of the product.

slug?string

URL slug. Letters, numbers, and hyphens only. Changing the slug breaks existing shopper bookmarks for this product.

sku?string

Stock keeping unit, meaning your internal product code.

gtin?string

Global Trade Item Number (universal barcode: EAN/UPC/ISBN). Validated against the checksum digit. Pass an empty string to clear.

mpn?string

Manufacturer Part Number, paired with brand when no GTIN exists. Max 70 chars.

Lengthlength <= 70
salePriceStartsAt?string

Sale-price effective window start (ISO 8601). Pass null to clear.

salePriceEndsAt?string

Sale-price effective window end (ISO 8601). Pass null to clear.

shippingWeightValue?number

Shipping weight numeric value. Pass null to clear.

shippingWeightUnit?string

Shipping weight unit.

Value in"kg" | "lb" | "g" | "oz"
shippingLengthValue?number

Shipping package length. Pass null to clear.

shippingWidthValue?number

Shipping package width. Pass null to clear.

shippingHeightValue?number

Shipping package height. Pass null to clear.

shippingDimensionUnit?string

Unit shared by the dimension fields.

Value in"cm" | "in"
metaDescription?string

SEO meta description (≤160 chars). Used by Google Search snippets and social link previews. Falls back to a stripped/truncated description when omitted.

Lengthlength <= 160
description?string

Long-form product description. Accepts HTML, and the backend strips dangerous tags/attributes (<script>, inline event handlers, <iframe>, …) before saving. Hard cap of 65 535 characters.

Lengthlength <= 65535
basePrice?number

Base list price in the store currency.

Range0 <= value
salePrice?number | null

Sale price (must be < basePrice). When present, the storefront displays it as the active price with basePrice shown struck-through. Pass null to take the product OFF sale. Omitting the field leaves the existing sale price untouched, which is not the same thing (D76: the dashboard form sent undefined for an emptied box, so a sale could be started and never ended).

Range0 <= value
costPrice?number | null

Internal cost (what you pay your supplier). Used for margin reporting only, and never shown to shoppers. Pass null to clear.

status?string

Publish state. active = visible to shoppers; draft = only visible in the dashboard.

Value in"active" | "draft"
type?string

Product structure. SIMPLE = single SKU; VARIABLE = parent of multiple variants.

Value in"SIMPLE" | "VARIABLE" | "KIT"
kitPricingMode?string

How a KIT is priced. FIXED (default) — you set basePrice and it stays put. SUM — the kit costs exactly what its components cost, recomputed on every read, so a component going on sale lowers the kit price on its own. SUM_MINUS_PERCENT — that sum less kitDiscountValue percent. Ignored on SIMPLE and VARIABLE products.

Value in"FIXED" | "SUM" | "SUM_MINUS_PERCENT"
kitDiscountValue?number

Percent off the component sum, 0-100. Required when kitPricingMode is SUM_MINUS_PERCENT; ignored otherwise.

Range0 <= value <= 100
isDownloadable?boolean

If true, the product is a digital download, so checkout issues a signed URL after payment instead of triggering shipping.

needsSync?boolean

When true, the product is flagged for re-sync to connected platforms on the next sync job.

channels?object

Per-platform field overrides. Keyed by platform code (SHOPIFY, TIKTOK, …); value is a partial product object that overrides the canonical fields for that channel only.

Empty Object

tags?array<string>

Free-form tags for filtering and segmentation.

categories?array<string>

Existing category IDs to assign (replaces the current set). Pass [] to remove all categories. Unknown/cross-store IDs are rejected with 400. To assign by name, creating the category if missing, use categoryNames.

categoryNames?array<string>

Category names to assign (case-insensitive match, auto-created if missing). Additive: names are ADDED to the product, joining the set in categories when that is also sent, otherwise to the product's current categories (they are not replaced).

brands?array<string>

Existing brand IDs to assign (replaces the current set). Pass [] to remove all brands. Unknown/cross-store IDs are rejected with 400. To assign by name, creating the brand if missing, use brandNames.

brandNames?array<string>

Brand names to assign (case-insensitive match, auto-created if missing). Additive: names are ADDED to the set in brands when that is also sent, otherwise to the product's current brands (they are not replaced).

images?array<string>

Product images. Each entry: { url, position, isMain }. Upload via /v1/media first to get a signed URL.

menuOrder?number

Display order within the catalog/category listing. Lower values render first. Mirrors WooCommerce menu_order.

shippingClassId?string | null

ID of the shipping class this product belongs to (used by shipping-rate rules). Pass null to clear.

taxClassId?string | null

ID of the tax class this product belongs to (drives differential tax rates at checkout). Pass null to clear (falls back to category/store-default/Standard).

downloadData?object

Digital-download configuration. Required when isDownloadable=true. files[] are uploaded via /v1/media. downloadLimit = max downloads per order (null = unlimited). downloadExpiry = hours until the signed URL expires (null = no expiry).

Empty Object

taxBehavior?string

Tax category. taxable is the default; exempt skips all tax calculation.

Value in"taxable" | "exempt"
translations?object

Multi-language overrides. Keyed by ISO locale code (he, ar, es, …). Each value is a partial product with translated name / description / metaDescription.

Empty Object

curl -X PATCH "https://api.brainerce.com/api/v1/products/string" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{}'
{
  "id": "string",
  "name": "string",
  "slug": "string",
  "localeSlugs": {
    "property1": "string",
    "property2": "string"
  },
  "description": "string",
  "descriptionFormat": "text",
  "basePrice": "49.90",
  "salePrice": "string",
  "costPrice": "string",
  "priceMin": "string",
  "priceMax": "string",
  "priceVaries": true,
  "displayPrice": "string",
  "displaySalePrice": "string",
  "displayPriceMin": "string",
  "displayPriceMax": "string",
  "displayCurrency": "string",
  "gtin": "string",
  "mpn": "string",
  "salePriceStartsAt": "2019-08-24T14:15:22Z",
  "salePriceEndsAt": "2019-08-24T14:15:22Z",
  "shippingWeightValue": 0,
  "shippingWeightUnit": "string",
  "shippingLengthValue": 0,
  "shippingWidthValue": 0,
  "shippingHeightValue": 0,
  "shippingDimensionUnit": "string",
  "metaDescription": "string",
  "status": "string",
  "sku": "string",
  "type": "SIMPLE",
  "kitPricingMode": "FIXED",
  "kitDiscountValue": 0,
  "isDownloadable": true,
  "downloadData": {
    "files": [
      {
        "id": "string",
        "name": "string",
        "url": "string",
        "storageKey": "string",
        "size": 0,
        "mimeType": "string"
      }
    ],
    "downloadLimit": 0,
    "downloadExpiry": 0
  },
  "needsSync": true,
  "lastSyncedAt": "2019-08-24T14:15:22Z",
  "menuOrder": 0,
  "taxBehavior": "string",
  "createdAt": "2019-08-24T14:15:22Z",
  "updatedAt": "2019-08-24T14:15:22Z",
  "inventory": {
    "trackingMode": "TRACKED",
    "total": 42,
    "reserved": 3,
    "available": 39,
    "inStock": true,
    "canPurchase": true,
    "lastInventorySyncAt": "2019-08-24T14:15:22Z"
  },
  "variants": [
    {
      "id": "string",
      "productId": "string",
      "sku": "string",
      "name": "string",
      "attributes": {},
      "price": "19.90",
      "salePrice": "string",
      "costPrice": "string",
      "displayPrice": "string",
      "displaySalePrice": "string",
      "displayCurrency": "string",
      "image": "string",
      "position": 0,
      "status": "string",
      "inventory": {
        "trackingMode": "TRACKED",
        "total": 42,
        "reserved": 3,
        "available": 39,
        "inStock": true,
        "canPurchase": true,
        "lastInventorySyncAt": "2019-08-24T14:15:22Z"
      },
      "isDownloadable": true,
      "downloadData": {
        "files": [
          {
            "id": "string",
            "name": "string",
            "url": "string",
            "storageKey": "string",
            "size": 0,
            "mimeType": "string"
          }
        ],
        "downloadLimit": 0,
        "downloadExpiry": 0
      },
      "shippingClassId": "string",
      "createdAt": "2019-08-24T14:15:22Z",
      "updatedAt": "2019-08-24T14:15:22Z"
    }
  ],
  "images": [
    {
      "url": "string",
      "position": 0,
      "isMain": true,
      "alt": "string",
      "id": "string",
      "key": "string",
      "thumbnailUrl": "string",
      "width": 0,
      "height": 0,
      "size": 0,
      "mimeType": "string",
      "createdAt": "2019-08-24T14:15:22Z",
      "source": "string",
      "importStatus": "string",
      "importError": "string"
    }
  ],
  "channels": {},
  "translations": {},
  "categories": [
    {
      "id": "string",
      "name": "string",
      "taxBehavior": "string"
    }
  ],
  "brands": [
    {
      "id": "clx1a2b3c4d5e6f7g8h9",
      "name": "Summer Collection"
    }
  ],
  "attributes": [
    "string"
  ],
  "productAttributeOptions": [
    {
      "id": "string",
      "attributeId": "string",
      "attributeOptionId": "string",
      "platform": "string",
      "attribute": {
        "id": "string",
        "name": "string",
        "displayType": "string"
      },
      "attributeOption": {
        "id": "string",
        "name": "string",
        "value": "string",
        "swatchColor": "string",
        "swatchColor2": "string",
        "swatchImageUrl": "string"
      }
    }
  ],
  "tags": [
    "string"
  ],
  "metafields": [
    {
      "id": "string",
      "definitionId": "string",
      "definitionKey": "string",
      "definitionName": "string",
      "type": "string",
      "value": "string",
      "variantId": "string"
    }
  ],
  "customizationFields": [
    {
      "definitionId": "string",
      "name": "string",
      "key": "string",
      "description": "string",
      "type": "string",
      "required": true,
      "minLength": 0,
      "maxLength": 0,
      "minValue": 0,
      "maxValue": 0,
      "enumValues": [
        "string"
      ],
      "defaultValue": "string",
      "position": 0
    }
  ],
  "channelPublishes": [
    {
      "salesChannel": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      },
      "connection": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      }
    }
  ],
  "vibeCodedPublishes": [
    {
      "salesChannel": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      },
      "connection": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      }
    }
  ],
  "channelSync": {},
  "shippingClassId": "string",
  "shippingClass": {
    "id": "string",
    "name": "string"
  },
  "crossSells": [
    {
      "id": "clx1a2b3c4d5e6f7g8h9",
      "name": "Summer Collection"
    }
  ],
  "upsells": [
    {
      "id": "clx1a2b3c4d5e6f7g8h9",
      "name": "Summer Collection"
    }
  ],
  "related": [
    {
      "id": "clx1a2b3c4d5e6f7g8h9",
      "name": "Summer Collection"
    }
  ],
  "bundleOfferCount": 0,
  "orderBumpCount": 0,
  "avgRating": 0,
  "reviewCount": 0,
  "seoScore": {
    "score": 0,
    "band": "excellent",
    "hasUnresolvedError": true
  },
  "modifierGroups": [
    {
      "attachmentId": "string",
      "groupId": "string",
      "variantId": "string",
      "position": 0,
      "minOverride": 0,
      "maxOverride": 0,
      "freeQuantityOverride": 0,
      "requiredOverride": true,
      "freeAllocationPolicyOverride": "EXPENSIVE_FREE",
      "defaultModifierIds": [
        "string"
      ],
      "name": "string",
      "internalName": "string",
      "description": "string",
      "translations": {},
      "selectionType": "SINGLE",
      "minSelections": 0,
      "maxSelections": 0,
      "freeQuantity": 0,
      "required": true,
      "freeAllocationPolicy": "EXPENSIVE_FREE",
      "status": "string",
      "modifiers": [
        {
          "id": "string",
          "name": "string",
          "description": "string",
          "translations": {},
          "priceDelta": "2.50",
          "sku": "string",
          "image": {},
          "position": 0,
          "isDefault": true,
          "available": true,
          "referencedProductId": "string",
          "status": "string"
        }
      ]
    }
  ]
}

{
  "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/products/{id}"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/products/{id}"
}

{
  "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/products/{id}"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/products/{id}"
}

Delete a product

Supports Idempotency-Key header for safe retries.

DELETE
/v1/products/{id}
AuthorizationBearer <token>

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

idstring

Product ID

Query Parameters

platforms?string

Platforms to delete from (comma-separated)

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
curl -X DELETE "https://api.brainerce.com/api/v1/products/string?platforms=string" \  -H "Idempotency-Key: string"
{
  "success": true,
  "jobIds": [
    "string"
  ],
  "warnings": [
    "string"
  ]
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/products/{id}"
}

{
  "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/products/{id}"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/products/{id}"
}

Create many products in one call (queued)

Queues a batch of products for creation and returns a jobId immediately. It does NOT wait for the import to finish. Every field accepted by POST /v1/products is accepted per row, including variants, categories, brands, tags, images, translations and tax behavior. Max 1000 products per request (500 recommended). For a 3,000-50,000 product catalog, send several requests of that size carrying the same importId, then poll GET /v1/products/bulk/import/{importId} for the whole import. Rows whose sku or externalId already exist are skipped by default (see conflictStrategy), so re-sending a batch after a timeout does not duplicate the catalog. The Idempotency-Key header is honored as well and returns the original jobId on replay.

POST
/v1/products/bulk
AuthorizationBearer <token>

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

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
productsarray<CreateProductDto>

Products to create. Each entry accepts every field POST /v1/products accepts. Max 1000 per request (500 recommended), so chunk larger catalogs and pass the same importId on every chunk. Rows are validated individually: an invalid row is reported as a row failure and does NOT reject the batch.

importId?string

Groups several chunked requests into one logical import so status can be read for the whole catalog rather than per chunk. Generate one value client-side and send it on every chunk. Any string you choose; it is not validated against anything.

Lengthlength <= 191
conflictStrategy?string

How to handle a row whose sku or externalId already exists in the store. skip (default) counts it under skipped and moves on, which is the safe choice for retries. error records it as a failure instead, which is what you want when a duplicate means the source file is wrong.

Value in"skip" | "error"
syncMode?string

Channel sync behavior. coalesced (default) suppresses the per-product connector push and files ONE sync per affected sales channel when the import finishes. That is necessary because connectors are rate-limited per catalog (Meta allows 100 batch requests/hour). none writes to Brainerce only and leaves channels to the next reconciliation.

Value in"coalesced" | "none"
idempotencyKey?string

Durable dedup key for this batch. Re-sending the same key returns the ORIGINAL jobId instead of importing again. On HTTP the Idempotency-Key header does the same thing and is preferred; this field exists because it is stored in the database rather than in Redis with a 24h TTL, and because the MCP tool has no HTTP headers to carry one.

Lengthlength <= 191
curl -X POST "https://api.brainerce.com/api/v1/products/bulk" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{    "products": [      {        "name": "Premium Wireless Headphones",        "basePrice": 299.99,        "type": "SIMPLE"      }    ]  }'
{
  "jobId": "string",
  "importId": "string",
  "status": "QUEUED",
  "total": 0,
  "replayed": true
}

{
  "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/products/bulk"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/products/bulk"
}

{
  "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/products/bulk"
}

{
  "statusCode": 409,
  "code": "CONFLICT",
  "message": "The request conflicts with the current state of the resource",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/products/bulk"
}

Get bulk product creation status

Counters for one queued batch: total, processed, succeeded, failed, skipped, pending. skipped counts rows whose sku/externalId already existed. They are NOT failures and NOT creations. Status is QUEUED, RUNNING, COMPLETED, COMPLETED_WITH_ERRORS, FAILED or CANCELLED; COMPLETED_WITH_ERRORS means every row was attempted and some failed, which is not something to retry wholesale. read the per-row failures from the errors endpoint instead.

GET
/v1/products/bulk/{jobId}
AuthorizationBearer <token>

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

jobIdstring

Job ID returned by POST /v1/products/bulk

curl -X GET "https://api.brainerce.com/api/v1/products/bulk/string"
{
  "jobId": "string",
  "importId": "string",
  "status": "string",
  "total": 0,
  "processed": 0,
  "succeeded": 0,
  "failed": 0,
  "skipped": 0,
  "pending": 0,
  "conflictStrategy": "string",
  "syncMode": "string",
  "errorMessage": "string",
  "startedAt": "2019-08-24T14:15:22Z",
  "finishedAt": "2019-08-24T14:15:22Z",
  "createdAt": "2019-08-24T14:15:22Z",
  "jobCount": 0
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/products/bulk/{jobId}"
}

Get aggregate status for a chunked import

Rolls every batch sharing this importId into one set of counters, so a 50,000-product catalog sent as 100 requests is polled once rather than 100 times. The aggregate status is the least-complete state across the chunks: any chunk still QUEUED or RUNNING keeps the whole import unfinished. finishedAt stays null until every chunk has finished.

GET
/v1/products/bulk/import/{importId}
AuthorizationBearer <token>

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

importIdstring

The importId sent on every chunk

curl -X GET "https://api.brainerce.com/api/v1/products/bulk/import/string"
{
  "jobId": "string",
  "importId": "string",
  "status": "string",
  "total": 0,
  "processed": 0,
  "succeeded": 0,
  "failed": 0,
  "skipped": 0,
  "pending": 0,
  "conflictStrategy": "string",
  "syncMode": "string",
  "errorMessage": "string",
  "startedAt": "2019-08-24T14:15:22Z",
  "finishedAt": "2019-08-24T14:15:22Z",
  "createdAt": "2019-08-24T14:15:22Z",
  "jobCount": 0
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/products/bulk/import/{importId}"
}

List per-product failures for a bulk create job

One entry per failed product, carrying the 1-indexed row from your submitted array plus sku, externalId, productName, a code (VALIDATION / DUPLICATE / PLAN_LIMIT / INTERNAL) and the message. Every failure is recorded and nothing is truncated, so this is paginated.

GET
/v1/products/bulk/{jobId}/errors
AuthorizationBearer <token>

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

jobIdstring

Job ID returned by POST /v1/products/bulk

Query Parameters

page?string

Page number (default 1)

limit?string

Page size (default 50, max 100)

curl -X GET "https://api.brainerce.com/api/v1/products/bulk/string/errors?page=string&limit=string"
{
  "data": [
    {
      "row": 0,
      "sku": "string",
      "externalId": "string",
      "productName": "string",
      "code": "string",
      "message": "string"
    }
  ],
  "meta": {
    "page": 1,
    "limit": 20,
    "total": 137,
    "totalPages": 7
  }
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/products/bulk/{jobId}/errors"
}

Get kit components

Returns the kit's components with each one's live price and stock, the resolved kit price, its per-component split, the pricing mode, and how many kits can be sold. A component whose product or pinned variant is not published reports zero stock and takes the kit's sellable count to zero with it, however much stock the other components hold. A product that is not a KIT returns an empty, unsellable shape rather than a 404.

GET
/v1/products/{id}/kit-components
AuthorizationBearer <token>

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

idstring

Kit product ID

curl -X GET "https://api.brainerce.com/api/v1/products/string/kit-components"
{
  "components": [
    {
      "id": "pkc_01H...",
      "componentProductId": "prod_01H...",
      "componentVariantId": "pv_01H...",
      "quantity": 2,
      "position": 0,
      "name": "Riedel tumbler — 300ml",
      "sku": "GLS-300",
      "unitPrice": "12.90",
      "available": 40,
      "image": "https://cdn.example.com/glass.jpg"
    }
  ],
  "pricingMode": "FIXED",
  "discountValue": 10,
  "price": "246.00",
  "allocationMinor": [
    13536,
    7973,
    3091
  ],
  "available": 12
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/products/{id}/kit-components"
}

{
  "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/products/{id}/kit-components"
}

Set kit components

Replaces the kit's component list in a single transaction and returns it re-resolved. Rejects a product that is not a KIT, a component from another store, a component that is itself a KIT, a VARIABLE component with no variant pinned, a variant that does not belong to its product, a duplicate slot, the kit itself, and a component whose product or pinned variant is not published. That last check runs over the whole list you send, not only the rows you changed, so once a product already inside a kit is unpublished no edit to that kit saves until you publish it again or drop it.

PUT
/v1/products/{id}/kit-components
AuthorizationBearer <token>

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

idstring

Kit product ID

pricingMode?string

How the kit is priced. FIXED (default) — the merchant types a price and it stays put. SUM — the kit costs exactly what its contents cost, recomputed on every read, so a component going on sale drops the kit price on its own. SUM_MINUS_PERCENT — that sum less kitDiscountValue percent. Omit to leave the current mode unchanged.

Value in"FIXED" | "SUM" | "SUM_MINUS_PERCENT"
discountValue?number

Percent off the component sum. Required when pricingMode is SUM_MINUS_PERCENT, ignored otherwise. 0-100.

Range0 <= value <= 100
componentsarray<KitComponentInputDto>

The kit's full component list, replacing whatever it had. Order is preserved as position. An empty array clears the kit — it then has no price and cannot be bought until components are added back.

Itemsitems <= 30
curl -X PUT "https://api.brainerce.com/api/v1/products/string/kit-components" \  -H "Content-Type: application/json" \  -d '{    "components": [      {        "componentProductId": "prod_01H...",        "quantity": 2      }    ]  }'
{
  "components": [
    {
      "id": "pkc_01H...",
      "componentProductId": "prod_01H...",
      "componentVariantId": "pv_01H...",
      "quantity": 2,
      "position": 0,
      "name": "Riedel tumbler — 300ml",
      "sku": "GLS-300",
      "unitPrice": "12.90",
      "available": 40,
      "image": "https://cdn.example.com/glass.jpg"
    }
  ],
  "pricingMode": "FIXED",
  "discountValue": 10,
  "price": "246.00",
  "allocationMinor": [
    13536,
    7973,
    3091
  ],
  "available": 12
}

{
  "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/products/{id}/kit-components"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/products/{id}/kit-components"
}

{
  "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/products/{id}/kit-components"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/products/{id}/kit-components"
}

Convert a SIMPLE product to VARIABLE

Converts the product type and migrates existing price/inventory into a first variant. Idempotent, and returns 400 if already VARIABLE.

PATCH
/v1/products/{id}/convert-to-variable
AuthorizationBearer <token>

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

idstring

Product ID

curl -X PATCH "https://api.brainerce.com/api/v1/products/string/convert-to-variable"
{
  "id": "string",
  "name": "string",
  "slug": "string",
  "localeSlugs": {
    "property1": "string",
    "property2": "string"
  },
  "description": "string",
  "descriptionFormat": "text",
  "basePrice": "49.90",
  "salePrice": "string",
  "costPrice": "string",
  "priceMin": "string",
  "priceMax": "string",
  "priceVaries": true,
  "displayPrice": "string",
  "displaySalePrice": "string",
  "displayPriceMin": "string",
  "displayPriceMax": "string",
  "displayCurrency": "string",
  "gtin": "string",
  "mpn": "string",
  "salePriceStartsAt": "2019-08-24T14:15:22Z",
  "salePriceEndsAt": "2019-08-24T14:15:22Z",
  "shippingWeightValue": 0,
  "shippingWeightUnit": "string",
  "shippingLengthValue": 0,
  "shippingWidthValue": 0,
  "shippingHeightValue": 0,
  "shippingDimensionUnit": "string",
  "metaDescription": "string",
  "status": "string",
  "sku": "string",
  "type": "SIMPLE",
  "kitPricingMode": "FIXED",
  "kitDiscountValue": 0,
  "isDownloadable": true,
  "downloadData": {
    "files": [
      {
        "id": "string",
        "name": "string",
        "url": "string",
        "storageKey": "string",
        "size": 0,
        "mimeType": "string"
      }
    ],
    "downloadLimit": 0,
    "downloadExpiry": 0
  },
  "needsSync": true,
  "lastSyncedAt": "2019-08-24T14:15:22Z",
  "menuOrder": 0,
  "taxBehavior": "string",
  "createdAt": "2019-08-24T14:15:22Z",
  "updatedAt": "2019-08-24T14:15:22Z",
  "inventory": {
    "trackingMode": "TRACKED",
    "total": 42,
    "reserved": 3,
    "available": 39,
    "inStock": true,
    "canPurchase": true,
    "lastInventorySyncAt": "2019-08-24T14:15:22Z"
  },
  "variants": [
    {
      "id": "string",
      "productId": "string",
      "sku": "string",
      "name": "string",
      "attributes": {},
      "price": "19.90",
      "salePrice": "string",
      "costPrice": "string",
      "displayPrice": "string",
      "displaySalePrice": "string",
      "displayCurrency": "string",
      "image": "string",
      "position": 0,
      "status": "string",
      "inventory": {
        "trackingMode": "TRACKED",
        "total": 42,
        "reserved": 3,
        "available": 39,
        "inStock": true,
        "canPurchase": true,
        "lastInventorySyncAt": "2019-08-24T14:15:22Z"
      },
      "isDownloadable": true,
      "downloadData": {
        "files": [
          {
            "id": "string",
            "name": "string",
            "url": "string",
            "storageKey": "string",
            "size": 0,
            "mimeType": "string"
          }
        ],
        "downloadLimit": 0,
        "downloadExpiry": 0
      },
      "shippingClassId": "string",
      "createdAt": "2019-08-24T14:15:22Z",
      "updatedAt": "2019-08-24T14:15:22Z"
    }
  ],
  "images": [
    {
      "url": "string",
      "position": 0,
      "isMain": true,
      "alt": "string",
      "id": "string",
      "key": "string",
      "thumbnailUrl": "string",
      "width": 0,
      "height": 0,
      "size": 0,
      "mimeType": "string",
      "createdAt": "2019-08-24T14:15:22Z",
      "source": "string",
      "importStatus": "string",
      "importError": "string"
    }
  ],
  "channels": {},
  "translations": {},
  "categories": [
    {
      "id": "string",
      "name": "string",
      "taxBehavior": "string"
    }
  ],
  "brands": [
    {
      "id": "clx1a2b3c4d5e6f7g8h9",
      "name": "Summer Collection"
    }
  ],
  "attributes": [
    "string"
  ],
  "productAttributeOptions": [
    {
      "id": "string",
      "attributeId": "string",
      "attributeOptionId": "string",
      "platform": "string",
      "attribute": {
        "id": "string",
        "name": "string",
        "displayType": "string"
      },
      "attributeOption": {
        "id": "string",
        "name": "string",
        "value": "string",
        "swatchColor": "string",
        "swatchColor2": "string",
        "swatchImageUrl": "string"
      }
    }
  ],
  "tags": [
    "string"
  ],
  "metafields": [
    {
      "id": "string",
      "definitionId": "string",
      "definitionKey": "string",
      "definitionName": "string",
      "type": "string",
      "value": "string",
      "variantId": "string"
    }
  ],
  "customizationFields": [
    {
      "definitionId": "string",
      "name": "string",
      "key": "string",
      "description": "string",
      "type": "string",
      "required": true,
      "minLength": 0,
      "maxLength": 0,
      "minValue": 0,
      "maxValue": 0,
      "enumValues": [
        "string"
      ],
      "defaultValue": "string",
      "position": 0
    }
  ],
  "channelPublishes": [
    {
      "salesChannel": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      },
      "connection": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      }
    }
  ],
  "vibeCodedPublishes": [
    {
      "salesChannel": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      },
      "connection": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      }
    }
  ],
  "channelSync": {},
  "shippingClassId": "string",
  "shippingClass": {
    "id": "string",
    "name": "string"
  },
  "crossSells": [
    {
      "id": "clx1a2b3c4d5e6f7g8h9",
      "name": "Summer Collection"
    }
  ],
  "upsells": [
    {
      "id": "clx1a2b3c4d5e6f7g8h9",
      "name": "Summer Collection"
    }
  ],
  "related": [
    {
      "id": "clx1a2b3c4d5e6f7g8h9",
      "name": "Summer Collection"
    }
  ],
  "bundleOfferCount": 0,
  "orderBumpCount": 0,
  "avgRating": 0,
  "reviewCount": 0,
  "seoScore": {
    "score": 0,
    "band": "excellent",
    "hasUnresolvedError": true
  },
  "modifierGroups": [
    {
      "attachmentId": "string",
      "groupId": "string",
      "variantId": "string",
      "position": 0,
      "minOverride": 0,
      "maxOverride": 0,
      "freeQuantityOverride": 0,
      "requiredOverride": true,
      "freeAllocationPolicyOverride": "EXPENSIVE_FREE",
      "defaultModifierIds": [
        "string"
      ],
      "name": "string",
      "internalName": "string",
      "description": "string",
      "translations": {},
      "selectionType": "SINGLE",
      "minSelections": 0,
      "maxSelections": 0,
      "freeQuantity": 0,
      "required": true,
      "freeAllocationPolicy": "EXPENSIVE_FREE",
      "status": "string",
      "modifiers": [
        {
          "id": "string",
          "name": "string",
          "description": "string",
          "translations": {},
          "priceDelta": "2.50",
          "sku": "string",
          "image": {},
          "position": 0,
          "isDefault": true,
          "available": true,
          "referencedProductId": "string",
          "status": "string"
        }
      ]
    }
  ]
}

{
  "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/products/{id}/convert-to-variable"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/products/{id}/convert-to-variable"
}

{
  "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/products/{id}/convert-to-variable"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/products/{id}/convert-to-variable"
}

Convert a VARIABLE product back to SIMPLE

Merges the last remaining variant back into the parent product. Returns 400 if the product still has multiple variants, so delete the extras first.

PATCH
/v1/products/{id}/convert-to-simple
AuthorizationBearer <token>

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

idstring

Product ID

curl -X PATCH "https://api.brainerce.com/api/v1/products/string/convert-to-simple"
{
  "id": "string",
  "name": "string",
  "slug": "string",
  "localeSlugs": {
    "property1": "string",
    "property2": "string"
  },
  "description": "string",
  "descriptionFormat": "text",
  "basePrice": "49.90",
  "salePrice": "string",
  "costPrice": "string",
  "priceMin": "string",
  "priceMax": "string",
  "priceVaries": true,
  "displayPrice": "string",
  "displaySalePrice": "string",
  "displayPriceMin": "string",
  "displayPriceMax": "string",
  "displayCurrency": "string",
  "gtin": "string",
  "mpn": "string",
  "salePriceStartsAt": "2019-08-24T14:15:22Z",
  "salePriceEndsAt": "2019-08-24T14:15:22Z",
  "shippingWeightValue": 0,
  "shippingWeightUnit": "string",
  "shippingLengthValue": 0,
  "shippingWidthValue": 0,
  "shippingHeightValue": 0,
  "shippingDimensionUnit": "string",
  "metaDescription": "string",
  "status": "string",
  "sku": "string",
  "type": "SIMPLE",
  "kitPricingMode": "FIXED",
  "kitDiscountValue": 0,
  "isDownloadable": true,
  "downloadData": {
    "files": [
      {
        "id": "string",
        "name": "string",
        "url": "string",
        "storageKey": "string",
        "size": 0,
        "mimeType": "string"
      }
    ],
    "downloadLimit": 0,
    "downloadExpiry": 0
  },
  "needsSync": true,
  "lastSyncedAt": "2019-08-24T14:15:22Z",
  "menuOrder": 0,
  "taxBehavior": "string",
  "createdAt": "2019-08-24T14:15:22Z",
  "updatedAt": "2019-08-24T14:15:22Z",
  "inventory": {
    "trackingMode": "TRACKED",
    "total": 42,
    "reserved": 3,
    "available": 39,
    "inStock": true,
    "canPurchase": true,
    "lastInventorySyncAt": "2019-08-24T14:15:22Z"
  },
  "variants": [
    {
      "id": "string",
      "productId": "string",
      "sku": "string",
      "name": "string",
      "attributes": {},
      "price": "19.90",
      "salePrice": "string",
      "costPrice": "string",
      "displayPrice": "string",
      "displaySalePrice": "string",
      "displayCurrency": "string",
      "image": "string",
      "position": 0,
      "status": "string",
      "inventory": {
        "trackingMode": "TRACKED",
        "total": 42,
        "reserved": 3,
        "available": 39,
        "inStock": true,
        "canPurchase": true,
        "lastInventorySyncAt": "2019-08-24T14:15:22Z"
      },
      "isDownloadable": true,
      "downloadData": {
        "files": [
          {
            "id": "string",
            "name": "string",
            "url": "string",
            "storageKey": "string",
            "size": 0,
            "mimeType": "string"
          }
        ],
        "downloadLimit": 0,
        "downloadExpiry": 0
      },
      "shippingClassId": "string",
      "createdAt": "2019-08-24T14:15:22Z",
      "updatedAt": "2019-08-24T14:15:22Z"
    }
  ],
  "images": [
    {
      "url": "string",
      "position": 0,
      "isMain": true,
      "alt": "string",
      "id": "string",
      "key": "string",
      "thumbnailUrl": "string",
      "width": 0,
      "height": 0,
      "size": 0,
      "mimeType": "string",
      "createdAt": "2019-08-24T14:15:22Z",
      "source": "string",
      "importStatus": "string",
      "importError": "string"
    }
  ],
  "channels": {},
  "translations": {},
  "categories": [
    {
      "id": "string",
      "name": "string",
      "taxBehavior": "string"
    }
  ],
  "brands": [
    {
      "id": "clx1a2b3c4d5e6f7g8h9",
      "name": "Summer Collection"
    }
  ],
  "attributes": [
    "string"
  ],
  "productAttributeOptions": [
    {
      "id": "string",
      "attributeId": "string",
      "attributeOptionId": "string",
      "platform": "string",
      "attribute": {
        "id": "string",
        "name": "string",
        "displayType": "string"
      },
      "attributeOption": {
        "id": "string",
        "name": "string",
        "value": "string",
        "swatchColor": "string",
        "swatchColor2": "string",
        "swatchImageUrl": "string"
      }
    }
  ],
  "tags": [
    "string"
  ],
  "metafields": [
    {
      "id": "string",
      "definitionId": "string",
      "definitionKey": "string",
      "definitionName": "string",
      "type": "string",
      "value": "string",
      "variantId": "string"
    }
  ],
  "customizationFields": [
    {
      "definitionId": "string",
      "name": "string",
      "key": "string",
      "description": "string",
      "type": "string",
      "required": true,
      "minLength": 0,
      "maxLength": 0,
      "minValue": 0,
      "maxValue": 0,
      "enumValues": [
        "string"
      ],
      "defaultValue": "string",
      "position": 0
    }
  ],
  "channelPublishes": [
    {
      "salesChannel": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      },
      "connection": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      }
    }
  ],
  "vibeCodedPublishes": [
    {
      "salesChannel": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      },
      "connection": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      }
    }
  ],
  "channelSync": {},
  "shippingClassId": "string",
  "shippingClass": {
    "id": "string",
    "name": "string"
  },
  "crossSells": [
    {
      "id": "clx1a2b3c4d5e6f7g8h9",
      "name": "Summer Collection"
    }
  ],
  "upsells": [
    {
      "id": "clx1a2b3c4d5e6f7g8h9",
      "name": "Summer Collection"
    }
  ],
  "related": [
    {
      "id": "clx1a2b3c4d5e6f7g8h9",
      "name": "Summer Collection"
    }
  ],
  "bundleOfferCount": 0,
  "orderBumpCount": 0,
  "avgRating": 0,
  "reviewCount": 0,
  "seoScore": {
    "score": 0,
    "band": "excellent",
    "hasUnresolvedError": true
  },
  "modifierGroups": [
    {
      "attachmentId": "string",
      "groupId": "string",
      "variantId": "string",
      "position": 0,
      "minOverride": 0,
      "maxOverride": 0,
      "freeQuantityOverride": 0,
      "requiredOverride": true,
      "freeAllocationPolicyOverride": "EXPENSIVE_FREE",
      "defaultModifierIds": [
        "string"
      ],
      "name": "string",
      "internalName": "string",
      "description": "string",
      "translations": {},
      "selectionType": "SINGLE",
      "minSelections": 0,
      "maxSelections": 0,
      "freeQuantity": 0,
      "required": true,
      "freeAllocationPolicy": "EXPENSIVE_FREE",
      "status": "string",
      "modifiers": [
        {
          "id": "string",
          "name": "string",
          "description": "string",
          "translations": {},
          "priceDelta": "2.50",
          "sku": "string",
          "image": {},
          "position": 0,
          "isDefault": true,
          "available": true,
          "referencedProductId": "string",
          "status": "string"
        }
      ]
    }
  ]
}

{
  "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/products/{id}/convert-to-simple"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/products/{id}/convert-to-simple"
}

{
  "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/products/{id}/convert-to-simple"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/products/{id}/convert-to-simple"
}

Get inventory for a product

GET
/v1/products/{id}/inventory
AuthorizationBearer <token>

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

idstring

Product ID

curl -X GET "https://api.brainerce.com/api/v1/products/string/inventory"
{
  "id": "string",
  "productId": "string",
  "trackingMode": "TRACKED",
  "backorderMode": "string",
  "backorderLimit": 0,
  "total": 0,
  "reserved": 0,
  "available": 0,
  "lowStockThreshold": 0,
  "lastInventorySyncAt": "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/products/{id}/inventory"
}

{
  "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/products/{id}/inventory"
}

Update inventory for a product

Supports Idempotency-Key header for safe retries. ⛔ Rejects a KIT with 400: a kit holds no stock of its own — its availability is MIN(floor(component.available / quantity)), recomputed from its components on every read. Set the stock on the component products instead.

PUT
/v1/products/{id}/inventory
AuthorizationBearer <token>

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

idstring

Product ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
quantitynumber

New absolute stock level

variantId?string

Variant ID (optional)

curl -X PUT "https://api.brainerce.com/api/v1/products/string/inventory" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{    "quantity": 0  }'
{
  "id": "string",
  "productId": "string",
  "trackingMode": "TRACKED",
  "backorderMode": "string",
  "backorderLimit": 0,
  "total": 0,
  "reserved": 0,
  "available": 0,
  "lowStockThreshold": 0,
  "lastInventorySyncAt": "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/products/{id}/inventory"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/products/{id}/inventory"
}

{
  "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/products/{id}/inventory"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/products/{id}/inventory"
}

Create a variant for a product

Create a variant under a VARIABLE product. Supports Idempotency-Key header for safe retries.

POST
/v1/products/{id}/variants
AuthorizationBearer <token>

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

idstring

Parent product ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
sku?string

Stock keeping unit

gtin?string

GTIN (UPC/EAN/JAN/ISBN). Falls back to product GTIN when omitted.

mpn?string

Manufacturer part number. Falls back to product MPN when omitted.

Lengthlength <= 70
name?string

Variant display name

attributes?object

Variant attribute map

Empty Object

price?number

Variant price (in minor units of store currency)

salePrice?number

Variant sale price

costPrice?number

Variant unit cost (COGS) override, which falls back to the product costPrice for margin analytics. Merchant-internal: never exposed on public storefront responses.

inventory?number

Initial stock quantity for this variant

Range0 <= value
image?object

Variant-specific image (URL string or image object)

Empty Object

position?number

Position in variant list (0-indexed)

Range0 <= value
status?string

Variant status

Value in"active" | "draft"
isDownloadable?boolean

Whether this variant is a digital download

downloadData?object

Download payload for digital variants (files + limits)

Empty Object

shippingClassId?string | null

Per-variant shipping class ID. null/undefined inherits from parent product.

taxClassId?string | null

Per-variant tax class ID. null/undefined inherits from product → category → store default.

curl -X POST "https://api.brainerce.com/api/v1/products/string/variants" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{}'
{
  "id": "string",
  "productId": "string",
  "sku": "string",
  "name": "string",
  "attributes": {},
  "price": "19.90",
  "salePrice": "string",
  "costPrice": "string",
  "displayPrice": "string",
  "displaySalePrice": "string",
  "displayCurrency": "string",
  "image": "string",
  "position": 0,
  "status": "string",
  "inventory": {
    "trackingMode": "TRACKED",
    "total": 42,
    "reserved": 3,
    "available": 39,
    "inStock": true,
    "canPurchase": true,
    "lastInventorySyncAt": "2019-08-24T14:15:22Z"
  },
  "isDownloadable": true,
  "downloadData": {
    "files": [
      {
        "id": "string",
        "name": "string",
        "url": "string",
        "storageKey": "string",
        "size": 0,
        "mimeType": "string"
      }
    ],
    "downloadLimit": 0,
    "downloadExpiry": 0
  },
  "shippingClassId": "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/products/{id}/variants"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/products/{id}/variants"
}

{
  "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/products/{id}/variants"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/products/{id}/variants"
}

Bulk save variants

Create, update, and delete variants in a single transaction. Items with isDeleted: true are removed; items without id (or with an id starting with new-) are created; the rest are updated. Supports Idempotency-Key header for safe retries.

POST
/v1/products/{id}/variants/bulk
AuthorizationBearer <token>

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

idstring

Parent product ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
variantsarray<BulkVariantInputDto>

Variant inputs (create/update/delete in one call)

productAttributes?array<ProductAttributeInputDto>

Optional product-level attribute links to set alongside the variants

curl -X POST "https://api.brainerce.com/api/v1/products/string/variants/bulk" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{    "variants": [      {        "sku": "TSHIRT-S-RED",        "attributes": {          "size": "S",          "color": "Red"        },        "stock": 100,        "isEnabled": true      }    ]  }'
{
  "created": 2,
  "updated": 3,
  "deleted": 1,
  "variants": [
    {}
  ]
}

{
  "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/products/{id}/variants/bulk"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/products/{id}/variants/bulk"
}

{
  "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/products/{id}/variants/bulk"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/products/{id}/variants/bulk"
}

Update a variant

Patch a single variant: only supplied fields are updated. This is how you set the real price of a VARIABLE product. Supports Idempotency-Key header for safe retries.

PATCH
/v1/products/{id}/variants/{variantId}
AuthorizationBearer <token>

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

idstring

Parent product ID

variantIdstring

Variant ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
sku?string

Stock keeping unit

gtin?string

GTIN (UPC/EAN/JAN/ISBN). Falls back to product GTIN when omitted.

mpn?string

Manufacturer part number. Falls back to product MPN when omitted.

Lengthlength <= 70
name?string

Variant display name

attributes?object

Variant attribute map

Empty Object

price?number

Variant price (in minor units of store currency)

salePrice?number

Variant sale price

costPrice?number | null

Variant unit cost (COGS) override, which falls back to the product costPrice for margin analytics. Pass null to clear. Merchant-internal: never exposed on public storefront responses.

image?object

Variant-specific image (URL string or image object)

Empty Object

position?number

Position in variant list (0-indexed)

Range0 <= value
status?string

Variant status

Value in"active" | "draft"
isDownloadable?boolean

Whether this variant is a digital download

downloadData?object

Download payload for digital variants (files + limits)

Empty Object

shippingClassId?string | null

Per-variant shipping class ID. null/undefined inherits from parent product.

taxClassId?string | null

Per-variant tax class ID. null clears it; resolution then falls back to product → category → store default.

channels?object

Per-channel overrides keyed by vc_* connectionId. Value is a partial variant object; a nested translations[locale] object holds per-channel localized fields.

Empty Object

curl -X PATCH "https://api.brainerce.com/api/v1/products/string/variants/string" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{}'
{
  "id": "string",
  "productId": "string",
  "sku": "string",
  "name": "string",
  "attributes": {},
  "price": "19.90",
  "salePrice": "string",
  "costPrice": "string",
  "displayPrice": "string",
  "displaySalePrice": "string",
  "displayCurrency": "string",
  "image": "string",
  "position": 0,
  "status": "string",
  "inventory": {
    "trackingMode": "TRACKED",
    "total": 42,
    "reserved": 3,
    "available": 39,
    "inStock": true,
    "canPurchase": true,
    "lastInventorySyncAt": "2019-08-24T14:15:22Z"
  },
  "isDownloadable": true,
  "downloadData": {
    "files": [
      {
        "id": "string",
        "name": "string",
        "url": "string",
        "storageKey": "string",
        "size": 0,
        "mimeType": "string"
      }
    ],
    "downloadLimit": 0,
    "downloadExpiry": 0
  },
  "shippingClassId": "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/products/{id}/variants/{variantId}"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/products/{id}/variants/{variantId}"
}

{
  "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/products/{id}/variants/{variantId}"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/products/{id}/variants/{variantId}"
}

Delete a variant

Delete a variant by ID. Supports Idempotency-Key header for safe retries.

DELETE
/v1/products/{id}/variants/{variantId}
AuthorizationBearer <token>

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

idstring

Parent product ID

variantIdstring

Variant ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
curl -X DELETE "https://api.brainerce.com/api/v1/products/string/variants/string" \  -H "Idempotency-Key: string"
Empty

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/products/{id}/variants/{variantId}"
}

{
  "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/products/{id}/variants/{variantId}"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/products/{id}/variants/{variantId}"
}

Get variant inventory

GET
/v1/products/{id}/variants/{variantId}/inventory
AuthorizationBearer <token>

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

idstring

Parent product ID

variantIdstring

Variant ID

curl -X GET "https://api.brainerce.com/api/v1/products/string/variants/string/inventory"
{
  "trackingMode": "TRACKED",
  "total": 0,
  "reserved": 0,
  "available": 0,
  "inStock": true,
  "canPurchase": true,
  "lastInventorySyncAt": "2019-08-24T14:15:22Z"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/products/{id}/variants/{variantId}/inventory"
}

{
  "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/products/{id}/variants/{variantId}/inventory"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/products/{id}/variants/{variantId}/inventory"
}

Update variant inventory

Adjust a variant’s inventory. Provide newTotal to set absolute stock, trackingMode to switch tracking strategy, and optionally reason for the audit log. Supports Idempotency-Key header for safe retries.

PATCH
/v1/products/{id}/variants/{variantId}/inventory
AuthorizationBearer <token>

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

idstring

Parent product ID

variantIdstring

Variant ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
newTotal?number

New absolute stock total. Must be a non-negative integer.

Range0 <= value
trackingMode?string

Inventory tracking strategy for this variant

Value in"TRACKED" | "UNLIMITED" | "DISABLED"
reason?string

Optional reason for the adjustment (recorded in the audit log)

curl -X PATCH "https://api.brainerce.com/api/v1/products/string/variants/string/inventory" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{}'
{
  "trackingMode": "TRACKED",
  "total": 0,
  "reserved": 0,
  "available": 0,
  "inStock": true,
  "canPurchase": true,
  "lastInventorySyncAt": "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/products/{id}/variants/{variantId}/inventory"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/products/{id}/variants/{variantId}/inventory"
}

{
  "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/products/{id}/variants/{variantId}/inventory"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/products/{id}/variants/{variantId}/inventory"
}

Publish a product to a sales-channel site

Makes the product visible on the given sales channel (vibe-coded storefront). Accepts either the sales-channel record ID or its public vc_* connection ID. Supports Idempotency-Key header for safe retries.

POST
/v1/products/{id}/publish-sales-channel
AuthorizationBearer <token>

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

idstring

Product ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
salesChannelIdstring

Sales channel ID (record ID or vc_* connection ID)

curl -X POST "https://api.brainerce.com/api/v1/products/string/publish-sales-channel" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{    "salesChannelId": "string"  }'
{
  "success": true,
  "publish": {
    "id": "clx1a2b3c4d5e6f7g8h9",
    "salesChannelId": "string",
    "storeId": "string",
    "createdAt": "2019-08-24T14:15:22Z",
    "updatedAt": "2019-08-24T14:15:22Z",
    "productId": "string"
  },
  "connectionName": "My storefront"
}

{
  "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/products/{id}/publish-sales-channel"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/products/{id}/publish-sales-channel"
}

{
  "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/products/{id}/publish-sales-channel"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/products/{id}/publish-sales-channel"
}

Unpublish a product from a sales-channel site

Hides the product from the given sales channel and clears its per-channel overrides. Accepts either the sales-channel record ID or its public vc_* connection ID. Supports Idempotency-Key header for safe retries.

POST
/v1/products/{id}/unpublish-sales-channel
AuthorizationBearer <token>

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

idstring

Product ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
salesChannelIdstring

Sales channel ID (record ID or vc_* connection ID)

curl -X POST "https://api.brainerce.com/api/v1/products/string/unpublish-sales-channel" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{    "salesChannelId": "string"  }'
{
  "success": true
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/products/{id}/unpublish-sales-channel"
}

{
  "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/products/{id}/unpublish-sales-channel"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/products/{id}/unpublish-sales-channel"
}

Publish a coupon to a sales-channel site

Makes the coupon redeemable on the given sales channel (vibe-coded storefront). Accepts either the sales-channel record ID or its public vc_* connection ID. Supports Idempotency-Key header for safe retries.

POST
/v1/coupons/{id}/publish-sales-channel
AuthorizationBearer <token>

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

idstring

Coupon ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
salesChannelIdstring

Sales channel ID (record ID or vc_* connection ID)

curl -X POST "https://api.brainerce.com/api/v1/coupons/string/publish-sales-channel" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{    "salesChannelId": "string"  }'
{
  "success": true,
  "publish": {
    "id": "clx1a2b3c4d5e6f7g8h9",
    "salesChannelId": "string",
    "storeId": "string",
    "createdAt": "2019-08-24T14:15:22Z",
    "updatedAt": "2019-08-24T14:15:22Z",
    "couponId": "string"
  },
  "connectionName": "My storefront"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/coupons/{id}/publish-sales-channel"
}

{
  "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/coupons/{id}/publish-sales-channel"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/coupons/{id}/publish-sales-channel"
}

Unpublish a coupon from a sales-channel site

Supports Idempotency-Key header for safe retries.

POST
/v1/coupons/{id}/unpublish-sales-channel
AuthorizationBearer <token>

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

idstring

Coupon ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
salesChannelIdstring

Sales channel ID (record ID or vc_* connection ID)

curl -X POST "https://api.brainerce.com/api/v1/coupons/string/unpublish-sales-channel" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{    "salesChannelId": "string"  }'
{
  "success": true
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/coupons/{id}/unpublish-sales-channel"
}

{
  "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/coupons/{id}/unpublish-sales-channel"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/coupons/{id}/unpublish-sales-channel"
}

List media library assets

GET
/v1/media
AuthorizationBearer <token>

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

Query Parameters

page?number
limit?number
search?string

Filename / name search

curl -X GET "https://api.brainerce.com/api/v1/media?page=0&limit=0&search=string"
{
  "data": [
    {
      "id": "string",
      "url": "string",
      "key": "string",
      "thumbnailUrl": "string",
      "mimeType": "image/webp",
      "width": 0,
      "height": 0,
      "size": 0,
      "name": "string",
      "alt": "string",
      "createdAt": "2019-08-24T14:15:22Z"
    }
  ],
  "meta": {
    "page": 1,
    "limit": 20,
    "total": 137,
    "totalPages": 7
  }
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/media"
}

{
  "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/media"
}

Upload or ingest a media asset

Send multipart/form-data with either a binary file part (≤10 MB) or a sourceUrl field to ingest a remote image by URL (fetched server-side through the SSRF-safe downloader). Returns the created asset.

POST
/v1/media
AuthorizationBearer <token>

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

file?string

Binary file (≤10 MB)

sourceUrl?string

Remote http(s) image URL to ingest (alternative to file)

curl -X POST "https://api.brainerce.com/api/v1/media"
{
  "id": "string",
  "url": "string",
  "key": "string",
  "thumbnailUrl": "string",
  "mimeType": "image/webp",
  "width": 0,
  "height": 0,
  "size": 0,
  "name": "string",
  "alt": "string",
  "createdAt": "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/media"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/media"
}

{
  "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/media"
}

{
  "statusCode": 429,
  "code": "RATE_LIMITED",
  "message": "Too many requests",
  "details": {
    "retryAfterSeconds": 12
  },
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/media"
}

Get a media asset by ID

GET
/v1/media/{id}
AuthorizationBearer <token>

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

idstring

Asset ID

curl -X GET "https://api.brainerce.com/api/v1/media/string"
{
  "id": "string",
  "url": "string",
  "key": "string",
  "thumbnailUrl": "string",
  "mimeType": "image/webp",
  "width": 0,
  "height": 0,
  "size": 0,
  "name": "string",
  "alt": "string",
  "createdAt": "2019-08-24T14:15:22Z"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/media/{id}"
}

{
  "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/media/{id}"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/media/{id}"
}

Update media asset metadata

Update the asset’s alt text and/or display name. Supports Idempotency-Key.

PATCH
/v1/media/{id}
AuthorizationBearer <token>

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

idstring

Asset ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
alt?string

Accessibility alt text for the asset.

name?string

Display name / title of the asset in the media library.

curl -X PATCH "https://api.brainerce.com/api/v1/media/string" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{}'
{
  "id": "string",
  "url": "string",
  "key": "string",
  "thumbnailUrl": "string",
  "mimeType": "image/webp",
  "width": 0,
  "height": 0,
  "size": 0,
  "name": "string",
  "alt": "string",
  "createdAt": "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/media/{id}"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/media/{id}"
}

{
  "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/media/{id}"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/media/{id}"
}

Delete a media asset

Soft-deletes the asset and removes its key from every product/variant/category/brand/store/modifier that referenced it. Supports Idempotency-Key.

DELETE
/v1/media/{id}
AuthorizationBearer <token>

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

idstring

Asset ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
curl -X DELETE "https://api.brainerce.com/api/v1/media/string" \  -H "Idempotency-Key: string"
{
  "success": true
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/media/{id}"
}

{
  "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/media/{id}"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/media/{id}"
}

List orders with pagination

GET
/v1/orders
AuthorizationBearer <token>

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

Query Parameters

page?number
limit?number
status?string
sortBy?string
sortOrder?string
curl -X GET "https://api.brainerce.com/api/v1/orders?page=0&limit=0&status=string&sortBy=string&sortOrder=string"
{
  "data": [
    {
      "id": "string",
      "storeId": "string",
      "externalId": "string",
      "orderNumber": "string",
      "platform": "string",
      "sourceName": "string",
      "totalAmount": "string",
      "subtotal": "string",
      "taxAmount": "string",
      "taxBreakdown": {
        "subtotal": 0,
        "shippingNet": 0,
        "totalTax": 0,
        "total": 0,
        "pricesIncludeTax": true,
        "breakdown": [
          {
            "name": "VAT",
            "rate": 17,
            "amount": 12.45
          }
        ],
        "currency": "string"
      },
      "shippingAmount": "string",
      "discountAmount": "string",
      "couponCode": "string",
      "couponDiscount": "string",
      "coupon": {
        "title": "string",
        "type": "string",
        "value": "string"
      },
      "surchargeAmount": "string",
      "appliedSurcharges": [
        {
          "key": "string",
          "name": "string",
          "value": {},
          "amount": 0
        }
      ],
      "customFieldValues": {},
      "adminFieldValues": {},
      "ruleDiscountAmount": "string",
      "appliedRules": [
        {
          "id": "string",
          "name": "string",
          "type": "string",
          "discountAmount": "string"
        }
      ],
      "currency": "string",
      "status": "string",
      "financialStatus": "string",
      "statusLabel": {
        "id": "string",
        "name": "string",
        "color": "string",
        "canonicalStatus": "string",
        "isSystem": true
      },
      "notes": "string",
      "paymentMethod": "string",
      "isTestOrder": true,
      "customer": {
        "email": "string",
        "name": "string"
      },
      "shippingAddress": {
        "firstName": "string",
        "lastName": "string",
        "name": "string",
        "company": "string",
        "line1": "string",
        "line2": "string",
        "city": "string",
        "state": "string",
        "country": "string",
        "postalCode": "string",
        "phone": "string"
      },
      "billingAddress": {
        "firstName": "string",
        "lastName": "string",
        "name": "string",
        "company": "string",
        "line1": "string",
        "line2": "string",
        "city": "string",
        "state": "string",
        "country": "string",
        "postalCode": "string",
        "phone": "string"
      },
      "items": [
        {
          "productId": "string",
          "variantId": "string",
          "sku": "string",
          "name": "string",
          "quantity": 0,
          "price": "string",
          "unitPrice": "string",
          "totalPrice": "string",
          "image": "string"
        }
      ],
      "createdAt": "2019-08-24T14:15:22Z",
      "syncWarning": "string",
      "paymentStatus": "string",
      "paymentProviderType": "string",
      "paymentProviderName": "string",
      "paymentId": "string",
      "paymentExternalId": "string",
      "paymentAmount": "string",
      "paymentCurrency": "string",
      "paymentRefundedAmount": "string",
      "invoiceUrl": "string",
      "invoiceNumber": "string",
      "numberOfPayments": 0,
      "paymentCapabilities": {
        "refund": true,
        "manualRefundUrl": "string"
      },
      "refunds": [
        {
          "id": "string",
          "amount": "string",
          "reason": "string",
          "status": "string",
          "type": "string",
          "source": "string",
          "createdAt": "2019-08-24T14:15:22Z"
        }
      ],
      "tenders": [
        {
          "id": "string",
          "type": "string",
          "amountBase": "string",
          "currencyBase": "string",
          "giftCard": {
            "id": "string",
            "codeLast4": "string"
          }
        }
      ],
      "isRead": true,
      "downloadMeta": {
        "files": [
          {
            "fileId": "string",
            "productId": "string",
            "productName": "string",
            "fileName": "string",
            "downloadsUsed": 0,
            "downloadLimit": 0,
            "expiresAt": "2019-08-24T14:15:22Z"
          }
        ]
      },
      "trackingNumber": "string",
      "trackingUrl": "string",
      "carrier": "string",
      "shippingSelection": {
        "carrier": "string",
        "service": "string",
        "methodName": "string",
        "amount": "string"
      },
      "deliveryType": "string",
      "pickupLocationData": {
        "name": "string",
        "line1": "string",
        "line2": "string",
        "city": "string",
        "region": "string",
        "postalCode": "string",
        "country": "string",
        "phone": "string",
        "rateName": "string"
      }
    }
  ],
  "meta": {
    "page": 1,
    "limit": 20,
    "total": 137,
    "totalPages": 7
  }
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/orders"
}

{
  "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/orders"
}

Create a new order

Honors Idempotency-Key header: retries with the same key return the original response. See /docs/api/idempotency.

POST
/v1/orders
AuthorizationBearer <token>

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

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
itemsarray<CreateOrderItemDto>

Line items being purchased (maximum 500)

customer?CreateOrderCustomerDto

Customer details for the order. Optional for guest orders.

customerId?string

Existing Brainerce customer ID (mutually exclusive with customer)

externalId?string

External system identifier for this order (idempotency aid for migrations).

status?string

Initial order status. Most callers should NOT set this, because the system manages status transitions through the payment + fulfillment pipeline. Setting to a paid-looking status (e.g. SHIPPED, DELIVERED) without going through payment will be rejected: the controller currently forces the status to PENDING on creation regardless of caller input, to prevent payment-bypass.

Value in"PENDING" | "PROCESSING" | "SHIPPED" | "DELIVERED" | "CANCELLED" | "REFUNDED"
currency?string

Currency code (ISO-4217). Defaults to the store currency.

discountAmount?number

Total discount amount applied

couponCode?string

Applied coupon code (snapshot)

shippingAmount?number

Shipping amount

taxAmount?number

Tax amount

paymentMethod?string

Payment method identifier

notes?string

Free-form notes attached to the order

sourceName?string

Source name (e.g. "web", "pos", "import")

shippingAddress?object

Optional shipping address. Shape matches the standard OrderAddress payload.

Empty Object

billingAddress?object

Optional billing address. Shape matches the standard OrderAddress payload.

Empty Object

curl -X POST "https://api.brainerce.com/api/v1/orders" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{    "items": [      {        "productId": "prd_01H...",        "quantity": 1,        "price": 19.99      }    ]  }'
{
  "id": "string",
  "storeId": "string",
  "externalId": "string",
  "orderNumber": "string",
  "platform": "string",
  "sourceName": "string",
  "totalAmount": "string",
  "subtotal": "string",
  "taxAmount": "string",
  "taxBreakdown": {
    "subtotal": 0,
    "shippingNet": 0,
    "totalTax": 0,
    "total": 0,
    "pricesIncludeTax": true,
    "breakdown": [
      {
        "name": "VAT",
        "rate": 17,
        "amount": 12.45
      }
    ],
    "currency": "string"
  },
  "shippingAmount": "string",
  "discountAmount": "string",
  "couponCode": "string",
  "couponDiscount": "string",
  "coupon": {
    "title": "string",
    "type": "string",
    "value": "string"
  },
  "surchargeAmount": "string",
  "appliedSurcharges": [
    {
      "key": "string",
      "name": "string",
      "value": {},
      "amount": 0
    }
  ],
  "customFieldValues": {},
  "adminFieldValues": {},
  "ruleDiscountAmount": "string",
  "appliedRules": [
    {
      "id": "string",
      "name": "string",
      "type": "string",
      "discountAmount": "string"
    }
  ],
  "currency": "string",
  "status": "string",
  "financialStatus": "string",
  "statusLabel": {
    "id": "string",
    "name": "string",
    "color": "string",
    "canonicalStatus": "string",
    "isSystem": true
  },
  "notes": "string",
  "paymentMethod": "string",
  "isTestOrder": true,
  "customer": {
    "email": "string",
    "name": "string"
  },
  "shippingAddress": {
    "firstName": "string",
    "lastName": "string",
    "name": "string",
    "company": "string",
    "line1": "string",
    "line2": "string",
    "city": "string",
    "state": "string",
    "country": "string",
    "postalCode": "string",
    "phone": "string"
  },
  "billingAddress": {
    "firstName": "string",
    "lastName": "string",
    "name": "string",
    "company": "string",
    "line1": "string",
    "line2": "string",
    "city": "string",
    "state": "string",
    "country": "string",
    "postalCode": "string",
    "phone": "string"
  },
  "items": [
    {
      "productId": "string",
      "variantId": "string",
      "sku": "string",
      "name": "string",
      "quantity": 0,
      "price": "string",
      "unitPrice": "string",
      "totalPrice": "string",
      "image": "string"
    }
  ],
  "createdAt": "2019-08-24T14:15:22Z",
  "syncWarning": "string",
  "paymentStatus": "string",
  "paymentProviderType": "string",
  "paymentProviderName": "string",
  "paymentId": "string",
  "paymentExternalId": "string",
  "paymentAmount": "string",
  "paymentCurrency": "string",
  "paymentRefundedAmount": "string",
  "invoiceUrl": "string",
  "invoiceNumber": "string",
  "numberOfPayments": 0,
  "paymentCapabilities": {
    "refund": true,
    "manualRefundUrl": "string"
  },
  "refunds": [
    {
      "id": "string",
      "amount": "string",
      "reason": "string",
      "status": "string",
      "type": "string",
      "source": "string",
      "createdAt": "2019-08-24T14:15:22Z"
    }
  ],
  "tenders": [
    {
      "id": "string",
      "type": "string",
      "amountBase": "string",
      "currencyBase": "string",
      "giftCard": {
        "id": "string",
        "codeLast4": "string"
      }
    }
  ],
  "isRead": true,
  "downloadMeta": {
    "files": [
      {
        "fileId": "string",
        "productId": "string",
        "productName": "string",
        "fileName": "string",
        "downloadsUsed": 0,
        "downloadLimit": 0,
        "expiresAt": "2019-08-24T14:15:22Z"
      }
    ]
  },
  "trackingNumber": "string",
  "trackingUrl": "string",
  "carrier": "string",
  "shippingSelection": {
    "carrier": "string",
    "service": "string",
    "methodName": "string",
    "amount": "string"
  },
  "deliveryType": "string",
  "pickupLocationData": {
    "name": "string",
    "line1": "string",
    "line2": "string",
    "city": "string",
    "region": "string",
    "postalCode": "string",
    "country": "string",
    "phone": "string",
    "rateName": "string"
  }
}

{
  "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/orders"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/orders"
}

{
  "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/orders"
}

Get a single order by ID

GET
/v1/orders/{id}
AuthorizationBearer <token>

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

idstring

Order ID

curl -X GET "https://api.brainerce.com/api/v1/orders/string"
{
  "id": "string",
  "storeId": "string",
  "externalId": "string",
  "orderNumber": "string",
  "platform": "string",
  "sourceName": "string",
  "totalAmount": "string",
  "subtotal": "string",
  "taxAmount": "string",
  "taxBreakdown": {
    "subtotal": 0,
    "shippingNet": 0,
    "totalTax": 0,
    "total": 0,
    "pricesIncludeTax": true,
    "breakdown": [
      {
        "name": "VAT",
        "rate": 17,
        "amount": 12.45
      }
    ],
    "currency": "string"
  },
  "shippingAmount": "string",
  "discountAmount": "string",
  "couponCode": "string",
  "couponDiscount": "string",
  "coupon": {
    "title": "string",
    "type": "string",
    "value": "string"
  },
  "surchargeAmount": "string",
  "appliedSurcharges": [
    {
      "key": "string",
      "name": "string",
      "value": {},
      "amount": 0
    }
  ],
  "customFieldValues": {},
  "adminFieldValues": {},
  "ruleDiscountAmount": "string",
  "appliedRules": [
    {
      "id": "string",
      "name": "string",
      "type": "string",
      "discountAmount": "string"
    }
  ],
  "currency": "string",
  "status": "string",
  "financialStatus": "string",
  "statusLabel": {
    "id": "string",
    "name": "string",
    "color": "string",
    "canonicalStatus": "string",
    "isSystem": true
  },
  "notes": "string",
  "paymentMethod": "string",
  "isTestOrder": true,
  "customer": {
    "email": "string",
    "name": "string"
  },
  "shippingAddress": {
    "firstName": "string",
    "lastName": "string",
    "name": "string",
    "company": "string",
    "line1": "string",
    "line2": "string",
    "city": "string",
    "state": "string",
    "country": "string",
    "postalCode": "string",
    "phone": "string"
  },
  "billingAddress": {
    "firstName": "string",
    "lastName": "string",
    "name": "string",
    "company": "string",
    "line1": "string",
    "line2": "string",
    "city": "string",
    "state": "string",
    "country": "string",
    "postalCode": "string",
    "phone": "string"
  },
  "items": [
    {
      "productId": "string",
      "variantId": "string",
      "sku": "string",
      "name": "string",
      "quantity": 0,
      "price": "string",
      "unitPrice": "string",
      "totalPrice": "string",
      "image": "string"
    }
  ],
  "createdAt": "2019-08-24T14:15:22Z",
  "syncWarning": "string",
  "paymentStatus": "string",
  "paymentProviderType": "string",
  "paymentProviderName": "string",
  "paymentId": "string",
  "paymentExternalId": "string",
  "paymentAmount": "string",
  "paymentCurrency": "string",
  "paymentRefundedAmount": "string",
  "invoiceUrl": "string",
  "invoiceNumber": "string",
  "numberOfPayments": 0,
  "paymentCapabilities": {
    "refund": true,
    "manualRefundUrl": "string"
  },
  "refunds": [
    {
      "id": "string",
      "amount": "string",
      "reason": "string",
      "status": "string",
      "type": "string",
      "source": "string",
      "createdAt": "2019-08-24T14:15:22Z"
    }
  ],
  "tenders": [
    {
      "id": "string",
      "type": "string",
      "amountBase": "string",
      "currencyBase": "string",
      "giftCard": {
        "id": "string",
        "codeLast4": "string"
      }
    }
  ],
  "isRead": true,
  "downloadMeta": {
    "files": [
      {
        "fileId": "string",
        "productId": "string",
        "productName": "string",
        "fileName": "string",
        "downloadsUsed": 0,
        "downloadLimit": 0,
        "expiresAt": "2019-08-24T14:15:22Z"
      }
    ]
  },
  "trackingNumber": "string",
  "trackingUrl": "string",
  "carrier": "string",
  "shippingSelection": {
    "carrier": "string",
    "service": "string",
    "methodName": "string",
    "amount": "string"
  },
  "deliveryType": "string",
  "pickupLocationData": {
    "name": "string",
    "line1": "string",
    "line2": "string",
    "city": "string",
    "region": "string",
    "postalCode": "string",
    "country": "string",
    "phone": "string",
    "rateName": "string"
  }
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/orders/{id}"
}

{
  "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/orders/{id}"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/orders/{id}"
}

Update an order

Supports Idempotency-Key header for safe retries.

PATCH
/v1/orders/{id}
AuthorizationBearer <token>

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

idstring

Order ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
status?string

New order status

curl -X PATCH "https://api.brainerce.com/api/v1/orders/string" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{}'
{
  "id": "string",
  "storeId": "string",
  "externalId": "string",
  "orderNumber": "string",
  "platform": "string",
  "sourceName": "string",
  "totalAmount": "string",
  "subtotal": "string",
  "taxAmount": "string",
  "taxBreakdown": {
    "subtotal": 0,
    "shippingNet": 0,
    "totalTax": 0,
    "total": 0,
    "pricesIncludeTax": true,
    "breakdown": [
      {
        "name": "VAT",
        "rate": 17,
        "amount": 12.45
      }
    ],
    "currency": "string"
  },
  "shippingAmount": "string",
  "discountAmount": "string",
  "couponCode": "string",
  "couponDiscount": "string",
  "coupon": {
    "title": "string",
    "type": "string",
    "value": "string"
  },
  "surchargeAmount": "string",
  "appliedSurcharges": [
    {
      "key": "string",
      "name": "string",
      "value": {},
      "amount": 0
    }
  ],
  "customFieldValues": {},
  "adminFieldValues": {},
  "ruleDiscountAmount": "string",
  "appliedRules": [
    {
      "id": "string",
      "name": "string",
      "type": "string",
      "discountAmount": "string"
    }
  ],
  "currency": "string",
  "status": "string",
  "financialStatus": "string",
  "statusLabel": {
    "id": "string",
    "name": "string",
    "color": "string",
    "canonicalStatus": "string",
    "isSystem": true
  },
  "notes": "string",
  "paymentMethod": "string",
  "isTestOrder": true,
  "customer": {
    "email": "string",
    "name": "string"
  },
  "shippingAddress": {
    "firstName": "string",
    "lastName": "string",
    "name": "string",
    "company": "string",
    "line1": "string",
    "line2": "string",
    "city": "string",
    "state": "string",
    "country": "string",
    "postalCode": "string",
    "phone": "string"
  },
  "billingAddress": {
    "firstName": "string",
    "lastName": "string",
    "name": "string",
    "company": "string",
    "line1": "string",
    "line2": "string",
    "city": "string",
    "state": "string",
    "country": "string",
    "postalCode": "string",
    "phone": "string"
  },
  "items": [
    {
      "productId": "string",
      "variantId": "string",
      "sku": "string",
      "name": "string",
      "quantity": 0,
      "price": "string",
      "unitPrice": "string",
      "totalPrice": "string",
      "image": "string"
    }
  ],
  "createdAt": "2019-08-24T14:15:22Z",
  "syncWarning": "string",
  "paymentStatus": "string",
  "paymentProviderType": "string",
  "paymentProviderName": "string",
  "paymentId": "string",
  "paymentExternalId": "string",
  "paymentAmount": "string",
  "paymentCurrency": "string",
  "paymentRefundedAmount": "string",
  "invoiceUrl": "string",
  "invoiceNumber": "string",
  "numberOfPayments": 0,
  "paymentCapabilities": {
    "refund": true,
    "manualRefundUrl": "string"
  },
  "refunds": [
    {
      "id": "string",
      "amount": "string",
      "reason": "string",
      "status": "string",
      "type": "string",
      "source": "string",
      "createdAt": "2019-08-24T14:15:22Z"
    }
  ],
  "tenders": [
    {
      "id": "string",
      "type": "string",
      "amountBase": "string",
      "currencyBase": "string",
      "giftCard": {
        "id": "string",
        "codeLast4": "string"
      }
    }
  ],
  "isRead": true,
  "downloadMeta": {
    "files": [
      {
        "fileId": "string",
        "productId": "string",
        "productName": "string",
        "fileName": "string",
        "downloadsUsed": 0,
        "downloadLimit": 0,
        "expiresAt": "2019-08-24T14:15:22Z"
      }
    ]
  },
  "trackingNumber": "string",
  "trackingUrl": "string",
  "carrier": "string",
  "shippingSelection": {
    "carrier": "string",
    "service": "string",
    "methodName": "string",
    "amount": "string"
  },
  "deliveryType": "string",
  "pickupLocationData": {
    "name": "string",
    "line1": "string",
    "line2": "string",
    "city": "string",
    "region": "string",
    "postalCode": "string",
    "country": "string",
    "phone": "string",
    "rateName": "string"
  }
}

{
  "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/orders/{id}"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/orders/{id}"
}

{
  "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/orders/{id}"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/orders/{id}"
}

Live carrier rates for an order

GET
/v1/orders/{id}/shipments/app-rates
AuthorizationBearer <token>

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

idstring

Order ID

curl -X GET "https://api.brainerce.com/api/v1/orders/string/shipments/app-rates"
[
  {
    "id": "string",
    "name": "USPS Priority",
    "description": "string",
    "price": "string",
    "currency": "string",
    "estimatedDays": 0,
    "carrier": "string",
    "service": "string"
  }
]

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/orders/{id}/shipments/app-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/orders/{id}/shipments/app-rates"
}

Buy a shipping label via the installed shipping app

POST
/v1/orders/{id}/shipments/app-label
AuthorizationBearer <token>

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

idstring

Order ID

rateIdstring

Opaque rate id returned by the installed shipping app, passed back verbatim. Do not parse it.

parcel?CreateAppLabelParcelDto

Override default parcel dimensions from app config

labelFormat?string

Label file format. ZPL/EPL drive warehouse thermal printers; PDF prints from a browser. If the carrier cannot produce the requested format it returns its closest match.

Default"PDF"
Value in"PDF" | "PNG" | "ZPL" | "EPL"
customsContentsType?string

Customs contents type for a cross-border shipment. Ignored for domestic labels.

Default"merchandise"
Value in"merchandise" | "gift" | "documents" | "sample" | "return"
curl -X POST "https://api.brainerce.com/api/v1/orders/string/shipments/app-label" \  -H "Content-Type: application/json" \  -d '{    "rateId": "rate_8f123456789abcdef"  }'
{
  "shipmentId": "string",
  "labelUrl": "string",
  "trackingNumber": "string",
  "carrier": "string"
}

{
  "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/orders/{id}/shipments/app-label"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/orders/{id}/shipments/app-label"
}

{
  "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/orders/{id}/shipments/app-label"
}

Shipments and tracking history for an order

GET
/v1/orders/{id}/shipments
AuthorizationBearer <token>

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

idstring

Order ID

curl -X GET "https://api.brainerce.com/api/v1/orders/string/shipments"
[
  {
    "id": "string",
    "carrier": "ups",
    "service": "ground",
    "status": "string",
    "trackingNumber": "string",
    "trackingUrl": "string",
    "labelUrl": "string",
    "labelFormat": "string",
    "estimatedDeliveryDate": "2019-08-24T14:15:22Z",
    "actualDeliveryDate": "2019-08-24T14:15:22Z",
    "rate": "string",
    "rateCurrency": "string",
    "createdAt": "2019-08-24T14:15:22Z",
    "events": [
      {
        "status": "in_transit",
        "statusDetail": "arrived_at_facility",
        "message": "string",
        "location": {},
        "occurredAt": "2019-08-24T14:15:22Z"
      }
    ]
  }
]

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/orders/{id}/shipments"
}

{
  "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/orders/{id}/shipments"
}

List order custom field definitions

The merchant-defined fields that can be written onto an order. Call this first to discover the key and type of each field — those keys are what PATCH /v1/orders/{id}/custom-fields accepts. Inactive definitions are included so a caller can tell "the merchant disabled this field" apart from "the merchant never created it".

GET
/v1/order-custom-fields
AuthorizationBearer <token>

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/order-custom-fields"
[
  {
    "id": "string",
    "storeId": "string",
    "name": "Licence key",
    "key": "licence_key",
    "description": "string",
    "type": "TEXT",
    "required": true,
    "isPublic": true,
    "position": 0,
    "isActive": true,
    "options": {},
    "minLength": 0,
    "maxLength": 0,
    "minValue": "string",
    "maxValue": "string",
    "dateAvailability": {},
    "translations": {},
    "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/order-custom-fields"
}

{
  "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/order-custom-fields"
}

Get the custom field values on an order

Returns the stored values keyed by definition key. These are the merchant-managed fields on the order (adminFieldValues), not the ones a shopper filled in at checkout.

GET
/v1/orders/{id}/custom-fields
AuthorizationBearer <token>

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

idstring

Order ID

curl -X GET "https://api.brainerce.com/api/v1/orders/string/custom-fields"
{
  "orderId": "string",
  "fields": {
    "licence_key": "ABCD-EFGH-IJKL",
    "activated_at": "2026-09-09"
  }
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/orders/{id}/custom-fields"
}

{
  "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/orders/{id}/custom-fields"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/orders/{id}/custom-fields"
}

Set custom field values on an order

Merges the supplied values into the order. Omitted keys keep their current value; null clears a non-required field. Values are coerced to the definition type and rejected with a 400 when they cannot be — a key with no active definition is ignored instead, so read the response to confirm what was stored.

Where a value ends up: it shows on the order in the dashboard, reaches the storefront through the SDK when the definition's isPublic is on, and travels to every order email as orderCustomFields. No default email template prints that variable — until the merchant adds the block to their template once, a value written here is stored correctly and seen by nobody. Writing a field is not the same as the customer being told.

Supports Idempotency-Key header for safe retries.

PATCH
/v1/orders/{id}/custom-fields
AuthorizationBearer <token>

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

idstring

Order ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
fieldsobject

Values to write, keyed by definition key. This is a MERGE: keys you omit keep their current value. Send null to clear one (a required field refuses this). A key with no active definition on the store is IGNORED rather than rejected, so a caller writing an optional field the merchant has not created yet does not fail the whole request — read the response fields to see what was actually stored.

Empty Object

curl -X PATCH "https://api.brainerce.com/api/v1/orders/string/custom-fields" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{    "fields": {      "licence_key": "ABCD-EFGH-IJKL"    }  }'
{
  "orderId": "string",
  "fields": {
    "licence_key": "ABCD-EFGH-IJKL",
    "activated_at": "2026-09-09"
  }
}

{
  "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/orders/{id}/custom-fields"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/orders/{id}/custom-fields"
}

{
  "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/orders/{id}/custom-fields"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/orders/{id}/custom-fields"
}

[Not implemented] Trigger a sync to platforms

This endpoint is not implemented. It returns 501 Not Implemented. Use the per-resource publish endpoints (e.g. POST /v1/coupons/{id}/sync, POST /v1/categories/{id}/publish-sales-channel) instead.

POST
/v1/sync
AuthorizationBearer <token>

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 POST "https://api.brainerce.com/api/v1/sync"

{
  "statusCode": 501,
  "code": "NOT_IMPLEMENTED",
  "message": "The generic sync trigger has been removed; use per-resource publish endpoints",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/sync"
}

[Not implemented] Get sync job status

This endpoint is not implemented. It returns 501 Not Implemented. There is no generic sync job pipeline behind /v1/sync.

GET
/v1/sync/{jobId}
AuthorizationBearer <token>

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

jobIdstring

Sync Job ID

curl -X GET "https://api.brainerce.com/api/v1/sync/string"

{
  "statusCode": 501,
  "code": "NOT_IMPLEMENTED",
  "message": "The generic sync trigger has been removed; use per-resource publish endpoints",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/sync/{jobId}"
}

Create a new customer

Honors Idempotency-Key header. See /docs/api/idempotency.

POST
/v1/customers
AuthorizationBearer <token>

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

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
emailstring

Customer email, unique per store. Used for login (when password is set) and order lookup.

phone?string

Phone number (E.164 format recommended for SMS notifications).

firstName?string

First name.

lastName?string

Last name.

password?string

Plaintext password. When provided, the customer is created as a registered account (can log in). When omitted, a guest record is created (orders can still link by email).

Length8 <= length
acceptsMarketing?boolean

Marketing consent flag. Required for transactional vs marketing email split.

birthMonth?number

Birthday month (1-12) for the loyalty birthday gift. Month and day only, with no year. Send it together with birthDay or not at all; one without the other is rejected.

Range1 <= value <= 12
birthDay?number

Birthday day of month (1-31). Send it together with birthMonth or not at all. The day must exist in that month, so 30 February is rejected.

Range1 <= value <= 31
tags?array<string>

Free-form tags for segmentation (VIP, B2B, beta-tester, …).

role?string

Free-form customer segment/role set by the merchant (e.g. "wholesale", "vip", "ambassador"). Returned on /customers/me so storefront code can branch on it to build custom, role-gated features. Not settable by the customer themselves: admin dashboard/API only.

Lengthlength <= 50
acquisitionSalesChannelId?string

FIRST-TOUCH sales channel for a customer being created here. Creating a customer through the dashboard or API is not itself a channel sighting, so this stays null unless you say otherwise. Set it when you know where the person actually came from (migrating from another system, a phone order taken for a specific storefront). Accepts the internal SalesChannel id or the public vc_* connectionId. Attach them to the channels they SHOP on separately, via the publish endpoint.

metadata?object

Arbitrary JSON metadata. Useful for storing integration-specific IDs (your ERP customer number, CRM contact id, …).

Empty Object

curl -X POST "https://api.brainerce.com/api/v1/customers" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{    "email": "[email protected]"  }'
{
  "id": "cust_abc123",
  "email": "[email protected]",
  "firstName": "Jane",
  "lastName": "Doe",
  "phone": "+14155552671",
  "hasAccount": true,
  "emailVerified": true,
  "acceptsMarketing": true,
  "birthMonth": 4,
  "birthDay": 17,
  "tags": [
    "vip",
    "newsletter"
  ],
  "role": "wholesale",
  "totalOrders": 12,
  "totalSpent": "1284.50",
  "lastOrderAt": "2026-05-10T14:32:11.000Z",
  "rfm": {
    "r": 5,
    "f": 4,
    "m": 5,
    "score": "5-4-5",
    "segment": "champion"
  },
  "createdAt": "2025-08-01T09:00:00.000Z",
  "loyaltyPointsBalance": 240,
  "isLoyaltyMember": true,
  "acquisitionSalesChannel": {
    "id": "clsc_abc123",
    "name": "Main storefront",
    "connectionId": "vc_3n8Xk2p9QwErTyUiOpAsD"
  },
  "channelPublishes": [
    {
      "salesChannel": {
        "id": "clsc_abc123",
        "name": "Main storefront",
        "connectionId": "vc_3n8Xk2p9QwErTyUiOpAsD"
      },
      "firstSeenAt": "2026-03-04T08:12:00.000Z",
      "lastSeenAt": "2026-08-11T19:40:22.000Z"
    }
  ],
  "metadata": {
    "erpId": "CUST-00421"
  },
  "loyaltyMembershipId": "clmem_abc123",
  "platformConnections": [
    {
      "platformCode": "shopify",
      "externalId": "7654321098765"
    }
  ],
  "addresses": [
    {
      "id": "addr_abc123",
      "label": "Home",
      "firstName": "Jane",
      "lastName": "Doe",
      "company": "Acme Inc",
      "line1": "123 Main St",
      "line2": "Apt 4B",
      "city": "San Francisco",
      "region": "CA",
      "postalCode": "94103",
      "country": "US",
      "phone": "+14155552671",
      "isDefault": true,
      "createdAt": "2025-08-01T09:00:00.000Z",
      "updatedAt": "2026-05-12T11:24:08.000Z"
    }
  ],
  "updatedAt": "2026-05-12T11:24:08.000Z"
}

{
  "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/customers"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/customers"
}

{
  "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/customers"
}

Get a customer by ID

GET
/v1/customers/{id}
AuthorizationBearer <token>

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

idstring

Customer ID

curl -X GET "https://api.brainerce.com/api/v1/customers/string"
{
  "id": "cust_abc123",
  "email": "[email protected]",
  "firstName": "Jane",
  "lastName": "Doe",
  "phone": "+14155552671",
  "hasAccount": true,
  "emailVerified": true,
  "acceptsMarketing": true,
  "birthMonth": 4,
  "birthDay": 17,
  "tags": [
    "vip",
    "newsletter"
  ],
  "role": "wholesale",
  "totalOrders": 12,
  "totalSpent": "1284.50",
  "lastOrderAt": "2026-05-10T14:32:11.000Z",
  "rfm": {
    "r": 5,
    "f": 4,
    "m": 5,
    "score": "5-4-5",
    "segment": "champion"
  },
  "createdAt": "2025-08-01T09:00:00.000Z",
  "loyaltyPointsBalance": 240,
  "isLoyaltyMember": true,
  "acquisitionSalesChannel": {
    "id": "clsc_abc123",
    "name": "Main storefront",
    "connectionId": "vc_3n8Xk2p9QwErTyUiOpAsD"
  },
  "channelPublishes": [
    {
      "salesChannel": {
        "id": "clsc_abc123",
        "name": "Main storefront",
        "connectionId": "vc_3n8Xk2p9QwErTyUiOpAsD"
      },
      "firstSeenAt": "2026-03-04T08:12:00.000Z",
      "lastSeenAt": "2026-08-11T19:40:22.000Z"
    }
  ],
  "metadata": {
    "erpId": "CUST-00421"
  },
  "loyaltyMembershipId": "clmem_abc123",
  "platformConnections": [
    {
      "platformCode": "shopify",
      "externalId": "7654321098765"
    }
  ],
  "addresses": [
    {
      "id": "addr_abc123",
      "label": "Home",
      "firstName": "Jane",
      "lastName": "Doe",
      "company": "Acme Inc",
      "line1": "123 Main St",
      "line2": "Apt 4B",
      "city": "San Francisco",
      "region": "CA",
      "postalCode": "94103",
      "country": "US",
      "phone": "+14155552671",
      "isDefault": true,
      "createdAt": "2025-08-01T09:00:00.000Z",
      "updatedAt": "2026-05-12T11:24:08.000Z"
    }
  ],
  "updatedAt": "2026-05-12T11:24:08.000Z",
  "marketingStatus": "SUBSCRIBED"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/customers/{id}"
}

{
  "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/customers/{id}"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/customers/{id}"
}

Update a customer

Supports Idempotency-Key header for safe retries.

PATCH
/v1/customers/{id}
AuthorizationBearer <token>

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

idstring

Customer ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
email?string

Customer email. Changing email re-triggers verification (the new address must be verified before sensitive operations).

phone?string

Phone number.

firstName?string

First name.

lastName?string

Last name.

acceptsMarketing?boolean

Marketing consent flag.

birthMonth?number | null

Birthday month (1-12) for the loyalty birthday gift. Month and day only, no year. Send null for BOTH fields to remove a stored birthday; sending neither leaves it untouched, so an edit form that simply omits an emptied field can correct a birthday but never clear one.

birthDay?number | null

Birthday day of month (1-31). Send null for BOTH fields to remove a stored birthday; sending neither leaves it untouched, so an edit form that simply omits an emptied field can correct a birthday but never clear one.

tags?array<string>

Replace the customer's tag list. Pass [] to clear all tags.

role?string

Free-form customer segment/role set by the merchant (e.g. "wholesale", "vip", "ambassador"). Pass an empty string to clear it. Not settable by the customer themselves: admin dashboard/API only.

Lengthlength <= 50
acquisitionSalesChannelId?string

Override the FIRST-TOUCH sales channel, the channel this customer came in through. Normally stamped automatically at registration / OAuth / checkout and then never changed; this is the merchant correction for customers created from the dashboard or imported from a file, where no channel was ever known. Accepts the internal SalesChannel id or the public vc_* connectionId. Pass an empty string to clear it back to "unknown". Does NOT change which channels the customer is active in: that is the publish/unpublish endpoints.

metadata?object

Replace metadata JSON (full replacement, not merge).

Empty Object

curl -X PATCH "https://api.brainerce.com/api/v1/customers/string" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{}'
{
  "id": "cust_abc123",
  "email": "[email protected]",
  "firstName": "Jane",
  "lastName": "Doe",
  "phone": "+14155552671",
  "hasAccount": true,
  "emailVerified": true,
  "acceptsMarketing": true,
  "birthMonth": 4,
  "birthDay": 17,
  "tags": [
    "vip",
    "newsletter"
  ],
  "role": "wholesale",
  "totalOrders": 12,
  "totalSpent": "1284.50",
  "lastOrderAt": "2026-05-10T14:32:11.000Z",
  "rfm": {
    "r": 5,
    "f": 4,
    "m": 5,
    "score": "5-4-5",
    "segment": "champion"
  },
  "createdAt": "2025-08-01T09:00:00.000Z",
  "loyaltyPointsBalance": 240,
  "isLoyaltyMember": true,
  "acquisitionSalesChannel": {
    "id": "clsc_abc123",
    "name": "Main storefront",
    "connectionId": "vc_3n8Xk2p9QwErTyUiOpAsD"
  },
  "channelPublishes": [
    {
      "salesChannel": {
        "id": "clsc_abc123",
        "name": "Main storefront",
        "connectionId": "vc_3n8Xk2p9QwErTyUiOpAsD"
      },
      "firstSeenAt": "2026-03-04T08:12:00.000Z",
      "lastSeenAt": "2026-08-11T19:40:22.000Z"
    }
  ],
  "metadata": {
    "erpId": "CUST-00421"
  },
  "loyaltyMembershipId": "clmem_abc123",
  "platformConnections": [
    {
      "platformCode": "shopify",
      "externalId": "7654321098765"
    }
  ],
  "addresses": [
    {
      "id": "addr_abc123",
      "label": "Home",
      "firstName": "Jane",
      "lastName": "Doe",
      "company": "Acme Inc",
      "line1": "123 Main St",
      "line2": "Apt 4B",
      "city": "San Francisco",
      "region": "CA",
      "postalCode": "94103",
      "country": "US",
      "phone": "+14155552671",
      "isDefault": true,
      "createdAt": "2025-08-01T09:00:00.000Z",
      "updatedAt": "2026-05-12T11:24:08.000Z"
    }
  ],
  "updatedAt": "2026-05-12T11:24:08.000Z"
}

{
  "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/customers/{id}"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/customers/{id}"
}

{
  "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/customers/{id}"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/customers/{id}"
}

Attach a customer to a sales channel

Marks the customer as active in the given sales channel. Normally automatic, because the platform records a channel when the customer registers, signs in, or checks out there, so use this for migrations and corrections. A customer belongs to one store but can be active in any number of its channels. Accepts either the sales-channel record ID or its public vc_* connection ID. Idempotent, and supports the Idempotency-Key header for safe retries. Does NOT change the first-touch channel (acquisitionSalesChannelId on the update endpoint).

POST
/v1/customers/{id}/publish-sales-channel
AuthorizationBearer <token>

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

idstring

Customer ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
salesChannelIdstring

Sales channel ID (record ID or vc_* connection ID)

curl -X POST "https://api.brainerce.com/api/v1/customers/string/publish-sales-channel" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{    "salesChannelId": "string"  }'
{
  "success": true
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/customers/{id}/publish-sales-channel"
}

{
  "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/customers/{id}/publish-sales-channel"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/customers/{id}/publish-sales-channel"
}

Detach a customer from a sales channel

Removes the customer from the given sales channel. This is a correction, NOT a block: it does not prevent that person from buying on that storefront, and the channel is recorded again the next time they sign in or order there. Supports the Idempotency-Key header for safe retries.

POST
/v1/customers/{id}/unpublish-sales-channel
AuthorizationBearer <token>

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

idstring

Customer ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
salesChannelIdstring

Sales channel ID (record ID or vc_* connection ID)

curl -X POST "https://api.brainerce.com/api/v1/customers/string/unpublish-sales-channel" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{    "salesChannelId": "string"  }'
{
  "success": true
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/customers/{id}/unpublish-sales-channel"
}

{
  "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/customers/{id}/unpublish-sales-channel"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/customers/{id}/unpublish-sales-channel"
}

Get a customer by email

GET
/v1/customers/by-email
AuthorizationBearer <token>

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

Query Parameters

emailstring
curl -X GET "https://api.brainerce.com/api/v1/customers/by-email?email=string"
Empty

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/customers/by-email"
}

{
  "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/customers/by-email"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/customers/by-email"
}

Customer login

Supports Idempotency-Key header for safe retries.

POST
/v1/customers/login
AuthorizationBearer <token>

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

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
emailstring

Customer email address used at registration.

passwordstring

Customer password (plaintext over HTTPS, and never logged).

curl -X POST "https://api.brainerce.com/api/v1/customers/login" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{    "email": "[email protected]",    "password": "CorrectHorse-Battery-Staple9"  }'
{
  "customer": {
    "id": "string",
    "email": "[email protected]",
    "firstName": "string",
    "lastName": "string",
    "phone": "string",
    "emailVerified": true
  },
  "token": "string",
  "expiresAt": "2019-08-24T14:15:22Z",
  "requiresVerification": true
}

{
  "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/customers/login"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/customers/login"
}

{
  "statusCode": 429,
  "code": "RATE_LIMITED",
  "message": "Too many requests",
  "details": {
    "retryAfterSeconds": 12
  },
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/customers/login"
}

Customer registration

Supports Idempotency-Key header for safe retries.

POST
/v1/customers/register
AuthorizationBearer <token>

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

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
emailstring

Customer email, unique per store. Becomes the login identifier.

passwordstring

Password (≥ 8 chars, must match PASSWORD_REGEX: at least one uppercase, lowercase, number, and special char).

Length8 <= length
firstName?string

First name.

lastName?string

Last name.

phone?string

Phone number (E.164 format recommended).

acceptsMarketing?boolean

Marketing consent flag (gates promo emails / SMS).

birthMonth?number

Birthday month (1-12) for the loyalty birthday gift. Month and day only, with no year. Send it together with birthDay or not at all. Optional on most channels; a channel with requireBirthday turned on rejects a registration that omits it.

Range1 <= value <= 12
birthDay?number

Birthday day of month (1-31). Send it together with birthMonth or not at all. The day must exist in that month, so 30 February is rejected.

Range1 <= value <= 31
privacyPolicyAccepted?boolean

Privacy-policy acceptance flag, required by some stores before account creation. The frontend should require this checkbox when the store enables it.

referralCode?string

Loyalty referral share code (REF-XXXXXXXX) from a referrer's link. Validated asynchronously after registration, so an invalid code never fails the registration itself.

curl -X POST "https://api.brainerce.com/api/v1/customers/register" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{    "email": "[email protected]",    "password": "CorrectHorse-Battery-Staple9"  }'
{
  "customer": {
    "id": "string",
    "email": "[email protected]",
    "firstName": "string",
    "lastName": "string",
    "phone": "string",
    "emailVerified": true
  },
  "token": "string",
  "expiresAt": "2019-08-24T14:15:22Z",
  "requiresVerification": true
}

{
  "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/customers/register"
}

{
  "statusCode": 429,
  "code": "RATE_LIMITED",
  "message": "Too many requests",
  "details": {
    "retryAfterSeconds": 12
  },
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/customers/register"
}

Request password reset

Supports Idempotency-Key header for safe retries.

POST
/v1/customers/forgot-password
AuthorizationBearer <token>

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

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
emailstring

Email address to send the password-reset link to. Endpoint always responds 200 (no enumeration); the email is only sent if a matching customer exists.

curl -X POST "https://api.brainerce.com/api/v1/customers/forgot-password" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{    "email": "[email protected]"  }'
{
  "message": "If an account exists with that email, a reset link has been sent"
}

{
  "statusCode": 429,
  "code": "RATE_LIMITED",
  "message": "Too many requests",
  "details": {
    "retryAfterSeconds": 12
  },
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/customers/forgot-password"
}

Reset password with token

Supports Idempotency-Key header for safe retries.

POST
/v1/customers/reset-password
AuthorizationBearer <token>

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

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
tokenstring

Single-use reset token from the password-reset email. Tokens expire after 1 hour and are invalidated on use.

newPasswordstring

New password (≥ 8 chars, must match PASSWORD_REGEX: at least one uppercase, lowercase, number, and special char).

Length8 <= length
curl -X POST "https://api.brainerce.com/api/v1/customers/reset-password" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{    "token": "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6",    "newPassword": "CorrectHorse-Battery-Staple9"  }'
{
  "message": "If an account exists with that email, a reset link has been sent"
}

{
  "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/customers/reset-password"
}

{
  "statusCode": 429,
  "code": "RATE_LIMITED",
  "message": "Too many requests",
  "details": {
    "retryAfterSeconds": 12
  },
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/customers/reset-password"
}

Get customer addresses

GET
/v1/customers/{id}/addresses
AuthorizationBearer <token>

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

idstring

Customer ID

curl -X GET "https://api.brainerce.com/api/v1/customers/string/addresses"
[
  {
    "id": "addr_abc123",
    "label": "Home",
    "firstName": "Jane",
    "lastName": "Doe",
    "company": "Acme Inc",
    "line1": "123 Main St",
    "line2": "Apt 4B",
    "city": "San Francisco",
    "region": "CA",
    "postalCode": "94103",
    "country": "US",
    "phone": "+14155552671",
    "isDefault": true,
    "createdAt": "2025-08-01T09:00:00.000Z",
    "updatedAt": "2026-05-12T11:24:08.000Z"
  }
]

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/customers/{id}/addresses"
}

{
  "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/customers/{id}/addresses"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/customers/{id}/addresses"
}

Add a customer address

Supports Idempotency-Key header for safe retries.

POST
/v1/customers/{id}/addresses
AuthorizationBearer <token>

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

idstring

Customer ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
label?string

Friendly label shown in the address picker UI.

firstNamestring

First name on the address.

lastNamestring

Last name on the address.

company?string

Company name (B2B).

line1string

Street address line 1.

line2?string

Street address line 2 (apt/suite).

citystring

City.

region?string

State / Province.

postalCodestring

Postal / ZIP code.

countrystring

ISO 3166-1 alpha-2 country code.

phone?string

Phone (E.164 recommended).

isDefault?boolean

Mark this address as the customer's default. Promoting a new default automatically demotes the previous one.

curl -X POST "https://api.brainerce.com/api/v1/customers/string/addresses" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{    "firstName": "Jane",    "lastName": "Doe",    "line1": "123 Main St",    "city": "San Francisco",    "postalCode": "94103",    "country": "US"  }'
{
  "id": "addr_abc123",
  "label": "Home",
  "firstName": "Jane",
  "lastName": "Doe",
  "company": "Acme Inc",
  "line1": "123 Main St",
  "line2": "Apt 4B",
  "city": "San Francisco",
  "region": "CA",
  "postalCode": "94103",
  "country": "US",
  "phone": "+14155552671",
  "isDefault": true,
  "createdAt": "2025-08-01T09:00:00.000Z",
  "updatedAt": "2026-05-12T11:24:08.000Z"
}

{
  "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/customers/{id}/addresses"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/customers/{id}/addresses"
}

{
  "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/customers/{id}/addresses"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/customers/{id}/addresses"
}

Update a customer address

Supports Idempotency-Key header for safe retries.

PATCH
/v1/customers/{id}/addresses/{addressId}
AuthorizationBearer <token>

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

idstring

Customer ID

addressIdstring

Address ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
label?string

Friendly label.

firstName?string

First name.

lastName?string

Last name.

company?string

Company.

line1?string

Street line 1.

line2?string

Street line 2.

city?string

City.

region?string

State / Province.

postalCode?string

Postal / ZIP.

country?string

ISO country code.

phone?string

Phone.

isDefault?boolean

Promote this address to be the customer's default.

curl -X PATCH "https://api.brainerce.com/api/v1/customers/string/addresses/string" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{}'
{
  "id": "addr_abc123",
  "label": "Home",
  "firstName": "Jane",
  "lastName": "Doe",
  "company": "Acme Inc",
  "line1": "123 Main St",
  "line2": "Apt 4B",
  "city": "San Francisco",
  "region": "CA",
  "postalCode": "94103",
  "country": "US",
  "phone": "+14155552671",
  "isDefault": true,
  "createdAt": "2025-08-01T09:00:00.000Z",
  "updatedAt": "2026-05-12T11:24:08.000Z"
}

{
  "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/customers/{id}/addresses/{addressId}"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/customers/{id}/addresses/{addressId}"
}

{
  "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/customers/{id}/addresses/{addressId}"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/customers/{id}/addresses/{addressId}"
}

Delete a customer address

Supports Idempotency-Key header for safe retries.

DELETE
/v1/customers/{id}/addresses/{addressId}
AuthorizationBearer <token>

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

idstring

Customer ID

addressIdstring

Address ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
curl -X DELETE "https://api.brainerce.com/api/v1/customers/string/addresses/string" \  -H "Idempotency-Key: string"
Empty

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/customers/{id}/addresses/{addressId}"
}

{
  "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/customers/{id}/addresses/{addressId}"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/customers/{id}/addresses/{addressId}"
}

Get customer orders

GET
/v1/customers/{id}/orders
AuthorizationBearer <token>

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

idstring

Customer ID

Query Parameters

page?number
limit?number
curl -X GET "https://api.brainerce.com/api/v1/customers/string/orders?page=0&limit=0"
{
  "data": [
    {
      "id": "string",
      "externalId": "string",
      "orderNumber": "#1001",
      "status": "string",
      "totalAmount": "string",
      "currency": "string",
      "subtotal": "string",
      "discountAmount": "string",
      "couponCode": "string",
      "couponDiscount": "string",
      "ruleDiscountAmount": "string",
      "appliedDiscounts": [
        {}
      ],
      "shippingAmount": "string",
      "taxAmount": "string",
      "shippingAddress": {},
      "billingAddress": {},
      "items": [
        {
          "productId": "string",
          "variantId": "string",
          "sku": "string",
          "name": "string",
          "quantity": 0,
          "price": "string",
          "unitPrice": "string",
          "totalPrice": "string",
          "image": "string",
          "customizations": {},
          "modifiers": [
            {
              "modifierId": "string",
              "groupId": "string",
              "groupName": "string",
              "name": "string",
              "priceDelta": "string",
              "freeApplied": true,
              "effectivePrice": "string"
            }
          ]
        }
      ],
      "paymentMethod": "string",
      "financialStatus": "string",
      "fulfillmentStatus": "string",
      "trackingNumber": "string",
      "trackingUrl": "string",
      "carrier": "string",
      "shippedAt": "2019-08-24T14:15:22Z",
      "deliveredAt": "2019-08-24T14:15:22Z",
      "statusHistory": [
        {}
      ],
      "platform": "string",
      "createdAt": "2019-08-24T14:15:22Z",
      "notes": "string",
      "hasDownloads": true
    }
  ],
  "meta": {
    "page": 1,
    "limit": 20,
    "total": 137,
    "totalPages": 7
  }
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/customers/{id}/orders"
}

{
  "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/customers/{id}/orders"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/customers/{id}/orders"
}

Create a new cart

Supports Idempotency-Key header for safe retries.

POST
/v1/cart
AuthorizationBearer <token>

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

Query Parameters

customerId?string

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
sessionToken?string

Existing session token to attach this cart to. Omit on the first call, and the server will generate a fresh token and return it on the response. Only supply on retry / explicit re-attach flows.

customerId?string

Customer id to associate the cart with. Only honoured for admin / server-to-server callers. Sales-channel callers must instead send a Authorization: Bearer <customer-token> header.

analyticsClientId?string

GA4 gtag.js client_id (from gtag('get', measurementId, 'client_id', cb)), forwarded so a later server-side purchase event can stitch to this browser session. Omit if unavailable, and never send a synthesized id.

Lengthlength <= 100
analyticsSessionId?string

GA4 gtag.js session_id (from gtag('get', measurementId, 'session_id', cb)), forwarded alongside analyticsClientId for the same stitch.

Lengthlength <= 100
curl -X POST "https://api.brainerce.com/api/v1/cart?customerId=string" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{}'
{
  "id": "string",
  "sessionToken": "string",
  "customerId": "string",
  "status": "string",
  "currency": "string",
  "notes": "string",
  "analyticsClientId": "string",
  "analyticsSessionId": "string",
  "subtotal": "string",
  "discountAmount": "string",
  "ruleDiscountAmount": "string",
  "promoDiscountTotal": "string",
  "couponCode": "string",
  "appliedDiscounts": [
    {
      "ruleId": "string",
      "ruleName": "string",
      "type": "string",
      "discountAmount": "string",
      "description": "string"
    }
  ],
  "nudges": [
    {
      "ruleId": "string",
      "text": "string",
      "type": "AMOUNT_NEEDED",
      "amountNeeded": "string",
      "quantityNeeded": 0
    }
  ],
  "items": [
    {
      "id": "string",
      "productId": "string",
      "variantId": "string",
      "quantity": 0,
      "unitPrice": "string",
      "currentUnitPrice": "string",
      "priceChanged": true,
      "priceDelta": "string",
      "priceDirection": "increased",
      "isAvailable": true,
      "unavailableReason": "PRODUCT_DRAFT",
      "discountAmount": "string",
      "promoDiscountAmount": "string",
      "promoSource": "string",
      "promoSourceId": "string",
      "notes": "string",
      "metadata": {},
      "customizations": {},
      "product": {
        "id": "string",
        "name": "string",
        "sku": "string",
        "images": [
          {}
        ]
      },
      "variant": {
        "id": "string",
        "name": "string",
        "sku": "string",
        "image": {}
      },
      "modifiers": [
        {
          "modifierId": "string",
          "modifierGroupId": "string",
          "name": "string",
          "priceDelta": "string",
          "freeApplied": true,
          "effectivePrice": "string",
          "positionInGroup": 0
        }
      ],
      "modifiersTotal": "string",
      "parentCartItemId": "string",
      "createdAt": "2019-08-24T14:15:22Z",
      "updatedAt": "2019-08-24T14:15:22Z"
    }
  ],
  "itemCount": 0,
  "hasPriceChanges": true,
  "hasUnavailableItems": true,
  "unavailableItemIds": [
    "string"
  ],
  "expiresAt": "2019-08-24T14:15:22Z",
  "createdAt": "2019-08-24T14:15:22Z",
  "updatedAt": "2019-08-24T14:15:22Z",
  "reservation": {
    "hasReservation": true,
    "expiresAt": "2019-08-24T14:15:22Z",
    "remainingSeconds": 0,
    "strategy": "ON_PAYMENT",
    "countdownMessage": "string"
  },
  "recommendations": {},
  "upgrades": {},
  "bundles": {}
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/cart"
}

{
  "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/cart"
}

Get cart by session token

GET
/v1/cart/session/{sessionToken}
AuthorizationBearer <token>

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

sessionTokenstring

Cart session token

curl -X GET "https://api.brainerce.com/api/v1/cart/session/string"
{
  "id": "string",
  "sessionToken": "string",
  "customerId": "string",
  "status": "string",
  "currency": "string",
  "notes": "string",
  "analyticsClientId": "string",
  "analyticsSessionId": "string",
  "subtotal": "string",
  "discountAmount": "string",
  "ruleDiscountAmount": "string",
  "promoDiscountTotal": "string",
  "couponCode": "string",
  "appliedDiscounts": [
    {
      "ruleId": "string",
      "ruleName": "string",
      "type": "string",
      "discountAmount": "string",
      "description": "string"
    }
  ],
  "nudges": [
    {
      "ruleId": "string",
      "text": "string",
      "type": "AMOUNT_NEEDED",
      "amountNeeded": "string",
      "quantityNeeded": 0
    }
  ],
  "items": [
    {
      "id": "string",
      "productId": "string",
      "variantId": "string",
      "quantity": 0,
      "unitPrice": "string",
      "currentUnitPrice": "string",
      "priceChanged": true,
      "priceDelta": "string",
      "priceDirection": "increased",
      "isAvailable": true,
      "unavailableReason": "PRODUCT_DRAFT",
      "discountAmount": "string",
      "promoDiscountAmount": "string",
      "promoSource": "string",
      "promoSourceId": "string",
      "notes": "string",
      "metadata": {},
      "customizations": {},
      "product": {
        "id": "string",
        "name": "string",
        "sku": "string",
        "images": [
          {}
        ]
      },
      "variant": {
        "id": "string",
        "name": "string",
        "sku": "string",
        "image": {}
      },
      "modifiers": [
        {
          "modifierId": "string",
          "modifierGroupId": "string",
          "name": "string",
          "priceDelta": "string",
          "freeApplied": true,
          "effectivePrice": "string",
          "positionInGroup": 0
        }
      ],
      "modifiersTotal": "string",
      "parentCartItemId": "string",
      "createdAt": "2019-08-24T14:15:22Z",
      "updatedAt": "2019-08-24T14:15:22Z"
    }
  ],
  "itemCount": 0,
  "hasPriceChanges": true,
  "hasUnavailableItems": true,
  "unavailableItemIds": [
    "string"
  ],
  "expiresAt": "2019-08-24T14:15:22Z",
  "createdAt": "2019-08-24T14:15:22Z",
  "updatedAt": "2019-08-24T14:15:22Z",
  "reservation": {
    "hasReservation": true,
    "expiresAt": "2019-08-24T14:15:22Z",
    "remainingSeconds": 0,
    "strategy": "ON_PAYMENT",
    "countdownMessage": "string"
  },
  "recommendations": {},
  "upgrades": {},
  "bundles": {}
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/cart/session/{sessionToken}"
}

{
  "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/cart/session/{sessionToken}"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/cart/session/{sessionToken}"
}

Get cart by customer ID

GET
/v1/cart/customer/{customerId}
AuthorizationBearer <token>

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

customerIdstring

Customer ID

curl -X GET "https://api.brainerce.com/api/v1/cart/customer/string"
{
  "id": "string",
  "sessionToken": "string",
  "customerId": "string",
  "status": "string",
  "currency": "string",
  "notes": "string",
  "analyticsClientId": "string",
  "analyticsSessionId": "string",
  "subtotal": "string",
  "discountAmount": "string",
  "ruleDiscountAmount": "string",
  "promoDiscountTotal": "string",
  "couponCode": "string",
  "appliedDiscounts": [
    {
      "ruleId": "string",
      "ruleName": "string",
      "type": "string",
      "discountAmount": "string",
      "description": "string"
    }
  ],
  "nudges": [
    {
      "ruleId": "string",
      "text": "string",
      "type": "AMOUNT_NEEDED",
      "amountNeeded": "string",
      "quantityNeeded": 0
    }
  ],
  "items": [
    {
      "id": "string",
      "productId": "string",
      "variantId": "string",
      "quantity": 0,
      "unitPrice": "string",
      "currentUnitPrice": "string",
      "priceChanged": true,
      "priceDelta": "string",
      "priceDirection": "increased",
      "isAvailable": true,
      "unavailableReason": "PRODUCT_DRAFT",
      "discountAmount": "string",
      "promoDiscountAmount": "string",
      "promoSource": "string",
      "promoSourceId": "string",
      "notes": "string",
      "metadata": {},
      "customizations": {},
      "product": {
        "id": "string",
        "name": "string",
        "sku": "string",
        "images": [
          {}
        ]
      },
      "variant": {
        "id": "string",
        "name": "string",
        "sku": "string",
        "image": {}
      },
      "modifiers": [
        {
          "modifierId": "string",
          "modifierGroupId": "string",
          "name": "string",
          "priceDelta": "string",
          "freeApplied": true,
          "effectivePrice": "string",
          "positionInGroup": 0
        }
      ],
      "modifiersTotal": "string",
      "parentCartItemId": "string",
      "createdAt": "2019-08-24T14:15:22Z",
      "updatedAt": "2019-08-24T14:15:22Z"
    }
  ],
  "itemCount": 0,
  "hasPriceChanges": true,
  "hasUnavailableItems": true,
  "unavailableItemIds": [
    "string"
  ],
  "expiresAt": "2019-08-24T14:15:22Z",
  "createdAt": "2019-08-24T14:15:22Z",
  "updatedAt": "2019-08-24T14:15:22Z",
  "reservation": {
    "hasReservation": true,
    "expiresAt": "2019-08-24T14:15:22Z",
    "remainingSeconds": 0,
    "strategy": "ON_PAYMENT",
    "countdownMessage": "string"
  },
  "recommendations": {},
  "upgrades": {},
  "bundles": {}
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/cart/customer/{customerId}"
}

{
  "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/cart/customer/{customerId}"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/cart/customer/{customerId}"
}

Get cart by ID

GET
/v1/cart/{cartId}
AuthorizationBearer <token>

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

cartIdstring

Cart ID

curl -X GET "https://api.brainerce.com/api/v1/cart/string"
{
  "id": "string",
  "sessionToken": "string",
  "customerId": "string",
  "status": "string",
  "currency": "string",
  "notes": "string",
  "analyticsClientId": "string",
  "analyticsSessionId": "string",
  "subtotal": "string",
  "discountAmount": "string",
  "ruleDiscountAmount": "string",
  "promoDiscountTotal": "string",
  "couponCode": "string",
  "appliedDiscounts": [
    {
      "ruleId": "string",
      "ruleName": "string",
      "type": "string",
      "discountAmount": "string",
      "description": "string"
    }
  ],
  "nudges": [
    {
      "ruleId": "string",
      "text": "string",
      "type": "AMOUNT_NEEDED",
      "amountNeeded": "string",
      "quantityNeeded": 0
    }
  ],
  "items": [
    {
      "id": "string",
      "productId": "string",
      "variantId": "string",
      "quantity": 0,
      "unitPrice": "string",
      "currentUnitPrice": "string",
      "priceChanged": true,
      "priceDelta": "string",
      "priceDirection": "increased",
      "isAvailable": true,
      "unavailableReason": "PRODUCT_DRAFT",
      "discountAmount": "string",
      "promoDiscountAmount": "string",
      "promoSource": "string",
      "promoSourceId": "string",
      "notes": "string",
      "metadata": {},
      "customizations": {},
      "product": {
        "id": "string",
        "name": "string",
        "sku": "string",
        "images": [
          {}
        ]
      },
      "variant": {
        "id": "string",
        "name": "string",
        "sku": "string",
        "image": {}
      },
      "modifiers": [
        {
          "modifierId": "string",
          "modifierGroupId": "string",
          "name": "string",
          "priceDelta": "string",
          "freeApplied": true,
          "effectivePrice": "string",
          "positionInGroup": 0
        }
      ],
      "modifiersTotal": "string",
      "parentCartItemId": "string",
      "createdAt": "2019-08-24T14:15:22Z",
      "updatedAt": "2019-08-24T14:15:22Z"
    }
  ],
  "itemCount": 0,
  "hasPriceChanges": true,
  "hasUnavailableItems": true,
  "unavailableItemIds": [
    "string"
  ],
  "expiresAt": "2019-08-24T14:15:22Z",
  "createdAt": "2019-08-24T14:15:22Z",
  "updatedAt": "2019-08-24T14:15:22Z",
  "reservation": {
    "hasReservation": true,
    "expiresAt": "2019-08-24T14:15:22Z",
    "remainingSeconds": 0,
    "strategy": "ON_PAYMENT",
    "countdownMessage": "string"
  },
  "recommendations": {},
  "upgrades": {},
  "bundles": {}
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/cart/{cartId}"
}

{
  "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/cart/{cartId}"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/cart/{cartId}"
}

Add item to cart

Supports Idempotency-Key header for safe retries.

POST
/v1/cart/{cartId}/items
AuthorizationBearer <token>

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

cartIdstring

Cart ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
productIdstring

Product to add.

variantId?string

Variant id for the product, if it has variants.

quantitynumber

Number of units to add (1..1000).

Range1 <= value <= 1000
notes?string

Shopper-facing note attached to this line item ("Please gift-wrap"). Max 2000 chars.

Lengthlength <= 2000
metadata?object

Arbitrary JSON metadata you want to carry through to the order line. Not displayed in the storefront UI. Max 4 KB, depth ≤ 3, no prototype keys.

Empty Object

analyticsClientId?string

GA4 gtag.js client_id (from gtag('get', measurementId, 'client_id', cb)), forwarded so a later server-side purchase event can stitch to this browser session. Written onto the cart if not already set. Omit if unavailable, and never send a synthesized id.

Lengthlength <= 100
analyticsSessionId?string

GA4 gtag.js session_id (from gtag('get', measurementId, 'session_id', cb)), forwarded alongside analyticsClientId for the same stitch.

Lengthlength <= 100
selections?array<ModifierSelectionDto>

Modifier selections grouped by modifierGroupId, in click-order. Omit for products without modifier groups.

nestedByModifierId?object

Nested combo specs (for modifiers with referencedProductId). Keyed by parent modifier id; validator rejects depth > 3.

Empty Object

curl -X POST "https://api.brainerce.com/api/v1/cart/string/items" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{    "productId": "prod_abc123",    "quantity": 1  }'
{
  "id": "string",
  "sessionToken": "string",
  "customerId": "string",
  "status": "string",
  "currency": "string",
  "notes": "string",
  "analyticsClientId": "string",
  "analyticsSessionId": "string",
  "subtotal": "string",
  "discountAmount": "string",
  "ruleDiscountAmount": "string",
  "promoDiscountTotal": "string",
  "couponCode": "string",
  "appliedDiscounts": [
    {
      "ruleId": "string",
      "ruleName": "string",
      "type": "string",
      "discountAmount": "string",
      "description": "string"
    }
  ],
  "nudges": [
    {
      "ruleId": "string",
      "text": "string",
      "type": "AMOUNT_NEEDED",
      "amountNeeded": "string",
      "quantityNeeded": 0
    }
  ],
  "items": [
    {
      "id": "string",
      "productId": "string",
      "variantId": "string",
      "quantity": 0,
      "unitPrice": "string",
      "currentUnitPrice": "string",
      "priceChanged": true,
      "priceDelta": "string",
      "priceDirection": "increased",
      "isAvailable": true,
      "unavailableReason": "PRODUCT_DRAFT",
      "discountAmount": "string",
      "promoDiscountAmount": "string",
      "promoSource": "string",
      "promoSourceId": "string",
      "notes": "string",
      "metadata": {},
      "customizations": {},
      "product": {
        "id": "string",
        "name": "string",
        "sku": "string",
        "images": [
          {}
        ]
      },
      "variant": {
        "id": "string",
        "name": "string",
        "sku": "string",
        "image": {}
      },
      "modifiers": [
        {
          "modifierId": "string",
          "modifierGroupId": "string",
          "name": "string",
          "priceDelta": "string",
          "freeApplied": true,
          "effectivePrice": "string",
          "positionInGroup": 0
        }
      ],
      "modifiersTotal": "string",
      "parentCartItemId": "string",
      "createdAt": "2019-08-24T14:15:22Z",
      "updatedAt": "2019-08-24T14:15:22Z"
    }
  ],
  "itemCount": 0,
  "hasPriceChanges": true,
  "hasUnavailableItems": true,
  "unavailableItemIds": [
    "string"
  ],
  "expiresAt": "2019-08-24T14:15:22Z",
  "createdAt": "2019-08-24T14:15:22Z",
  "updatedAt": "2019-08-24T14:15:22Z",
  "reservation": {
    "hasReservation": true,
    "expiresAt": "2019-08-24T14:15:22Z",
    "remainingSeconds": 0,
    "strategy": "ON_PAYMENT",
    "countdownMessage": "string"
  },
  "recommendations": {},
  "upgrades": {},
  "bundles": {}
}

{
  "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/cart/{cartId}/items"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/cart/{cartId}/items"
}

{
  "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/cart/{cartId}/items"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/cart/{cartId}/items"
}

Clear all items from cart

Supports Idempotency-Key header for safe retries.

DELETE
/v1/cart/{cartId}/items
AuthorizationBearer <token>

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

cartIdstring

Cart ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
curl -X DELETE "https://api.brainerce.com/api/v1/cart/string/items" \  -H "Idempotency-Key: string"
{
  "id": "string",
  "sessionToken": "string",
  "customerId": "string",
  "status": "string",
  "currency": "string",
  "notes": "string",
  "analyticsClientId": "string",
  "analyticsSessionId": "string",
  "subtotal": "string",
  "discountAmount": "string",
  "ruleDiscountAmount": "string",
  "promoDiscountTotal": "string",
  "couponCode": "string",
  "appliedDiscounts": [
    {
      "ruleId": "string",
      "ruleName": "string",
      "type": "string",
      "discountAmount": "string",
      "description": "string"
    }
  ],
  "nudges": [
    {
      "ruleId": "string",
      "text": "string",
      "type": "AMOUNT_NEEDED",
      "amountNeeded": "string",
      "quantityNeeded": 0
    }
  ],
  "items": [
    {
      "id": "string",
      "productId": "string",
      "variantId": "string",
      "quantity": 0,
      "unitPrice": "string",
      "currentUnitPrice": "string",
      "priceChanged": true,
      "priceDelta": "string",
      "priceDirection": "increased",
      "isAvailable": true,
      "unavailableReason": "PRODUCT_DRAFT",
      "discountAmount": "string",
      "promoDiscountAmount": "string",
      "promoSource": "string",
      "promoSourceId": "string",
      "notes": "string",
      "metadata": {},
      "customizations": {},
      "product": {
        "id": "string",
        "name": "string",
        "sku": "string",
        "images": [
          {}
        ]
      },
      "variant": {
        "id": "string",
        "name": "string",
        "sku": "string",
        "image": {}
      },
      "modifiers": [
        {
          "modifierId": "string",
          "modifierGroupId": "string",
          "name": "string",
          "priceDelta": "string",
          "freeApplied": true,
          "effectivePrice": "string",
          "positionInGroup": 0
        }
      ],
      "modifiersTotal": "string",
      "parentCartItemId": "string",
      "createdAt": "2019-08-24T14:15:22Z",
      "updatedAt": "2019-08-24T14:15:22Z"
    }
  ],
  "itemCount": 0,
  "hasPriceChanges": true,
  "hasUnavailableItems": true,
  "unavailableItemIds": [
    "string"
  ],
  "expiresAt": "2019-08-24T14:15:22Z",
  "createdAt": "2019-08-24T14:15:22Z",
  "updatedAt": "2019-08-24T14:15:22Z",
  "reservation": {
    "hasReservation": true,
    "expiresAt": "2019-08-24T14:15:22Z",
    "remainingSeconds": 0,
    "strategy": "ON_PAYMENT",
    "countdownMessage": "string"
  },
  "recommendations": {},
  "upgrades": {},
  "bundles": {}
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/cart/{cartId}/items"
}

{
  "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/cart/{cartId}/items"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/cart/{cartId}/items"
}

Update cart item

Supports Idempotency-Key header for safe retries.

PATCH
/v1/cart/{cartId}/items/{itemId}
AuthorizationBearer <token>

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

cartIdstring

Cart ID

itemIdstring

Cart Item ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
quantity?number

New quantity (0 to 1000). Setting quantity: 0 removes the line, same as DELETE.

Range0 <= value <= 1000
notes?string

New shopper-facing note for this line.

selections?array<ModifierSelectionDto>

New modifier selections. PRD §7.2.3: present means REPLACE all existing selections on this line. Omit to keep the existing selections unchanged.

nestedByModifierId?object

New nested combo selections. Same REPLACE semantics as selections.

Empty Object

curl -X PATCH "https://api.brainerce.com/api/v1/cart/string/items/string" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{}'
{
  "id": "string",
  "sessionToken": "string",
  "customerId": "string",
  "status": "string",
  "currency": "string",
  "notes": "string",
  "analyticsClientId": "string",
  "analyticsSessionId": "string",
  "subtotal": "string",
  "discountAmount": "string",
  "ruleDiscountAmount": "string",
  "promoDiscountTotal": "string",
  "couponCode": "string",
  "appliedDiscounts": [
    {
      "ruleId": "string",
      "ruleName": "string",
      "type": "string",
      "discountAmount": "string",
      "description": "string"
    }
  ],
  "nudges": [
    {
      "ruleId": "string",
      "text": "string",
      "type": "AMOUNT_NEEDED",
      "amountNeeded": "string",
      "quantityNeeded": 0
    }
  ],
  "items": [
    {
      "id": "string",
      "productId": "string",
      "variantId": "string",
      "quantity": 0,
      "unitPrice": "string",
      "currentUnitPrice": "string",
      "priceChanged": true,
      "priceDelta": "string",
      "priceDirection": "increased",
      "isAvailable": true,
      "unavailableReason": "PRODUCT_DRAFT",
      "discountAmount": "string",
      "promoDiscountAmount": "string",
      "promoSource": "string",
      "promoSourceId": "string",
      "notes": "string",
      "metadata": {},
      "customizations": {},
      "product": {
        "id": "string",
        "name": "string",
        "sku": "string",
        "images": [
          {}
        ]
      },
      "variant": {
        "id": "string",
        "name": "string",
        "sku": "string",
        "image": {}
      },
      "modifiers": [
        {
          "modifierId": "string",
          "modifierGroupId": "string",
          "name": "string",
          "priceDelta": "string",
          "freeApplied": true,
          "effectivePrice": "string",
          "positionInGroup": 0
        }
      ],
      "modifiersTotal": "string",
      "parentCartItemId": "string",
      "createdAt": "2019-08-24T14:15:22Z",
      "updatedAt": "2019-08-24T14:15:22Z"
    }
  ],
  "itemCount": 0,
  "hasPriceChanges": true,
  "hasUnavailableItems": true,
  "unavailableItemIds": [
    "string"
  ],
  "expiresAt": "2019-08-24T14:15:22Z",
  "createdAt": "2019-08-24T14:15:22Z",
  "updatedAt": "2019-08-24T14:15:22Z",
  "reservation": {
    "hasReservation": true,
    "expiresAt": "2019-08-24T14:15:22Z",
    "remainingSeconds": 0,
    "strategy": "ON_PAYMENT",
    "countdownMessage": "string"
  },
  "recommendations": {},
  "upgrades": {},
  "bundles": {}
}

{
  "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/cart/{cartId}/items/{itemId}"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/cart/{cartId}/items/{itemId}"
}

{
  "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/cart/{cartId}/items/{itemId}"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/cart/{cartId}/items/{itemId}"
}

Remove item from cart

Supports Idempotency-Key header for safe retries.

DELETE
/v1/cart/{cartId}/items/{itemId}
AuthorizationBearer <token>

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

cartIdstring

Cart ID

itemIdstring

Cart Item ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
curl -X DELETE "https://api.brainerce.com/api/v1/cart/string/items/string" \  -H "Idempotency-Key: string"
{
  "id": "string",
  "sessionToken": "string",
  "customerId": "string",
  "status": "string",
  "currency": "string",
  "notes": "string",
  "analyticsClientId": "string",
  "analyticsSessionId": "string",
  "subtotal": "string",
  "discountAmount": "string",
  "ruleDiscountAmount": "string",
  "promoDiscountTotal": "string",
  "couponCode": "string",
  "appliedDiscounts": [
    {
      "ruleId": "string",
      "ruleName": "string",
      "type": "string",
      "discountAmount": "string",
      "description": "string"
    }
  ],
  "nudges": [
    {
      "ruleId": "string",
      "text": "string",
      "type": "AMOUNT_NEEDED",
      "amountNeeded": "string",
      "quantityNeeded": 0
    }
  ],
  "items": [
    {
      "id": "string",
      "productId": "string",
      "variantId": "string",
      "quantity": 0,
      "unitPrice": "string",
      "currentUnitPrice": "string",
      "priceChanged": true,
      "priceDelta": "string",
      "priceDirection": "increased",
      "isAvailable": true,
      "unavailableReason": "PRODUCT_DRAFT",
      "discountAmount": "string",
      "promoDiscountAmount": "string",
      "promoSource": "string",
      "promoSourceId": "string",
      "notes": "string",
      "metadata": {},
      "customizations": {},
      "product": {
        "id": "string",
        "name": "string",
        "sku": "string",
        "images": [
          {}
        ]
      },
      "variant": {
        "id": "string",
        "name": "string",
        "sku": "string",
        "image": {}
      },
      "modifiers": [
        {
          "modifierId": "string",
          "modifierGroupId": "string",
          "name": "string",
          "priceDelta": "string",
          "freeApplied": true,
          "effectivePrice": "string",
          "positionInGroup": 0
        }
      ],
      "modifiersTotal": "string",
      "parentCartItemId": "string",
      "createdAt": "2019-08-24T14:15:22Z",
      "updatedAt": "2019-08-24T14:15:22Z"
    }
  ],
  "itemCount": 0,
  "hasPriceChanges": true,
  "hasUnavailableItems": true,
  "unavailableItemIds": [
    "string"
  ],
  "expiresAt": "2019-08-24T14:15:22Z",
  "createdAt": "2019-08-24T14:15:22Z",
  "updatedAt": "2019-08-24T14:15:22Z",
  "reservation": {
    "hasReservation": true,
    "expiresAt": "2019-08-24T14:15:22Z",
    "remainingSeconds": 0,
    "strategy": "ON_PAYMENT",
    "countdownMessage": "string"
  },
  "recommendations": {},
  "upgrades": {},
  "bundles": {}
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/cart/{cartId}/items/{itemId}"
}

{
  "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/cart/{cartId}/items/{itemId}"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/cart/{cartId}/items/{itemId}"
}

Apply coupon to cart

Supports Idempotency-Key header for safe retries.

POST
/v1/cart/{cartId}/coupon
AuthorizationBearer <token>

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

cartIdstring

Cart ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
codestring

Coupon code (case-insensitive). Validation runs server-side, and invalid codes return 400 with code: COUPON_INVALID or COUPON_USAGE_LIMIT_REACHED.

curl -X POST "https://api.brainerce.com/api/v1/cart/string/coupon" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{    "code": "SUMMER25"  }'
{
  "id": "string",
  "sessionToken": "string",
  "customerId": "string",
  "status": "string",
  "currency": "string",
  "notes": "string",
  "analyticsClientId": "string",
  "analyticsSessionId": "string",
  "subtotal": "string",
  "discountAmount": "string",
  "ruleDiscountAmount": "string",
  "promoDiscountTotal": "string",
  "couponCode": "string",
  "appliedDiscounts": [
    {
      "ruleId": "string",
      "ruleName": "string",
      "type": "string",
      "discountAmount": "string",
      "description": "string"
    }
  ],
  "nudges": [
    {
      "ruleId": "string",
      "text": "string",
      "type": "AMOUNT_NEEDED",
      "amountNeeded": "string",
      "quantityNeeded": 0
    }
  ],
  "items": [
    {
      "id": "string",
      "productId": "string",
      "variantId": "string",
      "quantity": 0,
      "unitPrice": "string",
      "currentUnitPrice": "string",
      "priceChanged": true,
      "priceDelta": "string",
      "priceDirection": "increased",
      "isAvailable": true,
      "unavailableReason": "PRODUCT_DRAFT",
      "discountAmount": "string",
      "promoDiscountAmount": "string",
      "promoSource": "string",
      "promoSourceId": "string",
      "notes": "string",
      "metadata": {},
      "customizations": {},
      "product": {
        "id": "string",
        "name": "string",
        "sku": "string",
        "images": [
          {}
        ]
      },
      "variant": {
        "id": "string",
        "name": "string",
        "sku": "string",
        "image": {}
      },
      "modifiers": [
        {
          "modifierId": "string",
          "modifierGroupId": "string",
          "name": "string",
          "priceDelta": "string",
          "freeApplied": true,
          "effectivePrice": "string",
          "positionInGroup": 0
        }
      ],
      "modifiersTotal": "string",
      "parentCartItemId": "string",
      "createdAt": "2019-08-24T14:15:22Z",
      "updatedAt": "2019-08-24T14:15:22Z"
    }
  ],
  "itemCount": 0,
  "hasPriceChanges": true,
  "hasUnavailableItems": true,
  "unavailableItemIds": [
    "string"
  ],
  "expiresAt": "2019-08-24T14:15:22Z",
  "createdAt": "2019-08-24T14:15:22Z",
  "updatedAt": "2019-08-24T14:15:22Z",
  "reservation": {
    "hasReservation": true,
    "expiresAt": "2019-08-24T14:15:22Z",
    "remainingSeconds": 0,
    "strategy": "ON_PAYMENT",
    "countdownMessage": "string"
  },
  "recommendations": {},
  "upgrades": {},
  "bundles": {}
}

{
  "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/cart/{cartId}/coupon"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/cart/{cartId}/coupon"
}

{
  "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/cart/{cartId}/coupon"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/cart/{cartId}/coupon"
}

{
  "statusCode": 429,
  "code": "RATE_LIMITED",
  "message": "Too many requests",
  "details": {
    "retryAfterSeconds": 12
  },
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/cart/{cartId}/coupon"
}

Remove coupon from cart

Supports Idempotency-Key header for safe retries.

DELETE
/v1/cart/{cartId}/coupon
AuthorizationBearer <token>

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

cartIdstring

Cart ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
curl -X DELETE "https://api.brainerce.com/api/v1/cart/string/coupon" \  -H "Idempotency-Key: string"
{
  "id": "string",
  "sessionToken": "string",
  "customerId": "string",
  "status": "string",
  "currency": "string",
  "notes": "string",
  "analyticsClientId": "string",
  "analyticsSessionId": "string",
  "subtotal": "string",
  "discountAmount": "string",
  "ruleDiscountAmount": "string",
  "promoDiscountTotal": "string",
  "couponCode": "string",
  "appliedDiscounts": [
    {
      "ruleId": "string",
      "ruleName": "string",
      "type": "string",
      "discountAmount": "string",
      "description": "string"
    }
  ],
  "nudges": [
    {
      "ruleId": "string",
      "text": "string",
      "type": "AMOUNT_NEEDED",
      "amountNeeded": "string",
      "quantityNeeded": 0
    }
  ],
  "items": [
    {
      "id": "string",
      "productId": "string",
      "variantId": "string",
      "quantity": 0,
      "unitPrice": "string",
      "currentUnitPrice": "string",
      "priceChanged": true,
      "priceDelta": "string",
      "priceDirection": "increased",
      "isAvailable": true,
      "unavailableReason": "PRODUCT_DRAFT",
      "discountAmount": "string",
      "promoDiscountAmount": "string",
      "promoSource": "string",
      "promoSourceId": "string",
      "notes": "string",
      "metadata": {},
      "customizations": {},
      "product": {
        "id": "string",
        "name": "string",
        "sku": "string",
        "images": [
          {}
        ]
      },
      "variant": {
        "id": "string",
        "name": "string",
        "sku": "string",
        "image": {}
      },
      "modifiers": [
        {
          "modifierId": "string",
          "modifierGroupId": "string",
          "name": "string",
          "priceDelta": "string",
          "freeApplied": true,
          "effectivePrice": "string",
          "positionInGroup": 0
        }
      ],
      "modifiersTotal": "string",
      "parentCartItemId": "string",
      "createdAt": "2019-08-24T14:15:22Z",
      "updatedAt": "2019-08-24T14:15:22Z"
    }
  ],
  "itemCount": 0,
  "hasPriceChanges": true,
  "hasUnavailableItems": true,
  "unavailableItemIds": [
    "string"
  ],
  "expiresAt": "2019-08-24T14:15:22Z",
  "createdAt": "2019-08-24T14:15:22Z",
  "updatedAt": "2019-08-24T14:15:22Z",
  "reservation": {
    "hasReservation": true,
    "expiresAt": "2019-08-24T14:15:22Z",
    "remainingSeconds": 0,
    "strategy": "ON_PAYMENT",
    "countdownMessage": "string"
  },
  "recommendations": {},
  "upgrades": {},
  "bundles": {}
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/cart/{cartId}/coupon"
}

{
  "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/cart/{cartId}/coupon"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/cart/{cartId}/coupon"
}

Merge guest cart into customer cart

Supports Idempotency-Key header for safe retries.

POST
/v1/cart/merge
AuthorizationBearer <token>

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

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
sessionTokenstring

Guest cart session token

customerIdstring

Customer ID to merge into

curl -X POST "https://api.brainerce.com/api/v1/cart/merge" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{    "sessionToken": "string",    "customerId": "string"  }'
{
  "id": "string",
  "sessionToken": "string",
  "customerId": "string",
  "status": "string",
  "currency": "string",
  "notes": "string",
  "analyticsClientId": "string",
  "analyticsSessionId": "string",
  "subtotal": "string",
  "discountAmount": "string",
  "ruleDiscountAmount": "string",
  "promoDiscountTotal": "string",
  "couponCode": "string",
  "appliedDiscounts": [
    {
      "ruleId": "string",
      "ruleName": "string",
      "type": "string",
      "discountAmount": "string",
      "description": "string"
    }
  ],
  "nudges": [
    {
      "ruleId": "string",
      "text": "string",
      "type": "AMOUNT_NEEDED",
      "amountNeeded": "string",
      "quantityNeeded": 0
    }
  ],
  "items": [
    {
      "id": "string",
      "productId": "string",
      "variantId": "string",
      "quantity": 0,
      "unitPrice": "string",
      "currentUnitPrice": "string",
      "priceChanged": true,
      "priceDelta": "string",
      "priceDirection": "increased",
      "isAvailable": true,
      "unavailableReason": "PRODUCT_DRAFT",
      "discountAmount": "string",
      "promoDiscountAmount": "string",
      "promoSource": "string",
      "promoSourceId": "string",
      "notes": "string",
      "metadata": {},
      "customizations": {},
      "product": {
        "id": "string",
        "name": "string",
        "sku": "string",
        "images": [
          {}
        ]
      },
      "variant": {
        "id": "string",
        "name": "string",
        "sku": "string",
        "image": {}
      },
      "modifiers": [
        {
          "modifierId": "string",
          "modifierGroupId": "string",
          "name": "string",
          "priceDelta": "string",
          "freeApplied": true,
          "effectivePrice": "string",
          "positionInGroup": 0
        }
      ],
      "modifiersTotal": "string",
      "parentCartItemId": "string",
      "createdAt": "2019-08-24T14:15:22Z",
      "updatedAt": "2019-08-24T14:15:22Z"
    }
  ],
  "itemCount": 0,
  "hasPriceChanges": true,
  "hasUnavailableItems": true,
  "unavailableItemIds": [
    "string"
  ],
  "expiresAt": "2019-08-24T14:15:22Z",
  "createdAt": "2019-08-24T14:15:22Z",
  "updatedAt": "2019-08-24T14:15:22Z",
  "reservation": {
    "hasReservation": true,
    "expiresAt": "2019-08-24T14:15:22Z",
    "remainingSeconds": 0,
    "strategy": "ON_PAYMENT",
    "countdownMessage": "string"
  },
  "recommendations": {},
  "upgrades": {},
  "bundles": {}
}

{
  "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/cart/merge"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/cart/merge"
}

{
  "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/cart/merge"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/cart/merge"
}

Create checkout from cart

Honors Idempotency-Key header. See /docs/api/idempotency. Strongly recommended on this endpoint.

POST
/v1/checkout
AuthorizationBearer <token>

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

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
cartIdstring

ID of the cart to convert into a checkout. The cart must belong to the same store and contain at least one purchasable line item.

customerId?string

ID of the logged-in customer to attach to the checkout. When omitted, the checkout is treated as a guest checkout and the customer record (if any) is matched/created from the email on setCustomerInfo.

selectedItemIds?array<string>

Subset of cart line-item IDs to include in this checkout. Use this for "buy now" / express-checkout flows that purchase a single item from a larger cart. When omitted, every line item in the cart is included.

regionId?string

ID of the region to associate with this checkout (multi-region commerce). The region must belong to the same store. Recorded for reporting + provider scoping. FX-at-checkout: when the region currency differs from the store base AND its payment provider can settle that currency (presentment-enabled, which today means Stripe), the checkout pins a charging rate and the payment intent is charged in the region currency; the response carries a presentment overlay with the charged amounts. Otherwise the checkout is charged in the store base currency (presentment unavailable → safe fallback). When omitted, the store default region applies.

curl -X POST "https://api.brainerce.com/api/v1/checkout" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{    "cartId": "cart_01HQ7Z2YK3M8N4P5R6S7T8V9W0"  }'
{
  "id": "string",
  "status": "string",
  "email": "string",
  "customerId": "string",
  "deliveryType": "shipping",
  "pickupLocation": {
    "id": "string",
    "name": "string",
    "rateName": "string",
    "address": {
      "line1": "string",
      "line2": "string",
      "city": "string",
      "region": "string",
      "postalCode": "string",
      "country": "string"
    },
    "phone": "string",
    "hours": "string",
    "instructions": "string",
    "price": "string",
    "currency": "string"
  },
  "shippingAddress": {
    "firstName": "string",
    "lastName": "string",
    "company": "string",
    "line1": "string",
    "line2": "string",
    "city": "string",
    "region": "string",
    "postalCode": "string",
    "country": "string",
    "phone": "string"
  },
  "billingAddress": {
    "firstName": "string",
    "lastName": "string",
    "company": "string",
    "line1": "string",
    "line2": "string",
    "city": "string",
    "region": "string",
    "postalCode": "string",
    "country": "string",
    "phone": "string"
  },
  "shippingRateId": "string",
  "shippingMethod": {
    "id": "string",
    "name": "string",
    "description": "string",
    "price": "string",
    "currency": "string",
    "estimatedDays": 0,
    "source": "manual",
    "carrier": "string",
    "service": "string",
    "speedTier": "cheapest"
  },
  "currency": "string",
  "subtotal": "string",
  "discountAmount": "string",
  "ruleDiscountAmount": "string",
  "appliedRules": [
    {
      "id": "string",
      "name": "string",
      "type": "string",
      "discountAmount": "string"
    }
  ],
  "shippingAmount": "string",
  "taxAmount": "string",
  "taxBreakdown": {
    "subtotal": 0,
    "shippingNet": 0,
    "totalTax": 0,
    "total": 0,
    "breakdown": [
      {
        "name": "string",
        "rate": 0,
        "amount": 0
      }
    ],
    "currency": "string"
  },
  "total": "string",
  "surchargeAmount": "string",
  "appliedSurcharges": [
    {
      "key": "string",
      "name": "string",
      "value": {},
      "amount": "string"
    }
  ],
  "customFieldValues": {},
  "couponCode": "string",
  "tenders": [
    {
      "tenderId": "string",
      "amountApplied": "string"
    }
  ],
  "providerAmountDue": "string",
  "notes": "string",
  "lineItems": [
    {
      "id": "string",
      "productId": "string",
      "variantId": "string",
      "quantity": 0,
      "unitPrice": "string",
      "discountAmount": "string",
      "product": {
        "id": "string",
        "name": "string",
        "sku": "string",
        "images": [
          {}
        ],
        "isDownloadable": true
      },
      "variant": {
        "id": "string",
        "name": "string",
        "sku": "string"
      },
      "notes": "string",
      "metadata": {},
      "customizations": {}
    }
  ],
  "itemCount": 0,
  "availableShippingRates": [
    {
      "id": "string",
      "name": "string",
      "description": "string",
      "price": "string",
      "currency": "string",
      "estimatedDays": 0,
      "source": "manual",
      "carrier": "string",
      "service": "string",
      "speedTier": "cheapest"
    }
  ],
  "expiresAt": "2019-08-24T14:15:22Z",
  "recoveryEmailSentAt": "2019-08-24T14:15:22Z",
  "createdAt": "2019-08-24T14:15:22Z",
  "updatedAt": "2019-08-24T14:15:22Z",
  "reservation": {
    "hasReservation": true,
    "expiresAt": "2019-08-24T14:15:22Z",
    "remainingSeconds": 0,
    "strategy": "ON_PAYMENT",
    "countdownMessage": "string"
  },
  "presentment": {
    "currency": "string",
    "subtotal": "string",
    "discountAmount": "string",
    "shippingAmount": "string",
    "taxAmount": "string",
    "surchargeAmount": "string",
    "total": "string",
    "fxChargingRate": "string",
    "fxBufferPercent": "string"
  }
}

{
  "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/checkout"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/checkout"
}

{
  "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/checkout"
}

Get checkout

GET
/v1/checkout/{checkoutId}
AuthorizationBearer <token>

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

checkoutIdstring

Checkout ID

curl -X GET "https://api.brainerce.com/api/v1/checkout/string"
{
  "id": "string",
  "status": "string",
  "email": "string",
  "customerId": "string",
  "deliveryType": "shipping",
  "pickupLocation": {
    "id": "string",
    "name": "string",
    "rateName": "string",
    "address": {
      "line1": "string",
      "line2": "string",
      "city": "string",
      "region": "string",
      "postalCode": "string",
      "country": "string"
    },
    "phone": "string",
    "hours": "string",
    "instructions": "string",
    "price": "string",
    "currency": "string"
  },
  "shippingAddress": {
    "firstName": "string",
    "lastName": "string",
    "company": "string",
    "line1": "string",
    "line2": "string",
    "city": "string",
    "region": "string",
    "postalCode": "string",
    "country": "string",
    "phone": "string"
  },
  "billingAddress": {
    "firstName": "string",
    "lastName": "string",
    "company": "string",
    "line1": "string",
    "line2": "string",
    "city": "string",
    "region": "string",
    "postalCode": "string",
    "country": "string",
    "phone": "string"
  },
  "shippingRateId": "string",
  "shippingMethod": {
    "id": "string",
    "name": "string",
    "description": "string",
    "price": "string",
    "currency": "string",
    "estimatedDays": 0,
    "source": "manual",
    "carrier": "string",
    "service": "string",
    "speedTier": "cheapest"
  },
  "currency": "string",
  "subtotal": "string",
  "discountAmount": "string",
  "ruleDiscountAmount": "string",
  "appliedRules": [
    {
      "id": "string",
      "name": "string",
      "type": "string",
      "discountAmount": "string"
    }
  ],
  "shippingAmount": "string",
  "taxAmount": "string",
  "taxBreakdown": {
    "subtotal": 0,
    "shippingNet": 0,
    "totalTax": 0,
    "total": 0,
    "breakdown": [
      {
        "name": "string",
        "rate": 0,
        "amount": 0
      }
    ],
    "currency": "string"
  },
  "total": "string",
  "surchargeAmount": "string",
  "appliedSurcharges": [
    {
      "key": "string",
      "name": "string",
      "value": {},
      "amount": "string"
    }
  ],
  "customFieldValues": {},
  "couponCode": "string",
  "tenders": [
    {
      "tenderId": "string",
      "amountApplied": "string"
    }
  ],
  "providerAmountDue": "string",
  "notes": "string",
  "lineItems": [
    {
      "id": "string",
      "productId": "string",
      "variantId": "string",
      "quantity": 0,
      "unitPrice": "string",
      "discountAmount": "string",
      "product": {
        "id": "string",
        "name": "string",
        "sku": "string",
        "images": [
          {}
        ],
        "isDownloadable": true
      },
      "variant": {
        "id": "string",
        "name": "string",
        "sku": "string"
      },
      "notes": "string",
      "metadata": {},
      "customizations": {}
    }
  ],
  "itemCount": 0,
  "availableShippingRates": [
    {
      "id": "string",
      "name": "string",
      "description": "string",
      "price": "string",
      "currency": "string",
      "estimatedDays": 0,
      "source": "manual",
      "carrier": "string",
      "service": "string",
      "speedTier": "cheapest"
    }
  ],
  "expiresAt": "2019-08-24T14:15:22Z",
  "recoveryEmailSentAt": "2019-08-24T14:15:22Z",
  "createdAt": "2019-08-24T14:15:22Z",
  "updatedAt": "2019-08-24T14:15:22Z",
  "reservation": {
    "hasReservation": true,
    "expiresAt": "2019-08-24T14:15:22Z",
    "remainingSeconds": 0,
    "strategy": "ON_PAYMENT",
    "countdownMessage": "string"
  },
  "presentment": {
    "currency": "string",
    "subtotal": "string",
    "discountAmount": "string",
    "shippingAmount": "string",
    "taxAmount": "string",
    "surchargeAmount": "string",
    "total": "string",
    "fxChargingRate": "string",
    "fxBufferPercent": "string"
  }
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/checkout/{checkoutId}"
}

{
  "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/checkout/{checkoutId}"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/checkout/{checkoutId}"
}

Delete a checkout

Supports Idempotency-Key header for safe retries.

DELETE
/v1/checkout/{checkoutId}
AuthorizationBearer <token>

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

checkoutIdstring

Checkout ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
curl -X DELETE "https://api.brainerce.com/api/v1/checkout/string" \  -H "Idempotency-Key: string"
{
  "success": true
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/checkout/{checkoutId}"
}

{
  "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/checkout/{checkoutId}"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/checkout/{checkoutId}"
}

Set customer info on checkout

Supports Idempotency-Key header for safe retries.

PATCH
/v1/checkout/{checkoutId}/customer
AuthorizationBearer <token>

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

checkoutIdstring

Checkout ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
emailstring

Shopper email address. Used for order confirmation, abandoned-checkout recovery, and matching/creating the customer record.

firstName?string

Shopper first name. Optional at this step, but required later if no shipping address is set.

lastName?string

Shopper last name. Optional at this step, but required later if no shipping address is set.

phone?string

Shopper phone number in E.164 format.

acceptsMarketing?boolean

Whether the shopper opted in to marketing communications. When true, the customer record is flagged for marketing email/SMS lists.

notes?string

Order-level note from the shopper ("Please leave at the door"). Stored on the cart and copied onto the order at completion, then shown to the merchant in the dashboard and included in the confirmation email. Max 2000 chars. Send an empty string to clear a previously-set note.

Lengthlength <= 2000
analyticsClientId?string

GA4 gtag.js client_id (from gtag('get', measurementId, 'client_id', cb)), forwarded so the server-side purchase event can stitch to this browser session. Written onto the cart if not already set. Omit if unavailable, and never send a synthesized id.

Lengthlength <= 100
analyticsSessionId?string

GA4 gtag.js session_id (from gtag('get', measurementId, 'session_id', cb)), forwarded alongside analyticsClientId for the same stitch.

Lengthlength <= 100
trafficReferrerHost?string

Hostname of the external referrer that brought this shopper (last non-direct touch), auto-attached by the storefront SDK from its brainerce_attr capture. Raw host on purpose, because the platform classifies it (AI/organic/social) at read time. Omit if unknown.

Lengthlength <= 253
trafficUtmSource?string

utm_source of the last non-direct touch, auto-attached alongside trafficReferrerHost.

Lengthlength <= 150
trafficUtmMedium?string

utm_medium of the last non-direct touch.

Lengthlength <= 150
trafficUtmCampaign?string

utm_campaign of the last non-direct touch.

Lengthlength <= 150
curl -X PATCH "https://api.brainerce.com/api/v1/checkout/string/customer" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{    "email": "[email protected]"  }'
{
  "id": "string",
  "status": "string",
  "email": "string",
  "customerId": "string",
  "deliveryType": "shipping",
  "pickupLocation": {
    "id": "string",
    "name": "string",
    "rateName": "string",
    "address": {
      "line1": "string",
      "line2": "string",
      "city": "string",
      "region": "string",
      "postalCode": "string",
      "country": "string"
    },
    "phone": "string",
    "hours": "string",
    "instructions": "string",
    "price": "string",
    "currency": "string"
  },
  "shippingAddress": {
    "firstName": "string",
    "lastName": "string",
    "company": "string",
    "line1": "string",
    "line2": "string",
    "city": "string",
    "region": "string",
    "postalCode": "string",
    "country": "string",
    "phone": "string"
  },
  "billingAddress": {
    "firstName": "string",
    "lastName": "string",
    "company": "string",
    "line1": "string",
    "line2": "string",
    "city": "string",
    "region": "string",
    "postalCode": "string",
    "country": "string",
    "phone": "string"
  },
  "shippingRateId": "string",
  "shippingMethod": {
    "id": "string",
    "name": "string",
    "description": "string",
    "price": "string",
    "currency": "string",
    "estimatedDays": 0,
    "source": "manual",
    "carrier": "string",
    "service": "string",
    "speedTier": "cheapest"
  },
  "currency": "string",
  "subtotal": "string",
  "discountAmount": "string",
  "ruleDiscountAmount": "string",
  "appliedRules": [
    {
      "id": "string",
      "name": "string",
      "type": "string",
      "discountAmount": "string"
    }
  ],
  "shippingAmount": "string",
  "taxAmount": "string",
  "taxBreakdown": {
    "subtotal": 0,
    "shippingNet": 0,
    "totalTax": 0,
    "total": 0,
    "breakdown": [
      {
        "name": "string",
        "rate": 0,
        "amount": 0
      }
    ],
    "currency": "string"
  },
  "total": "string",
  "surchargeAmount": "string",
  "appliedSurcharges": [
    {
      "key": "string",
      "name": "string",
      "value": {},
      "amount": "string"
    }
  ],
  "customFieldValues": {},
  "couponCode": "string",
  "tenders": [
    {
      "tenderId": "string",
      "amountApplied": "string"
    }
  ],
  "providerAmountDue": "string",
  "notes": "string",
  "lineItems": [
    {
      "id": "string",
      "productId": "string",
      "variantId": "string",
      "quantity": 0,
      "unitPrice": "string",
      "discountAmount": "string",
      "product": {
        "id": "string",
        "name": "string",
        "sku": "string",
        "images": [
          {}
        ],
        "isDownloadable": true
      },
      "variant": {
        "id": "string",
        "name": "string",
        "sku": "string"
      },
      "notes": "string",
      "metadata": {},
      "customizations": {}
    }
  ],
  "itemCount": 0,
  "availableShippingRates": [
    {
      "id": "string",
      "name": "string",
      "description": "string",
      "price": "string",
      "currency": "string",
      "estimatedDays": 0,
      "source": "manual",
      "carrier": "string",
      "service": "string",
      "speedTier": "cheapest"
    }
  ],
  "expiresAt": "2019-08-24T14:15:22Z",
  "recoveryEmailSentAt": "2019-08-24T14:15:22Z",
  "createdAt": "2019-08-24T14:15:22Z",
  "updatedAt": "2019-08-24T14:15:22Z",
  "reservation": {
    "hasReservation": true,
    "expiresAt": "2019-08-24T14:15:22Z",
    "remainingSeconds": 0,
    "strategy": "ON_PAYMENT",
    "countdownMessage": "string"
  },
  "presentment": {
    "currency": "string",
    "subtotal": "string",
    "discountAmount": "string",
    "shippingAmount": "string",
    "taxAmount": "string",
    "surchargeAmount": "string",
    "total": "string",
    "fxChargingRate": "string",
    "fxBufferPercent": "string"
  }
}

{
  "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/checkout/{checkoutId}/customer"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/checkout/{checkoutId}/customer"
}

{
  "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/checkout/{checkoutId}/customer"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/checkout/{checkoutId}/customer"
}

Set shipping address and get available rates

Supports Idempotency-Key header for safe retries.

PATCH
/v1/checkout/{checkoutId}/shipping-address
AuthorizationBearer <token>

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

checkoutIdstring

Checkout ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
firstNamestring

Recipient first name.

lastNamestring

Recipient last name.

company?string

Company / organization name for the address (used on invoices and customs forms).

line1string

Primary street address line (street number + street name).

line2?string

Secondary address line (apartment, suite, unit, building, floor, …).

citystring

City / locality.

region?string

State / province / region code or name. Required by some shipping carriers (e.g. USPS, UPS) for US/CA/AU addresses. Use the ISO-3166-2 subdivision code when possible (e.g. CA for California).

postalCodestring

Postal / ZIP code.

countrystring

ISO-3166-1 alpha-2 country code (uppercase, 2 letters).

phone?string

Recipient phone number in E.164 format. Used by carriers for delivery notifications.

emailstring

Shopper email address. Used for order confirmation, abandoned-checkout recovery, and matching/creating the customer record.

acceptsMarketing?boolean

Whether the shopper opted in to marketing communications at checkout. When true, the customer record is flagged for marketing email/SMS lists.

notes?string

Order-level note from the shopper ("Please leave at the door"). Rides along with the address step so single-page checkouts need no extra call. Copied onto the order at completion, then shown to the merchant in the dashboard and included in the confirmation email. Max 2000 chars. Send an empty string to clear a previously-set note.

Lengthlength <= 2000
analyticsClientId?string

GA4 gtag.js client_id (from gtag('get', measurementId, 'client_id', cb)), forwarded so the server-side purchase event can stitch to this browser session. Written onto the cart if not already set. Omit if unavailable, and never send a synthesized id.

Lengthlength <= 100
analyticsSessionId?string

GA4 gtag.js session_id (from gtag('get', measurementId, 'session_id', cb)), forwarded alongside analyticsClientId for the same stitch.

Lengthlength <= 100
trafficReferrerHost?string

Hostname of the external referrer that brought this shopper (last non-direct touch), auto-attached by the storefront SDK from its brainerce_attr capture. Raw host on purpose, because the platform classifies it (AI/organic/social) at read time. Omit if unknown.

Lengthlength <= 253
trafficUtmSource?string

utm_source of the last non-direct touch, auto-attached alongside trafficReferrerHost.

Lengthlength <= 150
trafficUtmMedium?string

utm_medium of the last non-direct touch.

Lengthlength <= 150
trafficUtmCampaign?string

utm_campaign of the last non-direct touch.

Lengthlength <= 150
placeId?string

The placeId of the autocomplete suggestion the shopper picked (from checkout/address-autocomplete). Send it whenever the address came from the autocomplete: the server re-resolves it to exact rooftop coordinates and matches polygon ("draw on map") shipping zones against those, instead of re-geocoding the typed address lines. Without it, matching falls back to geocoding the text, which is materially less accurate (a same-named street in another city can outrank the right one). Note there is deliberately NO lat/lng field here: zone matching decides which shipping rate is offered and charged, so coordinates are never taken from the caller.

Lengthlength <= 512
placeSessionToken?string

The same sessionToken used for the checkout/address-autocomplete calls that produced placeId. Optional: the resolved place is cached server-side for 24h by placeId, so the normal flow (autocomplete → address-details → this call) hits that cache and needs no token. Pass it when you have it so that a cache miss still bills within the original autocomplete session instead of opening a new one.

Lengthlength <= 200
curl -X PATCH "https://api.brainerce.com/api/v1/checkout/string/shipping-address" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{    "firstName": "Jane",    "lastName": "Doe",    "line1": "123 Main Street",    "city": "San Francisco",    "postalCode": "94105",    "country": "US",    "email": "[email protected]"  }'
{
  "checkout": {
    "id": "string",
    "status": "string",
    "email": "string",
    "customerId": "string",
    "deliveryType": "shipping",
    "pickupLocation": {
      "id": "string",
      "name": "string",
      "rateName": "string",
      "address": {
        "line1": "string",
        "line2": "string",
        "city": "string",
        "region": "string",
        "postalCode": "string",
        "country": "string"
      },
      "phone": "string",
      "hours": "string",
      "instructions": "string",
      "price": "string",
      "currency": "string"
    },
    "shippingAddress": {
      "firstName": "string",
      "lastName": "string",
      "company": "string",
      "line1": "string",
      "line2": "string",
      "city": "string",
      "region": "string",
      "postalCode": "string",
      "country": "string",
      "phone": "string"
    },
    "billingAddress": {
      "firstName": "string",
      "lastName": "string",
      "company": "string",
      "line1": "string",
      "line2": "string",
      "city": "string",
      "region": "string",
      "postalCode": "string",
      "country": "string",
      "phone": "string"
    },
    "shippingRateId": "string",
    "shippingMethod": {
      "id": "string",
      "name": "string",
      "description": "string",
      "price": "string",
      "currency": "string",
      "estimatedDays": 0,
      "source": "manual",
      "carrier": "string",
      "service": "string",
      "speedTier": "cheapest"
    },
    "currency": "string",
    "subtotal": "string",
    "discountAmount": "string",
    "ruleDiscountAmount": "string",
    "appliedRules": [
      {
        "id": "string",
        "name": "string",
        "type": "string",
        "discountAmount": "string"
      }
    ],
    "shippingAmount": "string",
    "taxAmount": "string",
    "taxBreakdown": {
      "subtotal": 0,
      "shippingNet": 0,
      "totalTax": 0,
      "total": 0,
      "breakdown": [
        {
          "name": "string",
          "rate": 0,
          "amount": 0
        }
      ],
      "currency": "string"
    },
    "total": "string",
    "surchargeAmount": "string",
    "appliedSurcharges": [
      {
        "key": "string",
        "name": "string",
        "value": {},
        "amount": "string"
      }
    ],
    "customFieldValues": {},
    "couponCode": "string",
    "tenders": [
      {
        "tenderId": "string",
        "amountApplied": "string"
      }
    ],
    "providerAmountDue": "string",
    "notes": "string",
    "lineItems": [
      {
        "id": "string",
        "productId": "string",
        "variantId": "string",
        "quantity": 0,
        "unitPrice": "string",
        "discountAmount": "string",
        "product": {
          "id": "string",
          "name": "string",
          "sku": "string",
          "images": [
            {}
          ],
          "isDownloadable": true
        },
        "variant": {
          "id": "string",
          "name": "string",
          "sku": "string"
        },
        "notes": "string",
        "metadata": {},
        "customizations": {}
      }
    ],
    "itemCount": 0,
    "availableShippingRates": [
      {
        "id": "string",
        "name": "string",
        "description": "string",
        "price": "string",
        "currency": "string",
        "estimatedDays": 0,
        "source": "manual",
        "carrier": "string",
        "service": "string",
        "speedTier": "cheapest"
      }
    ],
    "expiresAt": "2019-08-24T14:15:22Z",
    "recoveryEmailSentAt": "2019-08-24T14:15:22Z",
    "createdAt": "2019-08-24T14:15:22Z",
    "updatedAt": "2019-08-24T14:15:22Z",
    "reservation": {
      "hasReservation": true,
      "expiresAt": "2019-08-24T14:15:22Z",
      "remainingSeconds": 0,
      "strategy": "ON_PAYMENT",
      "countdownMessage": "string"
    },
    "presentment": {
      "currency": "string",
      "subtotal": "string",
      "discountAmount": "string",
      "shippingAmount": "string",
      "taxAmount": "string",
      "surchargeAmount": "string",
      "total": "string",
      "fxChargingRate": "string",
      "fxBufferPercent": "string"
    }
  },
  "rates": [
    {
      "id": "string",
      "name": "string",
      "description": "string",
      "price": "string",
      "currency": "string",
      "estimatedDays": 0,
      "source": "manual",
      "carrier": "string",
      "service": "string",
      "speedTier": "cheapest"
    }
  ]
}

{
  "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/checkout/{checkoutId}/shipping-address"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/checkout/{checkoutId}/shipping-address"
}

{
  "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/checkout/{checkoutId}/shipping-address"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/checkout/{checkoutId}/shipping-address"
}

Get available shipping rates

GET
/v1/checkout/{checkoutId}/shipping-rates
AuthorizationBearer <token>

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

checkoutIdstring

Checkout ID

curl -X GET "https://api.brainerce.com/api/v1/checkout/string/shipping-rates"
[
  {
    "id": "string",
    "name": "string",
    "description": "string",
    "price": "string",
    "currency": "string",
    "estimatedDays": 0,
    "source": "manual",
    "carrier": "string",
    "service": "string",
    "speedTier": "cheapest"
  }
]

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/checkout/{checkoutId}/shipping-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/checkout/{checkoutId}/shipping-rates"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/checkout/{checkoutId}/shipping-rates"
}

Select shipping method

Supports Idempotency-Key header for safe retries.

PATCH
/v1/checkout/{checkoutId}/shipping-method
AuthorizationBearer <token>

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

checkoutIdstring

Checkout ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
shippingRateIdstring

ID of the shipping rate to apply to the checkout. The ID must come from the availableShippingRates array on the checkout response (set after a shipping address is provided). For carrier-sourced rates the ID is opaque and only valid for the current checkout snapshot.

curl -X PATCH "https://api.brainerce.com/api/v1/checkout/string/shipping-method" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{    "shippingRateId": "rate_01HQ7Z2YK3M8N4P5R6S7T8V9W0"  }'
{
  "id": "string",
  "status": "string",
  "email": "string",
  "customerId": "string",
  "deliveryType": "shipping",
  "pickupLocation": {
    "id": "string",
    "name": "string",
    "rateName": "string",
    "address": {
      "line1": "string",
      "line2": "string",
      "city": "string",
      "region": "string",
      "postalCode": "string",
      "country": "string"
    },
    "phone": "string",
    "hours": "string",
    "instructions": "string",
    "price": "string",
    "currency": "string"
  },
  "shippingAddress": {
    "firstName": "string",
    "lastName": "string",
    "company": "string",
    "line1": "string",
    "line2": "string",
    "city": "string",
    "region": "string",
    "postalCode": "string",
    "country": "string",
    "phone": "string"
  },
  "billingAddress": {
    "firstName": "string",
    "lastName": "string",
    "company": "string",
    "line1": "string",
    "line2": "string",
    "city": "string",
    "region": "string",
    "postalCode": "string",
    "country": "string",
    "phone": "string"
  },
  "shippingRateId": "string",
  "shippingMethod": {
    "id": "string",
    "name": "string",
    "description": "string",
    "price": "string",
    "currency": "string",
    "estimatedDays": 0,
    "source": "manual",
    "carrier": "string",
    "service": "string",
    "speedTier": "cheapest"
  },
  "currency": "string",
  "subtotal": "string",
  "discountAmount": "string",
  "ruleDiscountAmount": "string",
  "appliedRules": [
    {
      "id": "string",
      "name": "string",
      "type": "string",
      "discountAmount": "string"
    }
  ],
  "shippingAmount": "string",
  "taxAmount": "string",
  "taxBreakdown": {
    "subtotal": 0,
    "shippingNet": 0,
    "totalTax": 0,
    "total": 0,
    "breakdown": [
      {
        "name": "string",
        "rate": 0,
        "amount": 0
      }
    ],
    "currency": "string"
  },
  "total": "string",
  "surchargeAmount": "string",
  "appliedSurcharges": [
    {
      "key": "string",
      "name": "string",
      "value": {},
      "amount": "string"
    }
  ],
  "customFieldValues": {},
  "couponCode": "string",
  "tenders": [
    {
      "tenderId": "string",
      "amountApplied": "string"
    }
  ],
  "providerAmountDue": "string",
  "notes": "string",
  "lineItems": [
    {
      "id": "string",
      "productId": "string",
      "variantId": "string",
      "quantity": 0,
      "unitPrice": "string",
      "discountAmount": "string",
      "product": {
        "id": "string",
        "name": "string",
        "sku": "string",
        "images": [
          {}
        ],
        "isDownloadable": true
      },
      "variant": {
        "id": "string",
        "name": "string",
        "sku": "string"
      },
      "notes": "string",
      "metadata": {},
      "customizations": {}
    }
  ],
  "itemCount": 0,
  "availableShippingRates": [
    {
      "id": "string",
      "name": "string",
      "description": "string",
      "price": "string",
      "currency": "string",
      "estimatedDays": 0,
      "source": "manual",
      "carrier": "string",
      "service": "string",
      "speedTier": "cheapest"
    }
  ],
  "expiresAt": "2019-08-24T14:15:22Z",
  "recoveryEmailSentAt": "2019-08-24T14:15:22Z",
  "createdAt": "2019-08-24T14:15:22Z",
  "updatedAt": "2019-08-24T14:15:22Z",
  "reservation": {
    "hasReservation": true,
    "expiresAt": "2019-08-24T14:15:22Z",
    "remainingSeconds": 0,
    "strategy": "ON_PAYMENT",
    "countdownMessage": "string"
  },
  "presentment": {
    "currency": "string",
    "subtotal": "string",
    "discountAmount": "string",
    "shippingAmount": "string",
    "taxAmount": "string",
    "surchargeAmount": "string",
    "total": "string",
    "fxChargingRate": "string",
    "fxBufferPercent": "string"
  }
}

{
  "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/checkout/{checkoutId}/shipping-method"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/checkout/{checkoutId}/shipping-method"
}

{
  "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/checkout/{checkoutId}/shipping-method"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/checkout/{checkoutId}/shipping-method"
}

Set billing address

Supports Idempotency-Key header for safe retries.

PATCH
/v1/checkout/{checkoutId}/billing-address
AuthorizationBearer <token>

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

checkoutIdstring

Checkout ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
firstNamestring

Recipient first name.

lastNamestring

Recipient last name.

company?string

Company / organization name for the address (used on invoices and customs forms).

line1string

Primary street address line (street number + street name).

line2?string

Secondary address line (apartment, suite, unit, building, floor, …).

citystring

City / locality.

region?string

State / province / region code or name. Required by some shipping carriers (e.g. USPS, UPS) for US/CA/AU addresses. Use the ISO-3166-2 subdivision code when possible (e.g. CA for California).

postalCodestring

Postal / ZIP code.

countrystring

ISO-3166-1 alpha-2 country code (uppercase, 2 letters).

phone?string

Recipient phone number in E.164 format. Used by carriers for delivery notifications.

sameAsShipping?boolean

When true, the billing address is set identically to the previously-saved shipping address and the address fields on this request are ignored.

curl -X PATCH "https://api.brainerce.com/api/v1/checkout/string/billing-address" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{    "firstName": "Jane",    "lastName": "Doe",    "line1": "123 Main Street",    "city": "San Francisco",    "postalCode": "94105",    "country": "US"  }'
{
  "id": "string",
  "status": "string",
  "email": "string",
  "customerId": "string",
  "deliveryType": "shipping",
  "pickupLocation": {
    "id": "string",
    "name": "string",
    "rateName": "string",
    "address": {
      "line1": "string",
      "line2": "string",
      "city": "string",
      "region": "string",
      "postalCode": "string",
      "country": "string"
    },
    "phone": "string",
    "hours": "string",
    "instructions": "string",
    "price": "string",
    "currency": "string"
  },
  "shippingAddress": {
    "firstName": "string",
    "lastName": "string",
    "company": "string",
    "line1": "string",
    "line2": "string",
    "city": "string",
    "region": "string",
    "postalCode": "string",
    "country": "string",
    "phone": "string"
  },
  "billingAddress": {
    "firstName": "string",
    "lastName": "string",
    "company": "string",
    "line1": "string",
    "line2": "string",
    "city": "string",
    "region": "string",
    "postalCode": "string",
    "country": "string",
    "phone": "string"
  },
  "shippingRateId": "string",
  "shippingMethod": {
    "id": "string",
    "name": "string",
    "description": "string",
    "price": "string",
    "currency": "string",
    "estimatedDays": 0,
    "source": "manual",
    "carrier": "string",
    "service": "string",
    "speedTier": "cheapest"
  },
  "currency": "string",
  "subtotal": "string",
  "discountAmount": "string",
  "ruleDiscountAmount": "string",
  "appliedRules": [
    {
      "id": "string",
      "name": "string",
      "type": "string",
      "discountAmount": "string"
    }
  ],
  "shippingAmount": "string",
  "taxAmount": "string",
  "taxBreakdown": {
    "subtotal": 0,
    "shippingNet": 0,
    "totalTax": 0,
    "total": 0,
    "breakdown": [
      {
        "name": "string",
        "rate": 0,
        "amount": 0
      }
    ],
    "currency": "string"
  },
  "total": "string",
  "surchargeAmount": "string",
  "appliedSurcharges": [
    {
      "key": "string",
      "name": "string",
      "value": {},
      "amount": "string"
    }
  ],
  "customFieldValues": {},
  "couponCode": "string",
  "tenders": [
    {
      "tenderId": "string",
      "amountApplied": "string"
    }
  ],
  "providerAmountDue": "string",
  "notes": "string",
  "lineItems": [
    {
      "id": "string",
      "productId": "string",
      "variantId": "string",
      "quantity": 0,
      "unitPrice": "string",
      "discountAmount": "string",
      "product": {
        "id": "string",
        "name": "string",
        "sku": "string",
        "images": [
          {}
        ],
        "isDownloadable": true
      },
      "variant": {
        "id": "string",
        "name": "string",
        "sku": "string"
      },
      "notes": "string",
      "metadata": {},
      "customizations": {}
    }
  ],
  "itemCount": 0,
  "availableShippingRates": [
    {
      "id": "string",
      "name": "string",
      "description": "string",
      "price": "string",
      "currency": "string",
      "estimatedDays": 0,
      "source": "manual",
      "carrier": "string",
      "service": "string",
      "speedTier": "cheapest"
    }
  ],
  "expiresAt": "2019-08-24T14:15:22Z",
  "recoveryEmailSentAt": "2019-08-24T14:15:22Z",
  "createdAt": "2019-08-24T14:15:22Z",
  "updatedAt": "2019-08-24T14:15:22Z",
  "reservation": {
    "hasReservation": true,
    "expiresAt": "2019-08-24T14:15:22Z",
    "remainingSeconds": 0,
    "strategy": "ON_PAYMENT",
    "countdownMessage": "string"
  },
  "presentment": {
    "currency": "string",
    "subtotal": "string",
    "discountAmount": "string",
    "shippingAmount": "string",
    "taxAmount": "string",
    "surchargeAmount": "string",
    "total": "string",
    "fxChargingRate": "string",
    "fxBufferPercent": "string"
  }
}

{
  "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/checkout/{checkoutId}/billing-address"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/checkout/{checkoutId}/billing-address"
}

{
  "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/checkout/{checkoutId}/billing-address"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/checkout/{checkoutId}/billing-address"
}

Apply a gift card to a checkout

Supports Idempotency-Key header for safe retries.

POST
/v1/checkout/{checkoutId}/gift-card
AuthorizationBearer <token>

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

checkoutIdstring

Checkout ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
codestring

The gift card code as the shopper typed it. Case, spaces and dashes are all tolerated, and characters excluded from the code alphabet (I, L, O, U) are corrected to the ones they are mistaken for.

curl -X POST "https://api.brainerce.com/api/v1/checkout/string/gift-card" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{    "code": "A1B2-C3D4-E5F6-G7H8-J9K0"  }'
Empty

{
  "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/checkout/{checkoutId}/gift-card"
}

{
  "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/checkout/{checkoutId}/gift-card"
}

{
  "statusCode": 429,
  "code": "RATE_LIMITED",
  "message": "Too many requests",
  "details": {
    "retryAfterSeconds": 12
  },
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/checkout/{checkoutId}/gift-card"
}

Remove a gift card from a checkout

DELETE
/v1/checkout/{checkoutId}/gift-card/{tenderId}
AuthorizationBearer <token>

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

checkoutIdstring

Checkout ID

tenderIdstring

Tender id returned when the card was applied

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
curl -X DELETE "https://api.brainerce.com/api/v1/checkout/string/gift-card/string" \  -H "Idempotency-Key: string"
Empty

Check a gift card balance

POST
/v1/gift-cards/balance
AuthorizationBearer <token>

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

codestring

The gift card code as the shopper typed it. Case, spaces and dashes are all tolerated, and characters excluded from the code alphabet (I, L, O, U) are corrected to the ones they are mistaken for.

curl -X POST "https://api.brainerce.com/api/v1/gift-cards/balance" \  -H "Content-Type: application/json" \  -d '{    "code": "A1B2-C3D4-E5F6-G7H8-J9K0"  }'
Empty

List gift cards

GET
/v1/gift-cards
AuthorizationBearer <token>

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

Query Parameters

search?string
filter?string
limit?number
page?number
curl -X GET "https://api.brainerce.com/api/v1/gift-cards?search=string&filter=string&limit=0&page=0"
Empty

Issue a gift card

Returns the plaintext code EXACTLY ONCE. It is stored only as an HMAC and cannot be retrieved again by anyone, including this API — persist it from this response or deliver it. A note is required: issuing stored value with no stated reason is not auditable. Refused when gift cards are switched off for the store.

POST
/v1/gift-cards
AuthorizationBearer <token>

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

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
amountstring

Amount to load onto the card, as a decimal string.

notestring

Why this card was issued. Required, and stored on the ledger row itself — issuing value with no stated reason is not auditable, and this is the entry a finance review will read a year from now.

customerId?string

Link the card to a customer.

expiresAt?string

Expiry. Must be in the future. Leave unset for a card that does not expire.

recipientEmail?string

Recipient email, for delivery.

recipientName?string

Recipient name, for the delivery email.

personalMessage?string

A message from the buyer to the recipient.

curl -X POST "https://api.brainerce.com/api/v1/gift-cards" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{    "amount": "200.00",    "note": "Goodwill for order ORD-20260902-0041, damaged in transit"  }'
Empty

Outstanding gift card liability

Per currency. Expired value is reported separately and is NOT written off — whether expiry extinguishes the obligation is an open legal question, so it is never folded into either side.

GET
/v1/gift-cards/liability
AuthorizationBearer <token>

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/gift-cards/liability"
Empty

Disable or reactivate many gift cards

DISABLED stops new holds; ACTIVE returns a card to service. REVOKED is not accepted in bulk — it belongs to re-issue, which moves the balance to a replacement first. There is no delete, here or anywhere: the ledger is append-only and a card may carry a statutory retention life.

PATCH
/v1/gift-cards/bulk/status
AuthorizationBearer <token>

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

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
giftCardIdsarray<string>

Gift card ids to update.

statusstring

DISABLED stops new holds; ACTIVE puts the card back in service. REVOKED is deliberately not accepted in bulk — it belongs to re-issue, which moves the balance to a replacement first.

Value in"ACTIVE" | "DISABLED"
curl -X PATCH "https://api.brainerce.com/api/v1/gift-cards/bulk/status" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{    "giftCardIds": [      "string"    ],    "status": "ACTIVE"  }'
Empty

Get a gift card and its full ledger

GET
/v1/gift-cards/{id}
AuthorizationBearer <token>

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

idstring

Gift card ID

curl -X GET "https://api.brainerce.com/api/v1/gift-cards/string"
Empty

Re-issue a gift card onto a new code

The answer to a customer losing their code. Mints a new code, moves the WHOLE balance to it, and REVOKES the old card — this is not a resend, and the old code stops working. Refused while a checkout holds value on the card. The original expiry carries forward, so this cannot restart an expiry clock. Returns the new code exactly once.

POST
/v1/gift-cards/{id}/reissue
AuthorizationBearer <token>

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

idstring

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
notestring

Why the card is being replaced. Required, and recorded on the ledger rows of both the revoked card and its replacement.

curl -X POST "https://api.brainerce.com/api/v1/gift-cards/string/reissue" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{    "note": "string"  }'
Empty

Adjust a gift card balance

Signed decimal: "25.00" adds, "-25.00" takes away. A note is REQUIRED and is written to the ledger permanently — this is the row a finance review reads a year from now. A debit cannot take the balance below what live checkout holds have already reserved.

PATCH
/v1/gift-cards/{id}/adjust
AuthorizationBearer <token>

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

idstring

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
deltastring

Signed decimal string. "25.00" credits the card, "-25.00" debits it.

notestring

Why. Required, and recorded on the ledger row.

curl -X PATCH "https://api.brainerce.com/api/v1/gift-cards/string/adjust" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{    "delta": "-25.00",    "note": "string"  }'
Empty

Enable, disable or revoke a gift card

Deliberately does NOT touch live holds: a checkout that already reserved value settles normally, because pulling it out from under a shopper mid-payment would strand a provider charge already in flight. Disabling stops NEW holds, which is what off actually means.

PATCH
/v1/gift-cards/{id}/status
AuthorizationBearer <token>

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

idstring

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
statusstring

DISABLED and REVOKED stop NEW holds. Neither touches a hold a checkout is already carrying — pulling value out from under a shopper mid-payment would strand a provider charge that is already in flight.

Value in"ACTIVE" | "DISABLED" | "REVOKED"
curl -X PATCH "https://api.brainerce.com/api/v1/gift-cards/string/status" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{    "status": "ACTIVE"  }'
Empty

Apply coupon to checkout

Supports Idempotency-Key header for safe retries.

POST
/v1/checkout/{checkoutId}/coupon
AuthorizationBearer <token>

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

checkoutIdstring

Checkout ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
codestring

Coupon code (case-insensitive). Validation runs server-side, and invalid codes return 400 with code: COUPON_INVALID or COUPON_USAGE_LIMIT_REACHED.

curl -X POST "https://api.brainerce.com/api/v1/checkout/string/coupon" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{    "code": "SUMMER25"  }'
{
  "id": "string",
  "status": "string",
  "email": "string",
  "customerId": "string",
  "deliveryType": "shipping",
  "pickupLocation": {
    "id": "string",
    "name": "string",
    "rateName": "string",
    "address": {
      "line1": "string",
      "line2": "string",
      "city": "string",
      "region": "string",
      "postalCode": "string",
      "country": "string"
    },
    "phone": "string",
    "hours": "string",
    "instructions": "string",
    "price": "string",
    "currency": "string"
  },
  "shippingAddress": {
    "firstName": "string",
    "lastName": "string",
    "company": "string",
    "line1": "string",
    "line2": "string",
    "city": "string",
    "region": "string",
    "postalCode": "string",
    "country": "string",
    "phone": "string"
  },
  "billingAddress": {
    "firstName": "string",
    "lastName": "string",
    "company": "string",
    "line1": "string",
    "line2": "string",
    "city": "string",
    "region": "string",
    "postalCode": "string",
    "country": "string",
    "phone": "string"
  },
  "shippingRateId": "string",
  "shippingMethod": {
    "id": "string",
    "name": "string",
    "description": "string",
    "price": "string",
    "currency": "string",
    "estimatedDays": 0,
    "source": "manual",
    "carrier": "string",
    "service": "string",
    "speedTier": "cheapest"
  },
  "currency": "string",
  "subtotal": "string",
  "discountAmount": "string",
  "ruleDiscountAmount": "string",
  "appliedRules": [
    {
      "id": "string",
      "name": "string",
      "type": "string",
      "discountAmount": "string"
    }
  ],
  "shippingAmount": "string",
  "taxAmount": "string",
  "taxBreakdown": {
    "subtotal": 0,
    "shippingNet": 0,
    "totalTax": 0,
    "total": 0,
    "breakdown": [
      {
        "name": "string",
        "rate": 0,
        "amount": 0
      }
    ],
    "currency": "string"
  },
  "total": "string",
  "surchargeAmount": "string",
  "appliedSurcharges": [
    {
      "key": "string",
      "name": "string",
      "value": {},
      "amount": "string"
    }
  ],
  "customFieldValues": {},
  "couponCode": "string",
  "tenders": [
    {
      "tenderId": "string",
      "amountApplied": "string"
    }
  ],
  "providerAmountDue": "string",
  "notes": "string",
  "lineItems": [
    {
      "id": "string",
      "productId": "string",
      "variantId": "string",
      "quantity": 0,
      "unitPrice": "string",
      "discountAmount": "string",
      "product": {
        "id": "string",
        "name": "string",
        "sku": "string",
        "images": [
          {}
        ],
        "isDownloadable": true
      },
      "variant": {
        "id": "string",
        "name": "string",
        "sku": "string"
      },
      "notes": "string",
      "metadata": {},
      "customizations": {}
    }
  ],
  "itemCount": 0,
  "availableShippingRates": [
    {
      "id": "string",
      "name": "string",
      "description": "string",
      "price": "string",
      "currency": "string",
      "estimatedDays": 0,
      "source": "manual",
      "carrier": "string",
      "service": "string",
      "speedTier": "cheapest"
    }
  ],
  "expiresAt": "2019-08-24T14:15:22Z",
  "recoveryEmailSentAt": "2019-08-24T14:15:22Z",
  "createdAt": "2019-08-24T14:15:22Z",
  "updatedAt": "2019-08-24T14:15:22Z",
  "reservation": {
    "hasReservation": true,
    "expiresAt": "2019-08-24T14:15:22Z",
    "remainingSeconds": 0,
    "strategy": "ON_PAYMENT",
    "countdownMessage": "string"
  },
  "presentment": {
    "currency": "string",
    "subtotal": "string",
    "discountAmount": "string",
    "shippingAmount": "string",
    "taxAmount": "string",
    "surchargeAmount": "string",
    "total": "string",
    "fxChargingRate": "string",
    "fxBufferPercent": "string"
  }
}

{
  "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/checkout/{checkoutId}/coupon"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/checkout/{checkoutId}/coupon"
}

{
  "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/checkout/{checkoutId}/coupon"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/checkout/{checkoutId}/coupon"
}

{
  "statusCode": 429,
  "code": "RATE_LIMITED",
  "message": "Too many requests",
  "details": {
    "retryAfterSeconds": 12
  },
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/checkout/{checkoutId}/coupon"
}

Remove coupon from checkout

Supports Idempotency-Key header for safe retries.

DELETE
/v1/checkout/{checkoutId}/coupon
AuthorizationBearer <token>

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

checkoutIdstring

Checkout ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
curl -X DELETE "https://api.brainerce.com/api/v1/checkout/string/coupon" \  -H "Idempotency-Key: string"
{
  "id": "string",
  "status": "string",
  "email": "string",
  "customerId": "string",
  "deliveryType": "shipping",
  "pickupLocation": {
    "id": "string",
    "name": "string",
    "rateName": "string",
    "address": {
      "line1": "string",
      "line2": "string",
      "city": "string",
      "region": "string",
      "postalCode": "string",
      "country": "string"
    },
    "phone": "string",
    "hours": "string",
    "instructions": "string",
    "price": "string",
    "currency": "string"
  },
  "shippingAddress": {
    "firstName": "string",
    "lastName": "string",
    "company": "string",
    "line1": "string",
    "line2": "string",
    "city": "string",
    "region": "string",
    "postalCode": "string",
    "country": "string",
    "phone": "string"
  },
  "billingAddress": {
    "firstName": "string",
    "lastName": "string",
    "company": "string",
    "line1": "string",
    "line2": "string",
    "city": "string",
    "region": "string",
    "postalCode": "string",
    "country": "string",
    "phone": "string"
  },
  "shippingRateId": "string",
  "shippingMethod": {
    "id": "string",
    "name": "string",
    "description": "string",
    "price": "string",
    "currency": "string",
    "estimatedDays": 0,
    "source": "manual",
    "carrier": "string",
    "service": "string",
    "speedTier": "cheapest"
  },
  "currency": "string",
  "subtotal": "string",
  "discountAmount": "string",
  "ruleDiscountAmount": "string",
  "appliedRules": [
    {
      "id": "string",
      "name": "string",
      "type": "string",
      "discountAmount": "string"
    }
  ],
  "shippingAmount": "string",
  "taxAmount": "string",
  "taxBreakdown": {
    "subtotal": 0,
    "shippingNet": 0,
    "totalTax": 0,
    "total": 0,
    "breakdown": [
      {
        "name": "string",
        "rate": 0,
        "amount": 0
      }
    ],
    "currency": "string"
  },
  "total": "string",
  "surchargeAmount": "string",
  "appliedSurcharges": [
    {
      "key": "string",
      "name": "string",
      "value": {},
      "amount": "string"
    }
  ],
  "customFieldValues": {},
  "couponCode": "string",
  "tenders": [
    {
      "tenderId": "string",
      "amountApplied": "string"
    }
  ],
  "providerAmountDue": "string",
  "notes": "string",
  "lineItems": [
    {
      "id": "string",
      "productId": "string",
      "variantId": "string",
      "quantity": 0,
      "unitPrice": "string",
      "discountAmount": "string",
      "product": {
        "id": "string",
        "name": "string",
        "sku": "string",
        "images": [
          {}
        ],
        "isDownloadable": true
      },
      "variant": {
        "id": "string",
        "name": "string",
        "sku": "string"
      },
      "notes": "string",
      "metadata": {},
      "customizations": {}
    }
  ],
  "itemCount": 0,
  "availableShippingRates": [
    {
      "id": "string",
      "name": "string",
      "description": "string",
      "price": "string",
      "currency": "string",
      "estimatedDays": 0,
      "source": "manual",
      "carrier": "string",
      "service": "string",
      "speedTier": "cheapest"
    }
  ],
  "expiresAt": "2019-08-24T14:15:22Z",
  "recoveryEmailSentAt": "2019-08-24T14:15:22Z",
  "createdAt": "2019-08-24T14:15:22Z",
  "updatedAt": "2019-08-24T14:15:22Z",
  "reservation": {
    "hasReservation": true,
    "expiresAt": "2019-08-24T14:15:22Z",
    "remainingSeconds": 0,
    "strategy": "ON_PAYMENT",
    "countdownMessage": "string"
  },
  "presentment": {
    "currency": "string",
    "subtotal": "string",
    "discountAmount": "string",
    "shippingAmount": "string",
    "taxAmount": "string",
    "surchargeAmount": "string",
    "total": "string",
    "fxChargingRate": "string",
    "fxBufferPercent": "string"
  }
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/checkout/{checkoutId}/coupon"
}

{
  "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/checkout/{checkoutId}/coupon"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/checkout/{checkoutId}/coupon"
}

Complete checkout and create order

Requires Idempotency-Key header, because this is the most critical endpoint to retry safely. Without an idempotency key, a network timeout during card capture can produce two orders. See /docs/api/idempotency.

POST
/v1/checkout/{checkoutId}/complete
AuthorizationBearer <token>

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

checkoutIdstring

Checkout ID

Header Parameters

Idempotency-Keystring

REQUIRED on this endpoint — it moves money, so an omitted key is refused with a 400 rather than risking a double charge on retry.

Because the key is mandatory here, this endpoint also fails closed: if the once-only lock cannot be taken (the lock store is unreachable) the request is refused with 503 Service Unavailable and code: "IDEMPOTENCY_LOCK_UNAVAILABLE", and nothing is processed — no order, no charge. Retry with the same key after a short backoff; do NOT generate a new one, and do not confuse this with IDEMPOTENCY_KEY_REUSED, which is a 409 telling you the opposite.

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
curl -X POST "https://api.brainerce.com/api/v1/checkout/string/complete" \  -H "Idempotency-Key: string"
{
  "orderId": "string",
  "orderNumber": "string",
  "status": "string",
  "total": "46.96",
  "message": "string"
}

{
  "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/checkout/{checkoutId}/complete"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/checkout/{checkoutId}/complete"
}

{
  "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/checkout/{checkoutId}/complete"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/checkout/{checkoutId}/complete"
}

{
  "statusCode": 409,
  "code": "CONFLICT",
  "message": "The request conflicts with the current state of the resource",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/checkout/{checkoutId}/complete"
}

{
  "statusCode": 503,
  "code": "INTERNAL_SERVER_ERROR",
  "message": "Internal server error",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/checkout/{checkoutId}/complete"
}

List coupons with pagination

GET
/v1/coupons
AuthorizationBearer <token>

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

Query Parameters

sortOrder?string
sortBy?string
platform?string
type?string
status?string
search?string
limit?number
page?number
curl -X GET "https://api.brainerce.com/api/v1/coupons?sortOrder=string&sortBy=string&platform=string&type=string&status=string&search=string&limit=0&page=0"
{
  "data": [
    {
      "id": "string",
      "code": "SUMMER20",
      "title": "string",
      "description": "string",
      "type": "string",
      "value": "string",
      "startsAt": "2019-08-24T14:15:22Z",
      "endsAt": "2019-08-24T14:15:22Z",
      "status": "string",
      "usageLimit": 0,
      "usageLimitPerCustomer": 0,
      "usageCount": 0,
      "minimumOrderAmount": "string",
      "maximumDiscount": "string",
      "conditions": {},
      "applicableProducts": [
        {
          "id": "clx1a2b3c4d5e6f7g8h9",
          "name": "Summer Collection"
        }
      ],
      "excludedProducts": [
        {
          "id": "clx1a2b3c4d5e6f7g8h9",
          "name": "Summer Collection"
        }
      ],
      "applicableCategories": [
        {
          "id": "clx1a2b3c4d5e6f7g8h9",
          "name": "Summer Collection"
        }
      ],
      "excludedCategories": [
        {
          "id": "clx1a2b3c4d5e6f7g8h9",
          "name": "Summer Collection"
        }
      ],
      "regionIds": [
        "string"
      ],
      "combinesWithOther": true,
      "needsSync": true,
      "lastSyncedAt": "2019-08-24T14:15:22Z",
      "channels": {},
      "channelPublishes": [
        {
          "salesChannel": {
            "id": "clx1a2b3c4d5e6f7g8h9",
            "name": "My storefront",
            "connectionId": "vc_9f3a2b1c8d7e6f5a"
          },
          "connection": {
            "id": "clx1a2b3c4d5e6f7g8h9",
            "name": "My storefront",
            "connectionId": "vc_9f3a2b1c8d7e6f5a"
          }
        }
      ],
      "vibeCodedPublishes": [
        {
          "salesChannel": {
            "id": "clx1a2b3c4d5e6f7g8h9",
            "name": "My storefront",
            "connectionId": "vc_9f3a2b1c8d7e6f5a"
          },
          "connection": {
            "id": "clx1a2b3c4d5e6f7g8h9",
            "name": "My storefront",
            "connectionId": "vc_9f3a2b1c8d7e6f5a"
          }
        }
      ],
      "createdAt": "2019-08-24T14:15:22Z",
      "updatedAt": "2019-08-24T14:15:22Z"
    }
  ],
  "meta": {
    "page": 1,
    "limit": 20,
    "total": 137,
    "totalPages": 7
  }
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/coupons"
}

{
  "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/coupons"
}

Create a new coupon

Honors Idempotency-Key header. See /docs/api/idempotency.

POST
/v1/coupons
AuthorizationBearer <token>

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

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
codestring

Redemption code. Case-insensitive, and unique per store.

title?string

Human-readable title shown in the dashboard list.

description?string

Internal description (not shown to shoppers).

typestring

Discount type. PERCENTAGE = value is a percent (0-100); FIXED_AMOUNT = value is in store currency.

Value in"PERCENTAGE" | "FIXED_AMOUNT"
valuenumber

Discount amount. Interpretation depends on type: 25 for PERCENTAGE means 25% off; 25 for FIXED_AMOUNT means $25 off.

Range0 <= value
startsAt?string

ISO 8601 start date. Coupon is inactive before this timestamp.

endsAt?string

ISO 8601 end date. Coupon is inactive after this timestamp.

status?string

Initial status. Defaults to ACTIVE. Use SCHEDULED when startsAt is in the future.

Value in"ACTIVE" | "SCHEDULED" | "EXPIRED" | "DISABLED"
usageLimit?number

Total redemption cap across all customers. Omit for unlimited.

Range0 <= value
usageLimitPerCustomer?number

Per-customer redemption cap (requires customerId on the checkout).

Range0 <= value
minimumOrderAmount?number

Minimum cart subtotal to redeem.

Range0 <= value
maximumDiscount?number

Cap on the discount value for PERCENTAGE coupons (e.g. "25% off, up to $50"). Ignored for FIXED_AMOUNT.

Range0 <= value
conditions?object

Advanced conditions (customer segment, sales channel, …). Free-form JSON.

Empty Object

applicableProducts?array<string>

Restrict redemption to specific product IDs.

regionIds?array<string>

Restrict the coupon to specific regions (PRD §24). Empty/omitted = applies in all regions. Holds Region IDs of this store.

excludedProducts?array<string>

Exclude specific product IDs from the coupon.

applicableCategories?array<string>

Restrict redemption to specific category IDs.

excludedCategories?array<string>

Exclude specific category IDs from the coupon.

combinesWithOther?boolean

When false (default), this coupon cannot stack with other discount sources (other coupons, automatic discount rules). When true, it stacks.

platforms?array<string>

Sales channels to publish the coupon to.

channels?object

Per-platform field overrides (keyed by platform code). Useful when the platform uses a different code or wants a different title.

Empty Object

curl -X POST "https://api.brainerce.com/api/v1/coupons" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{    "code": "SUMMER25",    "type": "PERCENTAGE",    "value": 25  }'
{
  "id": "string",
  "code": "SUMMER20",
  "title": "string",
  "description": "string",
  "type": "string",
  "value": "string",
  "startsAt": "2019-08-24T14:15:22Z",
  "endsAt": "2019-08-24T14:15:22Z",
  "status": "string",
  "usageLimit": 0,
  "usageLimitPerCustomer": 0,
  "usageCount": 0,
  "minimumOrderAmount": "string",
  "maximumDiscount": "string",
  "conditions": {},
  "applicableProducts": [
    {
      "id": "clx1a2b3c4d5e6f7g8h9",
      "name": "Summer Collection"
    }
  ],
  "excludedProducts": [
    {
      "id": "clx1a2b3c4d5e6f7g8h9",
      "name": "Summer Collection"
    }
  ],
  "applicableCategories": [
    {
      "id": "clx1a2b3c4d5e6f7g8h9",
      "name": "Summer Collection"
    }
  ],
  "excludedCategories": [
    {
      "id": "clx1a2b3c4d5e6f7g8h9",
      "name": "Summer Collection"
    }
  ],
  "regionIds": [
    "string"
  ],
  "combinesWithOther": true,
  "needsSync": true,
  "lastSyncedAt": "2019-08-24T14:15:22Z",
  "channels": {},
  "channelPublishes": [
    {
      "salesChannel": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      },
      "connection": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      }
    }
  ],
  "vibeCodedPublishes": [
    {
      "salesChannel": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      },
      "connection": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      }
    }
  ],
  "createdAt": "2019-08-24T14:15:22Z",
  "updatedAt": "2019-08-24T14:15:22Z",
  "validationWarnings": [
    {
      "platform": "SHOPIFY",
      "warnings": [
        "SHOPIFY does not support product exclusions - will be ignored on sync"
      ]
    }
  ]
}

{
  "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/coupons"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/coupons"
}

{
  "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/coupons"
}

Get platform capabilities for coupon features

GET
/v1/coupons/platform-capabilities
AuthorizationBearer <token>

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/coupons/platform-capabilities"
Empty

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/coupons/platform-capabilities"
}

{
  "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/coupons/platform-capabilities"
}

Get a single coupon by ID

GET
/v1/coupons/{id}
AuthorizationBearer <token>

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

idstring

Coupon ID

curl -X GET "https://api.brainerce.com/api/v1/coupons/string"
{
  "id": "string",
  "code": "SUMMER20",
  "title": "string",
  "description": "string",
  "type": "string",
  "value": "string",
  "startsAt": "2019-08-24T14:15:22Z",
  "endsAt": "2019-08-24T14:15:22Z",
  "status": "string",
  "usageLimit": 0,
  "usageLimitPerCustomer": 0,
  "usageCount": 0,
  "minimumOrderAmount": "string",
  "maximumDiscount": "string",
  "conditions": {},
  "applicableProducts": [
    {
      "id": "clx1a2b3c4d5e6f7g8h9",
      "name": "Summer Collection"
    }
  ],
  "excludedProducts": [
    {
      "id": "clx1a2b3c4d5e6f7g8h9",
      "name": "Summer Collection"
    }
  ],
  "applicableCategories": [
    {
      "id": "clx1a2b3c4d5e6f7g8h9",
      "name": "Summer Collection"
    }
  ],
  "excludedCategories": [
    {
      "id": "clx1a2b3c4d5e6f7g8h9",
      "name": "Summer Collection"
    }
  ],
  "regionIds": [
    "string"
  ],
  "combinesWithOther": true,
  "needsSync": true,
  "lastSyncedAt": "2019-08-24T14:15:22Z",
  "channels": {},
  "channelPublishes": [
    {
      "salesChannel": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      },
      "connection": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      }
    }
  ],
  "vibeCodedPublishes": [
    {
      "salesChannel": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      },
      "connection": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      }
    }
  ],
  "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/coupons/{id}"
}

{
  "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/coupons/{id}"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/coupons/{id}"
}

Update a coupon

Supports Idempotency-Key header for safe retries.

PATCH
/v1/coupons/{id}
AuthorizationBearer <token>

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

idstring

Coupon ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255

Empty Object

curl -X PATCH "https://api.brainerce.com/api/v1/coupons/string" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{}'
{
  "id": "string",
  "code": "SUMMER20",
  "title": "string",
  "description": "string",
  "type": "string",
  "value": "string",
  "startsAt": "2019-08-24T14:15:22Z",
  "endsAt": "2019-08-24T14:15:22Z",
  "status": "string",
  "usageLimit": 0,
  "usageLimitPerCustomer": 0,
  "usageCount": 0,
  "minimumOrderAmount": "string",
  "maximumDiscount": "string",
  "conditions": {},
  "applicableProducts": [
    {
      "id": "clx1a2b3c4d5e6f7g8h9",
      "name": "Summer Collection"
    }
  ],
  "excludedProducts": [
    {
      "id": "clx1a2b3c4d5e6f7g8h9",
      "name": "Summer Collection"
    }
  ],
  "applicableCategories": [
    {
      "id": "clx1a2b3c4d5e6f7g8h9",
      "name": "Summer Collection"
    }
  ],
  "excludedCategories": [
    {
      "id": "clx1a2b3c4d5e6f7g8h9",
      "name": "Summer Collection"
    }
  ],
  "regionIds": [
    "string"
  ],
  "combinesWithOther": true,
  "needsSync": true,
  "lastSyncedAt": "2019-08-24T14:15:22Z",
  "channels": {},
  "channelPublishes": [
    {
      "salesChannel": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      },
      "connection": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      }
    }
  ],
  "vibeCodedPublishes": [
    {
      "salesChannel": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      },
      "connection": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      }
    }
  ],
  "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/coupons/{id}"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/coupons/{id}"
}

{
  "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/coupons/{id}"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/coupons/{id}"
}

Delete a coupon

Supports Idempotency-Key header for safe retries.

DELETE
/v1/coupons/{id}
AuthorizationBearer <token>

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

idstring

Coupon ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
platforms?array<string>

Optional platforms to remove the coupon from

curl -X DELETE "https://api.brainerce.com/api/v1/coupons/string" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{}'
{
  "success": true,
  "jobIds": [
    "string"
  ],
  "warnings": [
    "string"
  ]
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/coupons/{id}"
}

{
  "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/coupons/{id}"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/coupons/{id}"
}

Sync a coupon to all connected platforms

Supports Idempotency-Key header for safe retries.

POST
/v1/coupons/{id}/sync
AuthorizationBearer <token>

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

idstring

Coupon ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
curl -X POST "https://api.brainerce.com/api/v1/coupons/string/sync" \  -H "Idempotency-Key: string"
{
  "success": true,
  "message": "string",
  "jobIds": [
    "string"
  ]
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/coupons/{id}/sync"
}

{
  "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/coupons/{id}/sync"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/coupons/{id}/sync"
}

Publish a coupon to specific platforms

Supports Idempotency-Key header for safe retries.

POST
/v1/coupons/{id}/publish
AuthorizationBearer <token>

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

idstring

Coupon ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
platformsarray<string>

Platforms to publish the coupon to

curl -X POST "https://api.brainerce.com/api/v1/coupons/string/publish" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{    "platforms": [      "string"    ]  }'
{
  "message": "string",
  "jobIds": [
    "string"
  ]
}

{
  "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/coupons/{id}/publish"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/coupons/{id}/publish"
}

{
  "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/coupons/{id}/publish"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/coupons/{id}/publish"
}

List categories with pagination

GET
/v1/categories
AuthorizationBearer <token>

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

Query Parameters

platform?string
search?string
limit?number
page?number
curl -X GET "https://api.brainerce.com/api/v1/categories?platform=string&search=string&limit=0&page=0"
{
  "data": [
    {
      "id": "string",
      "accountId": "string",
      "storeId": "string",
      "name": "string",
      "platformIds": {
        "property1": "string",
        "property2": "string"
      },
      "publishedOn": [
        "string"
      ],
      "platformMetadata": {},
      "parentId": "string",
      "slug": "string",
      "description": "string",
      "metaDescription": "string",
      "image": "string",
      "imageKey": "string",
      "isActive": true,
      "taxBehavior": "string",
      "translations": {},
      "deletedAt": "2019-08-24T14:15:22Z",
      "productCount": 0,
      "products": [
        {
          "id": "string",
          "name": "string",
          "sku": "string"
        }
      ],
      "channelPublishes": [
        {
          "salesChannel": {
            "id": "clx1a2b3c4d5e6f7g8h9",
            "name": "My storefront",
            "connectionId": "vc_9f3a2b1c8d7e6f5a"
          },
          "connection": {
            "id": "clx1a2b3c4d5e6f7g8h9",
            "name": "My storefront",
            "connectionId": "vc_9f3a2b1c8d7e6f5a"
          }
        }
      ],
      "vibeCodedPublishes": [
        {
          "salesChannel": {
            "id": "clx1a2b3c4d5e6f7g8h9",
            "name": "My storefront",
            "connectionId": "vc_9f3a2b1c8d7e6f5a"
          },
          "connection": {
            "id": "clx1a2b3c4d5e6f7g8h9",
            "name": "My storefront",
            "connectionId": "vc_9f3a2b1c8d7e6f5a"
          }
        }
      ],
      "createdAt": "2019-08-24T14:15:22Z",
      "updatedAt": "2019-08-24T14:15:22Z"
    }
  ],
  "meta": {
    "page": 1,
    "limit": 20,
    "total": 137,
    "totalPages": 7
  }
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/categories"
}

{
  "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/categories"
}

Create a new category

Supports Idempotency-Key header for safe retries.

POST
/v1/categories
AuthorizationBearer <token>

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

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
namestring

Category display name.

storeId?string

Store ID. See CreateBrandDto.storeId notes; resolved from auth when omitted.

parentId?string

Parent category ID. Omit for a root category. Categories form a tree, and depth-2 is the practical limit before the storefront filter UI degrades.

isActive?boolean

Active state. Inactive categories stay attached to products but are hidden from storefront filters.

source?string

Origin platform. Defaults to INTERNAL. Connector-sourced categories are managed by the connector and auto-deactivate on disconnect.

Value in"INTERNAL" | "SHOPIFY" | "WOOCOMMERCE" | "TIKTOK" | "META"
image?string

Category banner image URL. Upload via /v1/media first.

imageKey?string

R2/S3 object key for image above. When present, asset-deletion cascade can null this category's image automatically.

taxBehavior?string

Tax inheritance. taxable (default) inherits the product's tax category; exempt overrides every product in this category to be tax-exempt regardless of the product's own setting.

Value in"taxable" | "exempt"
googleTaxonomyId?number

Merchant override for Google's numeric product taxonomy id (Merchant Center google_product_category). Omit to let the name-based auto-resolver assign one.

description?string

Long-form category copy (HTML) rendered on the storefront category landing page.

metaDescription?string

for the category page. 50-160 characters recommended.

curl -X POST "https://api.brainerce.com/api/v1/categories" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{    "name": "Headphones"  }'
{
  "id": "string",
  "accountId": "string",
  "storeId": "string",
  "name": "string",
  "platformIds": {
    "property1": "string",
    "property2": "string"
  },
  "publishedOn": [
    "string"
  ],
  "platformMetadata": {},
  "parentId": "string",
  "slug": "string",
  "description": "string",
  "metaDescription": "string",
  "image": "string",
  "imageKey": "string",
  "isActive": true,
  "taxBehavior": "string",
  "translations": {},
  "deletedAt": "2019-08-24T14:15:22Z",
  "productCount": 0,
  "products": [
    {
      "id": "string",
      "name": "string",
      "sku": "string"
    }
  ],
  "channelPublishes": [
    {
      "salesChannel": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      },
      "connection": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      }
    }
  ],
  "vibeCodedPublishes": [
    {
      "salesChannel": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      },
      "connection": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      }
    }
  ],
  "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/categories"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/categories"
}

{
  "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/categories"
}

Get a single category by ID

GET
/v1/categories/{id}
AuthorizationBearer <token>

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

idstring

Category ID

curl -X GET "https://api.brainerce.com/api/v1/categories/string"
{
  "id": "string",
  "accountId": "string",
  "storeId": "string",
  "name": "string",
  "platformIds": {
    "property1": "string",
    "property2": "string"
  },
  "publishedOn": [
    "string"
  ],
  "platformMetadata": {},
  "parentId": "string",
  "slug": "string",
  "description": "string",
  "metaDescription": "string",
  "image": "string",
  "imageKey": "string",
  "isActive": true,
  "taxBehavior": "string",
  "translations": {},
  "deletedAt": "2019-08-24T14:15:22Z",
  "productCount": 0,
  "products": [
    {
      "id": "string",
      "name": "string",
      "sku": "string"
    }
  ],
  "channelPublishes": [
    {
      "salesChannel": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      },
      "connection": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      }
    }
  ],
  "vibeCodedPublishes": [
    {
      "salesChannel": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      },
      "connection": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      }
    }
  ],
  "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/categories/{id}"
}

{
  "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/categories/{id}"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/categories/{id}"
}

Update a category

Supports Idempotency-Key header for safe retries.

PATCH
/v1/categories/{id}
AuthorizationBearer <token>

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

idstring

Category ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255

Empty Object

curl -X PATCH "https://api.brainerce.com/api/v1/categories/string" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{}'
{
  "id": "string",
  "accountId": "string",
  "storeId": "string",
  "name": "string",
  "platformIds": {
    "property1": "string",
    "property2": "string"
  },
  "publishedOn": [
    "string"
  ],
  "platformMetadata": {},
  "parentId": "string",
  "slug": "string",
  "description": "string",
  "metaDescription": "string",
  "image": "string",
  "imageKey": "string",
  "isActive": true,
  "taxBehavior": "string",
  "translations": {},
  "deletedAt": "2019-08-24T14:15:22Z",
  "productCount": 0,
  "products": [
    {
      "id": "string",
      "name": "string",
      "sku": "string"
    }
  ],
  "channelPublishes": [
    {
      "salesChannel": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      },
      "connection": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      }
    }
  ],
  "vibeCodedPublishes": [
    {
      "salesChannel": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      },
      "connection": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      }
    }
  ],
  "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/categories/{id}"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/categories/{id}"
}

{
  "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/categories/{id}"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/categories/{id}"
}

Delete a category

Supports Idempotency-Key header for safe retries.

DELETE
/v1/categories/{id}
AuthorizationBearer <token>

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

idstring

Category ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
curl -X DELETE "https://api.brainerce.com/api/v1/categories/string" \  -H "Idempotency-Key: string"
Empty

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/categories/{id}"
}

{
  "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/categories/{id}"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/categories/{id}"
}

Publish a category to a sales-channel site

Supports Idempotency-Key header for safe retries.

POST
/v1/categories/{id}/publish-sales-channel
AuthorizationBearer <token>

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

idstring

Category ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
salesChannelIdstring

Sales channel ID

curl -X POST "https://api.brainerce.com/api/v1/categories/string/publish-sales-channel" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{    "salesChannelId": "string"  }'
{
  "success": true
}

{
  "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/categories/{id}/publish-sales-channel"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/categories/{id}/publish-sales-channel"
}

{
  "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/categories/{id}/publish-sales-channel"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/categories/{id}/publish-sales-channel"
}

Unpublish a category from a sales-channel site

Supports Idempotency-Key header for safe retries.

POST
/v1/categories/{id}/unpublish-sales-channel
AuthorizationBearer <token>

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

idstring

Category ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
salesChannelIdstring

Sales channel ID

curl -X POST "https://api.brainerce.com/api/v1/categories/string/unpublish-sales-channel" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{    "salesChannelId": "string"  }'
{
  "success": true
}

{
  "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/categories/{id}/unpublish-sales-channel"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/categories/{id}/unpublish-sales-channel"
}

{
  "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/categories/{id}/unpublish-sales-channel"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/categories/{id}/unpublish-sales-channel"
}

List brands with pagination

GET
/v1/brands
AuthorizationBearer <token>

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

Query Parameters

platform?string
search?string
limit?number
page?number
curl -X GET "https://api.brainerce.com/api/v1/brands?platform=string&search=string&limit=0&page=0"
{
  "data": [
    {
      "id": "string",
      "accountId": "string",
      "storeId": "string",
      "name": "string",
      "slug": "string",
      "description": "string",
      "logo": "string",
      "platformIds": {
        "property1": "string",
        "property2": "string"
      },
      "publishedOn": [
        "string"
      ],
      "platformMetadata": {},
      "source": "string",
      "syncStatus": {
        "property1": "string",
        "property2": "string"
      },
      "lastSyncedAt": {
        "property1": "string",
        "property2": "string"
      },
      "translations": {},
      "isActive": true,
      "deletedAt": "2019-08-24T14:15:22Z",
      "productCount": 0,
      "products": [
        {
          "id": "string",
          "name": "string",
          "sku": "string"
        }
      ],
      "channelPublishes": [
        {
          "salesChannel": {
            "id": "clx1a2b3c4d5e6f7g8h9",
            "name": "My storefront",
            "connectionId": "vc_9f3a2b1c8d7e6f5a"
          },
          "connection": {
            "id": "clx1a2b3c4d5e6f7g8h9",
            "name": "My storefront",
            "connectionId": "vc_9f3a2b1c8d7e6f5a"
          }
        }
      ],
      "vibeCodedPublishes": [
        {
          "salesChannel": {
            "id": "clx1a2b3c4d5e6f7g8h9",
            "name": "My storefront",
            "connectionId": "vc_9f3a2b1c8d7e6f5a"
          },
          "connection": {
            "id": "clx1a2b3c4d5e6f7g8h9",
            "name": "My storefront",
            "connectionId": "vc_9f3a2b1c8d7e6f5a"
          }
        }
      ],
      "createdAt": "2019-08-24T14:15:22Z",
      "updatedAt": "2019-08-24T14:15:22Z"
    }
  ],
  "meta": {
    "page": 1,
    "limit": 20,
    "total": 137,
    "totalPages": 7
  }
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/brands"
}

{
  "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/brands"
}

Create a new brand

Supports Idempotency-Key header for safe retries.

POST
/v1/brands
AuthorizationBearer <token>

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

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
namestring

Brand display name. Unique per store.

storeId?string

Store ID, required when calling via admin API key (path param does not exist on POST /v1/brands). Internal callers omit this and the storeId is resolved from auth context.

slug?string

URL slug. Auto-generated from name when omitted.

description?string

Long-form brand description (plain text).

logo?string

Brand logo URL. Upload via /v1/media first to get a hosted URL.

logoKey?string

R2/S3 object key for logo above. When present, asset-deletion cascade can null this brand's logo automatically. Merchants pasting external CDN URLs omit this; the cascade then treats the brand as orphan.

isActive?boolean

Active state. Inactive brands stay attached to existing products but are hidden from filter UIs.

translations?object

Multi-language overrides keyed by ISO locale code. Each value carries translated name / description.

Empty Object

curl -X POST "https://api.brainerce.com/api/v1/brands" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{    "name": "Acme Audio"  }'
{
  "id": "string",
  "accountId": "string",
  "storeId": "string",
  "name": "string",
  "slug": "string",
  "description": "string",
  "logo": "string",
  "platformIds": {
    "property1": "string",
    "property2": "string"
  },
  "publishedOn": [
    "string"
  ],
  "platformMetadata": {},
  "source": "string",
  "syncStatus": {
    "property1": "string",
    "property2": "string"
  },
  "lastSyncedAt": {
    "property1": "string",
    "property2": "string"
  },
  "translations": {},
  "isActive": true,
  "deletedAt": "2019-08-24T14:15:22Z",
  "productCount": 0,
  "products": [
    {
      "id": "string",
      "name": "string",
      "sku": "string"
    }
  ],
  "channelPublishes": [
    {
      "salesChannel": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      },
      "connection": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      }
    }
  ],
  "vibeCodedPublishes": [
    {
      "salesChannel": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      },
      "connection": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      }
    }
  ],
  "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/brands"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/brands"
}

{
  "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/brands"
}

Get a single brand by ID

GET
/v1/brands/{id}
AuthorizationBearer <token>

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

idstring

Brand ID

curl -X GET "https://api.brainerce.com/api/v1/brands/string"
{
  "id": "string",
  "accountId": "string",
  "storeId": "string",
  "name": "string",
  "slug": "string",
  "description": "string",
  "logo": "string",
  "platformIds": {
    "property1": "string",
    "property2": "string"
  },
  "publishedOn": [
    "string"
  ],
  "platformMetadata": {},
  "source": "string",
  "syncStatus": {
    "property1": "string",
    "property2": "string"
  },
  "lastSyncedAt": {
    "property1": "string",
    "property2": "string"
  },
  "translations": {},
  "isActive": true,
  "deletedAt": "2019-08-24T14:15:22Z",
  "productCount": 0,
  "products": [
    {
      "id": "string",
      "name": "string",
      "sku": "string"
    }
  ],
  "channelPublishes": [
    {
      "salesChannel": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      },
      "connection": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      }
    }
  ],
  "vibeCodedPublishes": [
    {
      "salesChannel": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      },
      "connection": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      }
    }
  ],
  "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/brands/{id}"
}

{
  "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/brands/{id}"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/brands/{id}"
}

Update a brand

Supports Idempotency-Key header for safe retries.

PATCH
/v1/brands/{id}
AuthorizationBearer <token>

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

idstring

Brand ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255

Empty Object

curl -X PATCH "https://api.brainerce.com/api/v1/brands/string" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{}'
{
  "id": "string",
  "accountId": "string",
  "storeId": "string",
  "name": "string",
  "slug": "string",
  "description": "string",
  "logo": "string",
  "platformIds": {
    "property1": "string",
    "property2": "string"
  },
  "publishedOn": [
    "string"
  ],
  "platformMetadata": {},
  "source": "string",
  "syncStatus": {
    "property1": "string",
    "property2": "string"
  },
  "lastSyncedAt": {
    "property1": "string",
    "property2": "string"
  },
  "translations": {},
  "isActive": true,
  "deletedAt": "2019-08-24T14:15:22Z",
  "productCount": 0,
  "products": [
    {
      "id": "string",
      "name": "string",
      "sku": "string"
    }
  ],
  "channelPublishes": [
    {
      "salesChannel": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      },
      "connection": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      }
    }
  ],
  "vibeCodedPublishes": [
    {
      "salesChannel": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      },
      "connection": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      }
    }
  ],
  "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/brands/{id}"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/brands/{id}"
}

{
  "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/brands/{id}"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/brands/{id}"
}

Delete a brand

Supports Idempotency-Key header for safe retries.

DELETE
/v1/brands/{id}
AuthorizationBearer <token>

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

idstring

Brand ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
curl -X DELETE "https://api.brainerce.com/api/v1/brands/string" \  -H "Idempotency-Key: string"
Empty

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/brands/{id}"
}

{
  "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/brands/{id}"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/brands/{id}"
}

Publish a brand to a sales-channel site

Supports Idempotency-Key header for safe retries.

POST
/v1/brands/{id}/publish-sales-channel
AuthorizationBearer <token>

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

idstring

Brand ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
salesChannelIdstring

Sales channel ID

curl -X POST "https://api.brainerce.com/api/v1/brands/string/publish-sales-channel" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{    "salesChannelId": "string"  }'
{
  "success": true
}

{
  "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/brands/{id}/publish-sales-channel"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/brands/{id}/publish-sales-channel"
}

{
  "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/brands/{id}/publish-sales-channel"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/brands/{id}/publish-sales-channel"
}

Unpublish a brand from a sales-channel site

Supports Idempotency-Key header for safe retries.

POST
/v1/brands/{id}/unpublish-sales-channel
AuthorizationBearer <token>

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

idstring

Brand ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
salesChannelIdstring

Sales channel ID

curl -X POST "https://api.brainerce.com/api/v1/brands/string/unpublish-sales-channel" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{    "salesChannelId": "string"  }'
{
  "success": true
}

{
  "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/brands/{id}/unpublish-sales-channel"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/brands/{id}/unpublish-sales-channel"
}

{
  "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/brands/{id}/unpublish-sales-channel"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/brands/{id}/unpublish-sales-channel"
}

List tags with pagination

GET
/v1/tags
AuthorizationBearer <token>

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

Query Parameters

platform?string
search?string
limit?number
page?number
curl -X GET "https://api.brainerce.com/api/v1/tags?platform=string&search=string&limit=0&page=0"
{
  "data": [
    {
      "id": "string",
      "accountId": "string",
      "storeId": "string",
      "name": "string",
      "platformIds": {
        "property1": "string",
        "property2": "string"
      },
      "publishedOn": [
        "string"
      ],
      "translations": {},
      "productCount": 0,
      "products": [
        {
          "id": "string",
          "name": "string",
          "sku": "string"
        }
      ],
      "channelPublishes": [
        {
          "salesChannel": {
            "id": "clx1a2b3c4d5e6f7g8h9",
            "name": "My storefront",
            "connectionId": "vc_9f3a2b1c8d7e6f5a"
          },
          "connection": {
            "id": "clx1a2b3c4d5e6f7g8h9",
            "name": "My storefront",
            "connectionId": "vc_9f3a2b1c8d7e6f5a"
          }
        }
      ],
      "vibeCodedPublishes": [
        {
          "salesChannel": {
            "id": "clx1a2b3c4d5e6f7g8h9",
            "name": "My storefront",
            "connectionId": "vc_9f3a2b1c8d7e6f5a"
          },
          "connection": {
            "id": "clx1a2b3c4d5e6f7g8h9",
            "name": "My storefront",
            "connectionId": "vc_9f3a2b1c8d7e6f5a"
          }
        }
      ],
      "isActive": true,
      "createdAt": "2019-08-24T14:15:22Z",
      "updatedAt": "2019-08-24T14:15:22Z"
    }
  ],
  "meta": {
    "page": 1,
    "limit": 20,
    "total": 137,
    "totalPages": 7
  }
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/tags"
}

{
  "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/tags"
}

Create a new tag

Supports Idempotency-Key header for safe retries.

POST
/v1/tags
AuthorizationBearer <token>

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

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
namestring

Tag name. Case-insensitive, and unique per store.

isActive?boolean

Active state. Inactive tags stay attached to existing products but are hidden from filter UIs.

source?string

Origin platform. Defaults to INTERNAL. Platform-sourced tags (SHOPIFY, WOOCOMMERCE, …) are managed by the connector and auto-deactivate when the source platform disconnects.

Value in"INTERNAL" | "SHOPIFY" | "WOOCOMMERCE" | "TIKTOK" | "META"
curl -X POST "https://api.brainerce.com/api/v1/tags" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{    "name": "summer-2026"  }'
{
  "id": "string",
  "accountId": "string",
  "storeId": "string",
  "name": "string",
  "platformIds": {
    "property1": "string",
    "property2": "string"
  },
  "publishedOn": [
    "string"
  ],
  "translations": {},
  "productCount": 0,
  "products": [
    {
      "id": "string",
      "name": "string",
      "sku": "string"
    }
  ],
  "channelPublishes": [
    {
      "salesChannel": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      },
      "connection": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      }
    }
  ],
  "vibeCodedPublishes": [
    {
      "salesChannel": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      },
      "connection": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      }
    }
  ],
  "isActive": true,
  "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/tags"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/tags"
}

{
  "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/tags"
}

Get a single tag by ID

GET
/v1/tags/{id}
AuthorizationBearer <token>

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

idstring

Tag ID

curl -X GET "https://api.brainerce.com/api/v1/tags/string"
{
  "id": "string",
  "accountId": "string",
  "storeId": "string",
  "name": "string",
  "platformIds": {
    "property1": "string",
    "property2": "string"
  },
  "publishedOn": [
    "string"
  ],
  "translations": {},
  "productCount": 0,
  "products": [
    {
      "id": "string",
      "name": "string",
      "sku": "string"
    }
  ],
  "channelPublishes": [
    {
      "salesChannel": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      },
      "connection": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      }
    }
  ],
  "vibeCodedPublishes": [
    {
      "salesChannel": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      },
      "connection": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      }
    }
  ],
  "isActive": true,
  "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/tags/{id}"
}

{
  "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/tags/{id}"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/tags/{id}"
}

Delete a tag

Supports Idempotency-Key header for safe retries.

DELETE
/v1/tags/{id}
AuthorizationBearer <token>

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

idstring

Tag ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
curl -X DELETE "https://api.brainerce.com/api/v1/tags/string" \  -H "Idempotency-Key: string"
Empty

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/tags/{id}"
}

{
  "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/tags/{id}"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/tags/{id}"
}

Publish a tag to a sales-channel site

Supports Idempotency-Key header for safe retries.

POST
/v1/tags/{id}/publish-sales-channel
AuthorizationBearer <token>

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

idstring

Tag ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
salesChannelIdstring

Sales channel ID

curl -X POST "https://api.brainerce.com/api/v1/tags/string/publish-sales-channel" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{    "salesChannelId": "string"  }'
{
  "success": true
}

{
  "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/tags/{id}/publish-sales-channel"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/tags/{id}/publish-sales-channel"
}

{
  "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/tags/{id}/publish-sales-channel"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/tags/{id}/publish-sales-channel"
}

Unpublish a tag from a sales-channel site

Supports Idempotency-Key header for safe retries.

POST
/v1/tags/{id}/unpublish-sales-channel
AuthorizationBearer <token>

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

idstring

Tag ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
salesChannelIdstring

Sales channel ID

curl -X POST "https://api.brainerce.com/api/v1/tags/string/unpublish-sales-channel" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{    "salesChannelId": "string"  }'
{
  "success": true
}

{
  "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/tags/{id}/unpublish-sales-channel"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/tags/{id}/unpublish-sales-channel"
}

{
  "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/tags/{id}/unpublish-sales-channel"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/tags/{id}/unpublish-sales-channel"
}

List attributes with pagination

GET
/v1/attributes
AuthorizationBearer <token>

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

Query Parameters

platform?string
source?string
search?string
limit?number
page?number
curl -X GET "https://api.brainerce.com/api/v1/attributes?platform=string&source=string&search=string&limit=0&page=0"
{
  "data": [
    {
      "id": "string",
      "accountId": "string",
      "storeId": "string",
      "name": "string",
      "displayType": "string",
      "source": "string",
      "platform": "string",
      "externalId": "string",
      "platformMetadata": {},
      "translations": {},
      "isActive": true,
      "deletedAt": "2019-08-24T14:15:22Z",
      "createdAt": "2019-08-24T14:15:22Z",
      "updatedAt": "2019-08-24T14:15:22Z",
      "productCount": 0,
      "options": [
        {
          "id": "string",
          "attributeId": "string",
          "name": "string",
          "value": "string",
          "swatchColor": "string",
          "swatchColor2": "string",
          "swatchImageUrl": "string",
          "source": "string",
          "platform": "string",
          "externalId": "string",
          "platformMetadata": {},
          "translations": {},
          "position": 0,
          "isActive": true,
          "deletedAt": "2019-08-24T14:15:22Z",
          "createdAt": "2019-08-24T14:15:22Z",
          "updatedAt": "2019-08-24T14:15:22Z"
        }
      ]
    }
  ],
  "meta": {
    "page": 1,
    "limit": 20,
    "total": 137,
    "totalPages": 7
  }
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/attributes"
}

{
  "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/attributes"
}

Create a new attribute

Supports Idempotency-Key header for safe retries.

POST
/v1/attributes
AuthorizationBearer <token>

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

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255

Empty Object

curl -X POST "https://api.brainerce.com/api/v1/attributes" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{}'
{
  "id": "string",
  "accountId": "string",
  "storeId": "string",
  "name": "string",
  "displayType": "string",
  "source": "string",
  "platform": "string",
  "externalId": "string",
  "platformMetadata": {},
  "translations": {},
  "isActive": true,
  "deletedAt": "2019-08-24T14:15:22Z",
  "createdAt": "2019-08-24T14:15:22Z",
  "updatedAt": "2019-08-24T14:15:22Z",
  "productCount": 0,
  "options": [
    {
      "id": "string",
      "attributeId": "string",
      "name": "string",
      "value": "string",
      "swatchColor": "string",
      "swatchColor2": "string",
      "swatchImageUrl": "string",
      "source": "string",
      "platform": "string",
      "externalId": "string",
      "platformMetadata": {},
      "translations": {},
      "position": 0,
      "isActive": true,
      "deletedAt": "2019-08-24T14:15:22Z",
      "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/attributes"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/attributes"
}

{
  "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/attributes"
}

Get a single attribute by ID

GET
/v1/attributes/{id}
AuthorizationBearer <token>

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

idstring

Attribute ID

curl -X GET "https://api.brainerce.com/api/v1/attributes/string"
{
  "id": "string",
  "accountId": "string",
  "storeId": "string",
  "name": "string",
  "displayType": "string",
  "source": "string",
  "platform": "string",
  "externalId": "string",
  "platformMetadata": {},
  "translations": {},
  "isActive": true,
  "deletedAt": "2019-08-24T14:15:22Z",
  "createdAt": "2019-08-24T14:15:22Z",
  "updatedAt": "2019-08-24T14:15:22Z",
  "productCount": 0,
  "options": [
    {
      "id": "string",
      "attributeId": "string",
      "name": "string",
      "value": "string",
      "swatchColor": "string",
      "swatchColor2": "string",
      "swatchImageUrl": "string",
      "source": "string",
      "platform": "string",
      "externalId": "string",
      "platformMetadata": {},
      "translations": {},
      "position": 0,
      "isActive": true,
      "deletedAt": "2019-08-24T14:15:22Z",
      "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/attributes/{id}"
}

{
  "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/attributes/{id}"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/attributes/{id}"
}

Update an attribute

Supports Idempotency-Key header for safe retries.

PATCH
/v1/attributes/{id}
AuthorizationBearer <token>

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

idstring

Attribute ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255

Empty Object

curl -X PATCH "https://api.brainerce.com/api/v1/attributes/string" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{}'
{
  "id": "string",
  "accountId": "string",
  "storeId": "string",
  "name": "string",
  "displayType": "string",
  "source": "string",
  "platform": "string",
  "externalId": "string",
  "platformMetadata": {},
  "translations": {},
  "isActive": true,
  "deletedAt": "2019-08-24T14:15:22Z",
  "createdAt": "2019-08-24T14:15:22Z",
  "updatedAt": "2019-08-24T14:15:22Z",
  "productCount": 0,
  "options": [
    {
      "id": "string",
      "attributeId": "string",
      "name": "string",
      "value": "string",
      "swatchColor": "string",
      "swatchColor2": "string",
      "swatchImageUrl": "string",
      "source": "string",
      "platform": "string",
      "externalId": "string",
      "platformMetadata": {},
      "translations": {},
      "position": 0,
      "isActive": true,
      "deletedAt": "2019-08-24T14:15:22Z",
      "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/attributes/{id}"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/attributes/{id}"
}

{
  "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/attributes/{id}"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/attributes/{id}"
}

Delete an attribute

Supports Idempotency-Key header for safe retries.

DELETE
/v1/attributes/{id}
AuthorizationBearer <token>

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

idstring

Attribute ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
curl -X DELETE "https://api.brainerce.com/api/v1/attributes/string" \  -H "Idempotency-Key: string"
Empty

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/attributes/{id}"
}

{
  "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/attributes/{id}"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/attributes/{id}"
}

Get attribute options

GET
/v1/attributes/{id}/options
AuthorizationBearer <token>

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

idstring

Attribute ID

Query Parameters

platform?string
curl -X GET "https://api.brainerce.com/api/v1/attributes/string/options?platform=string"
[
  {
    "id": "string",
    "attributeId": "string",
    "name": "string",
    "value": "string",
    "swatchColor": "string",
    "swatchColor2": "string",
    "swatchImageUrl": "string",
    "source": "string",
    "platform": "string",
    "externalId": "string",
    "platformMetadata": {},
    "translations": {},
    "position": 0,
    "isActive": true,
    "deletedAt": "2019-08-24T14:15:22Z",
    "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/attributes/{id}/options"
}

{
  "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/attributes/{id}/options"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/attributes/{id}/options"
}

Create an attribute option

Supports Idempotency-Key header for safe retries.

POST
/v1/attributes/{id}/options
AuthorizationBearer <token>

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

idstring

Attribute ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255

Empty Object

curl -X POST "https://api.brainerce.com/api/v1/attributes/string/options" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{}'
{
  "id": "string",
  "attributeId": "string",
  "name": "string",
  "value": "string",
  "swatchColor": "string",
  "swatchColor2": "string",
  "swatchImageUrl": "string",
  "source": "string",
  "platform": "string",
  "externalId": "string",
  "platformMetadata": {},
  "translations": {},
  "position": 0,
  "isActive": true,
  "deletedAt": "2019-08-24T14:15:22Z",
  "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/attributes/{id}/options"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/attributes/{id}/options"
}

{
  "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/attributes/{id}/options"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/attributes/{id}/options"
}

List shipping zones with pagination

GET
/v1/shipping/zones
AuthorizationBearer <token>

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

Query Parameters

page?number
limit?number
search?string
isActive?boolean
curl -X GET "https://api.brainerce.com/api/v1/shipping/zones?page=0&limit=0&search=string&isActive=true"
{
  "data": [
    {
      "id": "string",
      "accountId": "string",
      "storeId": "string",
      "name": "US Domestic",
      "countries": [
        "string"
      ],
      "regions": {},
      "postalCodes": {},
      "geometry": {},
      "hasGeometry": true,
      "geometryLabels": [
        "string"
      ],
      "regionIds": [
        "string"
      ],
      "salesChannelIds": [
        "string"
      ],
      "priority": 0,
      "isActive": true,
      "rateSource": "string",
      "platformSettings": {},
      "createdAt": "2019-08-24T14:15:22Z",
      "updatedAt": "2019-08-24T14:15:22Z",
      "rates": [
        {
          "id": "string",
          "zoneId": "string",
          "name": "Standard Shipping",
          "description": "string",
          "type": "string",
          "rateConfig": {},
          "minDeliveryDays": 0,
          "maxDeliveryDays": 0,
          "handlingTime": 0,
          "taxStatus": "string",
          "minOrderAmount": "string",
          "maxCost": "string",
          "classCosts": {},
          "classCalculationType": "string",
          "isActive": true,
          "platformSettings": {},
          "createdAt": "2019-08-24T14:15:22Z",
          "updatedAt": "2019-08-24T14:15:22Z"
        }
      ],
      "_count": {
        "rates": 0
      },
      "ratesCount": 0
    }
  ],
  "meta": {
    "page": 1,
    "limit": 20,
    "total": 137,
    "totalPages": 7
  }
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/shipping/zones"
}

{
  "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/shipping/zones"
}

Create a new shipping zone

Supports Idempotency-Key header for safe retries.

POST
/v1/shipping/zones
AuthorizationBearer <token>

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

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
namestring

Display name (shown in the dashboard, never to shoppers).

countriesarray<string>

ISO 3166-1 alpha-2 country codes covered by this zone.

regions?object

Per-country region (state/province) restriction. Keyed by ISO country code; value is an array of subdivision codes. Addresses outside the listed regions do not match the zone.

Empty Object

postalCodes?object

Per-country postal-code patterns (exact, prefix 941*, or range 94100-94199). Further narrows the zone after regions.

Empty Object

geometry?object

Polygon zone definition, a hand-drawn shape ("draw on map"). GeoJSON Polygon or MultiPolygon, ring coordinates in [lng, lat] order (RFC 7946). Independent of countries/regions/postalCodes: a zone matches if the address satisfies either the country-list coverage or the polygon coverage, so both may be set on the same zone.

Empty Object

geometryLabels?array<string>

Display-only label per shape in geometry (index 0 = the Polygon, or a MultiPolygon's Nth part), e.g. the searched place name ("Tel Aviv") instead of a generic "Custom area N". Purely cosmetic; never used for matching.

regionIds?array<string>

Region restriction (PRD §25). Region IDs this zone is limited to. Empty/omitted = available for any region (default). Non-empty = the zone is only offered to checkouts whose region is in the list.

salesChannelIds?array<string>

Sales-channel restriction. SalesChannel.id values (the public vc_* connectionId also accepted). Empty/omitted = available on every channel (default). Non-empty = the zone is only offered to checkouts from a channel in the list.

priority?number

Match priority: a lower value is evaluated first. Defaults to 0.

Default0
Range0 <= value
isActive?boolean

Whether this zone participates in rate calculation. Defaults to true.

Defaulttrue
curl -X POST "https://api.brainerce.com/api/v1/shipping/zones" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{    "name": "United States",    "countries": [      "US",      "CA",      "MX"    ]  }'
{
  "id": "string",
  "accountId": "string",
  "storeId": "string",
  "name": "US Domestic",
  "countries": [
    "string"
  ],
  "regions": {},
  "postalCodes": {},
  "geometry": {},
  "hasGeometry": true,
  "geometryLabels": [
    "string"
  ],
  "regionIds": [
    "string"
  ],
  "salesChannelIds": [
    "string"
  ],
  "priority": 0,
  "isActive": true,
  "rateSource": "string",
  "platformSettings": {},
  "createdAt": "2019-08-24T14:15:22Z",
  "updatedAt": "2019-08-24T14:15:22Z",
  "rates": [
    {
      "id": "string",
      "zoneId": "string",
      "name": "Standard Shipping",
      "description": "string",
      "type": "string",
      "rateConfig": {},
      "minDeliveryDays": 0,
      "maxDeliveryDays": 0,
      "handlingTime": 0,
      "taxStatus": "string",
      "minOrderAmount": "string",
      "maxCost": "string",
      "classCosts": {},
      "classCalculationType": "string",
      "isActive": true,
      "platformSettings": {},
      "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/shipping/zones"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/shipping/zones"
}

{
  "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/shipping/zones"
}

Get a single shipping zone by ID

GET
/v1/shipping/zones/{zoneId}
AuthorizationBearer <token>

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

zoneIdstring

Shipping Zone ID

curl -X GET "https://api.brainerce.com/api/v1/shipping/zones/string"
{
  "id": "string",
  "accountId": "string",
  "storeId": "string",
  "name": "US Domestic",
  "countries": [
    "string"
  ],
  "regions": {},
  "postalCodes": {},
  "geometry": {},
  "hasGeometry": true,
  "geometryLabels": [
    "string"
  ],
  "regionIds": [
    "string"
  ],
  "salesChannelIds": [
    "string"
  ],
  "priority": 0,
  "isActive": true,
  "rateSource": "string",
  "platformSettings": {},
  "createdAt": "2019-08-24T14:15:22Z",
  "updatedAt": "2019-08-24T14:15:22Z",
  "rates": [
    {
      "id": "string",
      "zoneId": "string",
      "name": "Standard Shipping",
      "description": "string",
      "type": "string",
      "rateConfig": {},
      "minDeliveryDays": 0,
      "maxDeliveryDays": 0,
      "handlingTime": 0,
      "taxStatus": "string",
      "minOrderAmount": "string",
      "maxCost": "string",
      "classCosts": {},
      "classCalculationType": "string",
      "isActive": true,
      "platformSettings": {},
      "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/shipping/zones/{zoneId}"
}

{
  "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/shipping/zones/{zoneId}"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/shipping/zones/{zoneId}"
}

Update a shipping zone

Supports Idempotency-Key header for safe retries.

PATCH
/v1/shipping/zones/{zoneId}
AuthorizationBearer <token>

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

zoneIdstring

Shipping Zone ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
name?string

Display name.

countries?array<string>

ISO 3166-1 alpha-2 country codes covered by this zone.

regions?object

Per-country region restriction (subdivision codes).

Empty Object

postalCodes?object

Per-country postal-code patterns.

Empty Object

geometry?object

Polygon zone definition ("draw on map"). GeoJSON Polygon or MultiPolygon, [lng, lat] ring coordinates. Pass null to clear a previously-set polygon. Omit to leave unchanged.

Empty Object

geometryLabels?array<string>

Display-only label per shape in geometry (index 0 = the Polygon, or a MultiPolygon's Nth part). Purely cosmetic; never used for matching. Omit to leave unchanged.

regionIds?array<string>

Region restriction (PRD §25). Region IDs this zone is limited to. Empty array = available for any region. Omit to leave unchanged.

salesChannelIds?array<string>

Sales-channel restriction. SalesChannel.id values (the public vc_* connectionId also accepted). Empty array = available on every channel. Omit to leave unchanged.

priority?number

Match priority: a lower value is evaluated first.

Range0 <= value
isActive?boolean

Whether this zone participates in rate calculation.

platformSettings?object

Per-platform overrides (e.g. Shopify zone ID mapping). Keyed by platform code; value is platform-specific.

Empty Object

curl -X PATCH "https://api.brainerce.com/api/v1/shipping/zones/string" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{}'
{
  "id": "string",
  "accountId": "string",
  "storeId": "string",
  "name": "US Domestic",
  "countries": [
    "string"
  ],
  "regions": {},
  "postalCodes": {},
  "geometry": {},
  "hasGeometry": true,
  "geometryLabels": [
    "string"
  ],
  "regionIds": [
    "string"
  ],
  "salesChannelIds": [
    "string"
  ],
  "priority": 0,
  "isActive": true,
  "rateSource": "string",
  "platformSettings": {},
  "createdAt": "2019-08-24T14:15:22Z",
  "updatedAt": "2019-08-24T14:15:22Z",
  "rates": [
    {
      "id": "string",
      "zoneId": "string",
      "name": "Standard Shipping",
      "description": "string",
      "type": "string",
      "rateConfig": {},
      "minDeliveryDays": 0,
      "maxDeliveryDays": 0,
      "handlingTime": 0,
      "taxStatus": "string",
      "minOrderAmount": "string",
      "maxCost": "string",
      "classCosts": {},
      "classCalculationType": "string",
      "isActive": true,
      "platformSettings": {},
      "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/shipping/zones/{zoneId}"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/shipping/zones/{zoneId}"
}

{
  "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/shipping/zones/{zoneId}"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/shipping/zones/{zoneId}"
}

Delete a shipping zone

Supports Idempotency-Key header for safe retries.

DELETE
/v1/shipping/zones/{zoneId}
AuthorizationBearer <token>

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

zoneIdstring

Shipping Zone ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
curl -X DELETE "https://api.brainerce.com/api/v1/shipping/zones/string" \  -H "Idempotency-Key: string"
Empty

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/shipping/zones/{zoneId}"
}

{
  "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/shipping/zones/{zoneId}"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/shipping/zones/{zoneId}"
}

Get shipping rates for a zone

GET
/v1/shipping/zones/{zoneId}/rates
AuthorizationBearer <token>

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

zoneIdstring

Shipping Zone ID

curl -X GET "https://api.brainerce.com/api/v1/shipping/zones/string/rates"
[
  {
    "id": "string",
    "zoneId": "string",
    "name": "Standard Shipping",
    "description": "string",
    "type": "string",
    "rateConfig": {},
    "minDeliveryDays": 0,
    "maxDeliveryDays": 0,
    "handlingTime": 0,
    "taxStatus": "string",
    "minOrderAmount": "string",
    "maxCost": "string",
    "classCosts": {},
    "classCalculationType": "string",
    "isActive": true,
    "platformSettings": {},
    "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/shipping/zones/{zoneId}/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/shipping/zones/{zoneId}/rates"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/shipping/zones/{zoneId}/rates"
}

Create a shipping rate for a zone

Supports Idempotency-Key header for safe retries.

POST
/v1/shipping/zones/{zoneId}/rates
AuthorizationBearer <token>

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

zoneIdstring

Shipping Zone ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
namestring

Display name shown to shoppers at checkout (e.g. "Standard", "Express").

description?string

Optional short description shown alongside the rate at checkout.

typestring

Rate calculation strategy.

Value in"FLAT_RATE" | "WEIGHT_BASED" | "PRICE_BASED" | "FREE" | "LOCAL_PICKUP"
rateConfigobject

Type-specific configuration. Shape depends on type; see the DTO docs. For FLAT_RATE use { amount: number }.

Empty Object

minDeliveryDays?number

Minimum delivery time in business days (inclusive).

Range0 <= value
maxDeliveryDays?number

Maximum delivery time in business days (inclusive).

Range0 <= value
handlingTime?number

Internal handling time in business days before the package ships.

Range1 <= value
taxStatus?string

Whether the delivery charge itself is taxed. TAXABLE (the default when omitted) taxes it at the Standard rate; NONE leaves postage untaxed.

Default"TAXABLE"
Value in"TAXABLE" | "NONE"
minOrderAmount?number

Minimum cart subtotal required for this rate to be offered. Use to hide low-value carts from free shipping.

Range0 <= value
maxCost?number

Optional cap on the computed cost. Useful for WEIGHT_BASED rates to prevent runaway charges.

Range0 <= value
isActive?boolean

Whether this rate is offered. Defaults to true.

Defaulttrue
classCosts?object

Per-shipping-class cost overrides. Keyed by ShippingClass.id; value is either a flat amount or { first, additional } for first-item + each-additional pricing.

Empty Object

classCalculationType?string

How classCosts are aggregated when the cart mixes classes. per_class charges once per distinct class; per_order once for the whole order; per_item per line-item.

Value in"per_class" | "per_order" | "per_item"
curl -X POST "https://api.brainerce.com/api/v1/shipping/zones/string/rates" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{    "name": "Standard Shipping",    "type": "FLAT_RATE",    "rateConfig": {      "amount": 9.99    }  }'
{
  "id": "string",
  "zoneId": "string",
  "name": "Standard Shipping",
  "description": "string",
  "type": "string",
  "rateConfig": {},
  "minDeliveryDays": 0,
  "maxDeliveryDays": 0,
  "handlingTime": 0,
  "taxStatus": "string",
  "minOrderAmount": "string",
  "maxCost": "string",
  "classCosts": {},
  "classCalculationType": "string",
  "isActive": true,
  "platformSettings": {},
  "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/shipping/zones/{zoneId}/rates"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/shipping/zones/{zoneId}/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/shipping/zones/{zoneId}/rates"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/shipping/zones/{zoneId}/rates"
}

Update a shipping rate

Supports Idempotency-Key header for safe retries.

PATCH
/v1/shipping/zones/{zoneId}/rates/{rateId}
AuthorizationBearer <token>

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

zoneIdstring

Shipping Zone ID

rateIdstring

Shipping Rate ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
name?string

Display name shown to shoppers.

description?string

Short description shown alongside the rate.

type?string

Rate calculation strategy.

Value in"FLAT_RATE" | "WEIGHT_BASED" | "PRICE_BASED" | "FREE" | "LOCAL_PICKUP"
rateConfig?object

Type-specific configuration; shape depends on type.

Empty Object

minDeliveryDays?number

Minimum delivery time in business days.

Range0 <= value
maxDeliveryDays?number

Maximum delivery time in business days.

Range0 <= value
handlingTime?number

Internal handling time in business days.

Range1 <= value
taxStatus?string

Whether shipping itself is taxable.

Value in"TAXABLE" | "NONE"
minOrderAmount?number

Minimum cart subtotal for this rate to be offered.

Range0 <= value
maxCost?number

Optional cap on the computed cost.

Range0 <= value
isActive?boolean

Whether this rate is offered.

platformSettings?object

Per-platform overrides (e.g. Shopify rate ID mapping). Keyed by platform code.

Empty Object

classCosts?object

Per-shipping-class cost overrides.

Empty Object

classCalculationType?string

How classCosts are aggregated when the cart mixes classes.

Value in"per_class" | "per_order" | "per_item"
curl -X PATCH "https://api.brainerce.com/api/v1/shipping/zones/string/rates/string" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{}'
{
  "id": "string",
  "zoneId": "string",
  "name": "Standard Shipping",
  "description": "string",
  "type": "string",
  "rateConfig": {},
  "minDeliveryDays": 0,
  "maxDeliveryDays": 0,
  "handlingTime": 0,
  "taxStatus": "string",
  "minOrderAmount": "string",
  "maxCost": "string",
  "classCosts": {},
  "classCalculationType": "string",
  "isActive": true,
  "platformSettings": {},
  "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/shipping/zones/{zoneId}/rates/{rateId}"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/shipping/zones/{zoneId}/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/shipping/zones/{zoneId}/rates/{rateId}"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/shipping/zones/{zoneId}/rates/{rateId}"
}

Delete a shipping rate

Supports Idempotency-Key header for safe retries.

DELETE
/v1/shipping/zones/{zoneId}/rates/{rateId}
AuthorizationBearer <token>

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

zoneIdstring

Shipping Zone ID

rateIdstring

Shipping Rate ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
curl -X DELETE "https://api.brainerce.com/api/v1/shipping/zones/string/rates/string" \  -H "Idempotency-Key: string"
Empty

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/shipping/zones/{zoneId}/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/shipping/zones/{zoneId}/rates/{rateId}"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/shipping/zones/{zoneId}/rates/{rateId}"
}

List tax rates for the store

GET
/v1/tax/rates
AuthorizationBearer <token>

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.

POST
/v1/tax/rates
AuthorizationBearer <token>

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

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
namestring

Display name shown on invoices and the storefront tax line.

ratenumber

Tax rate as a percentage (e.g. 8.5 for 8.5%). Range 0 to 100.

Range0 <= value <= 100
countrystring

ISO 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.

region?string

State/province code (ISO 3166-2 subdivision part, e.g. CA for California). Narrows the match within country.

postalCode?string

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.

taxType?string

Tax category label (e.g. sales, vat, gst). Shown on invoices and used for reporting; does not affect calculation.

isInclusive?boolean

When true, prices are tax-inclusive (VAT-style): the rate is backed out of the displayed price instead of added on top.

Defaultfalse
priority?number

Tiebreak 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.

isActive?boolean

Whether this rate participates in calculation. Defaults to true.

Defaulttrue
exceptionCountries?array<string>

Additional 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).

stackable?boolean

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.

Defaultfalse
taxClassId?string

Tax 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

GET
/v1/tax/rates/{rateId}
AuthorizationBearer <token>

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

rateIdstring

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.

PATCH
/v1/tax/rates/{rateId}
AuthorizationBearer <token>

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

rateIdstring

Tax Rate ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
name?string

Display name shown on invoices and the storefront tax line.

rate?number

Tax rate as a percentage (0 to 100).

Range0 <= value <= 100
country?string

ISO 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.

region?string

State/province subdivision code.

postalCode?string

Exact postal code (case-insensitive, spaces and hyphens ignored). Prefixes and ranges are NOT supported.

taxType?string

Tax category label (e.g. sales, vat, gst).

isInclusive?boolean

Treat prices as tax-inclusive (VAT-style).

priority?number

Tiebreak between rates of EQUAL specificity: the LOWER number wins. More specific rates (postal > region > country) win regardless of priority.

isActive?boolean

Whether this rate participates in calculation.

exceptionCountries?array<string>

Additional ISO country codes where this rate also applies.

stackable?boolean

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.

taxClassId?string

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.

DELETE
/v1/tax/rates/{rateId}
AuthorizationBearer <token>

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

rateIdstring

Tax Rate ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
curl -X DELETE "https://api.brainerce.com/api/v1/tax/rates/string" \  -H "Idempotency-Key: string"
Empty

{
  "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}"
}

List available country tax presets

Ready-made rate tables a store can apply in one call instead of hand-entering every province.

GET
/v1/tax/presets
AuthorizationBearer <token>

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/presets"
[
  {
    "key": "CA",
    "country": "string",
    "label": "string",
    "description": "string",
    "rateCount": 0
  }
]

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/tax/presets"
}

{
  "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/presets"
}

Apply a country tax preset

Writes a whole country's rate table in one transaction. CA writes the federal GST 5% plus every province's HST or PST/QST, already flagged so GST and PST/QST stack correctly and HST does not. Refuses with 409 when the store already has rates for that country — delete them first to replace them. Supports Idempotency-Key for safe retries.

POST
/v1/tax/presets/{presetKey}/apply
AuthorizationBearer <token>

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

presetKeystring

Preset key, e.g. CA

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
curl -X POST "https://api.brainerce.com/api/v1/tax/presets/string/apply" \  -H "Idempotency-Key: string"
{
  "preset": "CA",
  "created": 0,
  "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": 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/presets/{presetKey}/apply"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/tax/presets/{presetKey}/apply"
}

{
  "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/presets/{presetKey}/apply"
}

{
  "statusCode": 409,
  "code": "CONFLICT",
  "message": "The request conflicts with the current state of the resource",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/tax/presets/{presetKey}/apply"
}

List the store tax classes

GET
/v1/tax-classes
AuthorizationBearer <token>

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-classes"
{
  "data": [
    {
      "id": "string",
      "accountId": "string",
      "storeId": "string",
      "name": "Reduced rate",
      "slug": "reduced-rate",
      "description": "string",
      "isDefault": true,
      "metadata": {},
      "createdAt": "2019-08-24T14:15:22Z",
      "updatedAt": "2019-08-24T14:15:22Z"
    }
  ]
}

{
  "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-classes"
}

Create a tax class

Supports Idempotency-Key header for safe retries.

POST
/v1/tax-classes
AuthorizationBearer <token>

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

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255

Empty Object

curl -X POST "https://api.brainerce.com/api/v1/tax-classes" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{}'
{
  "id": "string",
  "accountId": "string",
  "storeId": "string",
  "name": "Reduced rate",
  "slug": "reduced-rate",
  "description": "string",
  "isDefault": true,
  "metadata": {},
  "createdAt": "2019-08-24T14:15:22Z",
  "updatedAt": "2019-08-24T14:15:22Z"
}

{
  "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-classes"
}

Get a tax class with its dependent counts

GET
/v1/tax-classes/{id}
AuthorizationBearer <token>

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

idstring

Tax Class ID

curl -X GET "https://api.brainerce.com/api/v1/tax-classes/string"
{
  "id": "string",
  "accountId": "string",
  "storeId": "string",
  "name": "Reduced rate",
  "slug": "reduced-rate",
  "description": "string",
  "isDefault": true,
  "metadata": {},
  "createdAt": "2019-08-24T14:15:22Z",
  "updatedAt": "2019-08-24T14:15:22Z",
  "dependents": {
    "productCount": 0,
    "variantCount": 0,
    "categoryCount": 0,
    "taxRateCount": 0
  }
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/tax-classes/{id}"
}

Update a tax class

PATCH
/v1/tax-classes/{id}
AuthorizationBearer <token>

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

idstring

Tax Class ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255

Empty Object

curl -X PATCH "https://api.brainerce.com/api/v1/tax-classes/string" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{}'
{
  "id": "string",
  "accountId": "string",
  "storeId": "string",
  "name": "Reduced rate",
  "slug": "reduced-rate",
  "description": "string",
  "isDefault": true,
  "metadata": {},
  "createdAt": "2019-08-24T14:15:22Z",
  "updatedAt": "2019-08-24T14:15:22Z"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/tax-classes/{id}"
}

Delete a tax class (409 while dependents exist)

DELETE
/v1/tax-classes/{id}
AuthorizationBearer <token>

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

idstring

Tax Class ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
curl -X DELETE "https://api.brainerce.com/api/v1/tax-classes/string" \  -H "Idempotency-Key: string"
Empty

{
  "statusCode": 409,
  "code": "CONFLICT",
  "message": "The request conflicts with the current state of the resource",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/tax-classes/{id}"
}

Set this tax class as the store default

PATCH
/v1/tax-classes/{id}/set-default
AuthorizationBearer <token>

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

idstring

Tax Class ID

curl -X PATCH "https://api.brainerce.com/api/v1/tax-classes/string/set-default"
{
  "id": "string",
  "accountId": "string",
  "storeId": "string",
  "name": "Reduced rate",
  "slug": "reduced-rate",
  "description": "string",
  "isDefault": true,
  "metadata": {},
  "createdAt": "2019-08-24T14:15:22Z",
  "updatedAt": "2019-08-24T14:15:22Z"
}

Bulk-assign a tax class to products / variants / categories

POST
/v1/tax-classes/{id}/assign
AuthorizationBearer <token>

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

idstring

Tax Class ID

Empty Object

curl -X POST "https://api.brainerce.com/api/v1/tax-classes/string/assign" \  -H "Content-Type: application/json" \  -d '{}'
{
  "updated": 0
}

Merge this tax class into another, moving all FKs, then delete it

POST
/v1/tax-classes/{id}/merge-into/{target}
AuthorizationBearer <token>

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

idstring

Source Tax Class ID

targetstring

Target Tax Class ID

curl -X POST "https://api.brainerce.com/api/v1/tax-classes/string/merge-into/string"
Empty

List the store regions

GET
/v1/regions
AuthorizationBearer <token>

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

Query Parameters

page?string
limit?string
curl -X GET "https://api.brainerce.com/api/v1/regions?page=string&limit=string"
{
  "data": [
    {
      "id": "string",
      "accountId": "string",
      "storeId": "string",
      "name": "Israel",
      "slug": "israel",
      "currency": "string",
      "fxBufferPercent": "string",
      "countries": [
        "string"
      ],
      "provinces": {},
      "taxInclusive": true,
      "automaticTaxes": true,
      "isDefault": true,
      "isActive": true,
      "metadata": {},
      "createdAt": "2019-08-24T14:15:22Z",
      "updatedAt": "2019-08-24T14:15:22Z",
      "paymentProviders": [
        {
          "id": "string",
          "regionId": "string",
          "appInstallationId": "string",
          "isEnabled": true,
          "createdAt": "2019-08-24T14:15:22Z"
        }
      ]
    }
  ],
  "meta": {
    "page": 1,
    "limit": 20,
    "total": 137,
    "totalPages": 7
  }
}

{
  "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/regions"
}

Create a region

Supports Idempotency-Key header for safe retries.

POST
/v1/regions
AuthorizationBearer <token>

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

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255

Empty Object

curl -X POST "https://api.brainerce.com/api/v1/regions" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{}'
{
  "id": "string",
  "accountId": "string",
  "storeId": "string",
  "name": "Israel",
  "slug": "israel",
  "currency": "string",
  "fxBufferPercent": "string",
  "countries": [
    "string"
  ],
  "provinces": {},
  "taxInclusive": true,
  "automaticTaxes": true,
  "isDefault": true,
  "isActive": true,
  "metadata": {},
  "createdAt": "2019-08-24T14:15:22Z",
  "updatedAt": "2019-08-24T14:15:22Z",
  "paymentProviders": [
    {
      "id": "string",
      "regionId": "string",
      "appInstallationId": "string",
      "isEnabled": true,
      "createdAt": "2019-08-24T14:15:22Z"
    }
  ]
}

{
  "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/regions"
}

Get a region

GET
/v1/regions/{regionId}
AuthorizationBearer <token>

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

regionIdstring

Region ID

curl -X GET "https://api.brainerce.com/api/v1/regions/string"
{
  "id": "string",
  "accountId": "string",
  "storeId": "string",
  "name": "Israel",
  "slug": "israel",
  "currency": "string",
  "fxBufferPercent": "string",
  "countries": [
    "string"
  ],
  "provinces": {},
  "taxInclusive": true,
  "automaticTaxes": true,
  "isDefault": true,
  "isActive": true,
  "metadata": {},
  "createdAt": "2019-08-24T14:15:22Z",
  "updatedAt": "2019-08-24T14:15:22Z",
  "paymentProviders": [
    {
      "id": "string",
      "regionId": "string",
      "appInstallationId": "string",
      "isEnabled": true,
      "createdAt": "2019-08-24T14:15:22Z"
    }
  ]
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/regions/{regionId}"
}

Update a region

PATCH
/v1/regions/{regionId}
AuthorizationBearer <token>

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

regionIdstring

Region ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255

Empty Object

curl -X PATCH "https://api.brainerce.com/api/v1/regions/string" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{}'
{
  "id": "string",
  "accountId": "string",
  "storeId": "string",
  "name": "Israel",
  "slug": "israel",
  "currency": "string",
  "fxBufferPercent": "string",
  "countries": [
    "string"
  ],
  "provinces": {},
  "taxInclusive": true,
  "automaticTaxes": true,
  "isDefault": true,
  "isActive": true,
  "metadata": {},
  "createdAt": "2019-08-24T14:15:22Z",
  "updatedAt": "2019-08-24T14:15:22Z",
  "paymentProviders": [
    {
      "id": "string",
      "regionId": "string",
      "appInstallationId": "string",
      "isEnabled": true,
      "createdAt": "2019-08-24T14:15:22Z"
    }
  ]
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/regions/{regionId}"
}

Delete a region

DELETE
/v1/regions/{regionId}
AuthorizationBearer <token>

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

regionIdstring

Region ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
curl -X DELETE "https://api.brainerce.com/api/v1/regions/string" \  -H "Idempotency-Key: string"
Empty

List installed providers compatible with this region

GET
/v1/regions/{regionId}/compatible-providers
AuthorizationBearer <token>

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

regionIdstring

Region ID

curl -X GET "https://api.brainerce.com/api/v1/regions/string/compatible-providers"
[
  {
    "id": "string",
    "appId": "string",
    "app": {
      "id": "string",
      "name": "string",
      "slug": "string"
    }
  }
]

Set this region as the store default

PATCH
/v1/regions/{regionId}/set-default
AuthorizationBearer <token>

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

regionIdstring

Region ID

curl -X PATCH "https://api.brainerce.com/api/v1/regions/string/set-default"
{
  "id": "string",
  "accountId": "string",
  "storeId": "string",
  "name": "Israel",
  "slug": "israel",
  "currency": "string",
  "fxBufferPercent": "string",
  "countries": [
    "string"
  ],
  "provinces": {},
  "taxInclusive": true,
  "automaticTaxes": true,
  "isDefault": true,
  "isActive": true,
  "metadata": {},
  "createdAt": "2019-08-24T14:15:22Z",
  "updatedAt": "2019-08-24T14:15:22Z",
  "paymentProviders": [
    {
      "id": "string",
      "regionId": "string",
      "appInstallationId": "string",
      "isEnabled": true,
      "createdAt": "2019-08-24T14:15:22Z"
    }
  ]
}

Replace the region enabled payment providers

PUT
/v1/regions/{regionId}/payment-providers
AuthorizationBearer <token>

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

regionIdstring

Region ID

Empty Object

curl -X PUT "https://api.brainerce.com/api/v1/regions/string/payment-providers" \  -H "Content-Type: application/json" \  -d '{}'
{
  "id": "string",
  "accountId": "string",
  "storeId": "string",
  "name": "Israel",
  "slug": "israel",
  "currency": "string",
  "fxBufferPercent": "string",
  "countries": [
    "string"
  ],
  "provinces": {},
  "taxInclusive": true,
  "automaticTaxes": true,
  "isDefault": true,
  "isActive": true,
  "metadata": {},
  "createdAt": "2019-08-24T14:15:22Z",
  "updatedAt": "2019-08-24T14:15:22Z",
  "paymentProviders": [
    {
      "id": "string",
      "regionId": "string",
      "appInstallationId": "string",
      "isEnabled": true,
      "createdAt": "2019-08-24T14:15:22Z"
    }
  ]
}

List the region manual price overrides

GET
/v1/regions/{regionId}/prices
AuthorizationBearer <token>

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

regionIdstring

Region ID

Query Parameters

productId?string
page?string
limit?string
curl -X GET "https://api.brainerce.com/api/v1/regions/string/prices?productId=string&page=string&limit=string"
{
  "data": [
    {
      "id": "string",
      "productId": "string",
      "variantId": "string",
      "price": "string",
      "salePrice": "string",
      "updatedAt": "2019-08-24T14:15:22Z"
    }
  ],
  "meta": {
    "page": 1,
    "limit": 20,
    "total": 137,
    "totalPages": 7
  }
}

{
  "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/regions/{regionId}/prices"
}

Bulk upsert/remove the region manual price overrides

Supports Idempotency-Key header for safe retries.

PUT
/v1/regions/{regionId}/prices
AuthorizationBearer <token>

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

regionIdstring

Region ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255

Empty Object

curl -X PUT "https://api.brainerce.com/api/v1/regions/string/prices" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{}'
{
  "upserted": 0,
  "removed": 0
}

{
  "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/regions/{regionId}/prices"
}

Delete one manual price override

DELETE
/v1/regions/{regionId}/prices/{priceId}
AuthorizationBearer <token>

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

regionIdstring

Region ID

priceIdstring

Region price ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
curl -X DELETE "https://api.brainerce.com/api/v1/regions/string/prices/string" \  -H "Idempotency-Key: string"
Empty

Add countries to a region

POST
/v1/regions/{regionId}/countries
AuthorizationBearer <token>

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

regionIdstring

Region ID

Empty Object

curl -X POST "https://api.brainerce.com/api/v1/regions/string/countries" \  -H "Content-Type: application/json" \  -d '{}'
{
  "id": "string",
  "accountId": "string",
  "storeId": "string",
  "name": "Israel",
  "slug": "israel",
  "currency": "string",
  "fxBufferPercent": "string",
  "countries": [
    "string"
  ],
  "provinces": {},
  "taxInclusive": true,
  "automaticTaxes": true,
  "isDefault": true,
  "isActive": true,
  "metadata": {},
  "createdAt": "2019-08-24T14:15:22Z",
  "updatedAt": "2019-08-24T14:15:22Z",
  "paymentProviders": [
    {
      "id": "string",
      "regionId": "string",
      "appInstallationId": "string",
      "isEnabled": true,
      "createdAt": "2019-08-24T14:15:22Z"
    }
  ]
}

Remove a country from a region

DELETE
/v1/regions/{regionId}/countries/{code}
AuthorizationBearer <token>

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

regionIdstring

Region ID

codestring

ISO 3166-1 alpha-2 country code

curl -X DELETE "https://api.brainerce.com/api/v1/regions/string/countries/string"
{
  "id": "string",
  "accountId": "string",
  "storeId": "string",
  "name": "Israel",
  "slug": "israel",
  "currency": "string",
  "fxBufferPercent": "string",
  "countries": [
    "string"
  ],
  "provinces": {},
  "taxInclusive": true,
  "automaticTaxes": true,
  "isDefault": true,
  "isActive": true,
  "metadata": {},
  "createdAt": "2019-08-24T14:15:22Z",
  "updatedAt": "2019-08-24T14:15:22Z",
  "paymentProviders": [
    {
      "id": "string",
      "regionId": "string",
      "appInstallationId": "string",
      "isEnabled": true,
      "createdAt": "2019-08-24T14:15:22Z"
    }
  ]
}

List metafield definitions

GET
/v1/metafield-definitions
AuthorizationBearer <token>

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/metafield-definitions"
[
  {
    "id": "string",
    "accountId": "string",
    "storeId": "string",
    "name": "Warranty Info",
    "key": "warranty_info",
    "description": "string",
    "type": "string",
    "required": true,
    "isCustomerInput": true,
    "appliesToAllProducts": true,
    "filterable": true,
    "minLength": 0,
    "maxLength": 0,
    "minValue": 0,
    "maxValue": 0,
    "dateAvailability": {},
    "enumValues": [
      {
        "label": "string",
        "value": "string",
        "swatchColor": "string",
        "swatchImageUrl": "string"
      }
    ],
    "defaultValue": "string",
    "position": 0,
    "isActive": true,
    "publishedOn": [
      "SHOPIFY"
    ],
    "platformIds": {
      "property1": "string",
      "property2": "string"
    },
    "platformMetadata": {},
    "syncStatus": {
      "property1": "string",
      "property2": "string"
    },
    "lastSyncedAt": {
      "property1": "string",
      "property2": "string"
    },
    "source": "string",
    "products": [
      {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "Summer Collection"
      }
    ],
    "channelPublishes": [
      {
        "salesChannel": {
          "id": "clx1a2b3c4d5e6f7g8h9",
          "name": "My storefront",
          "connectionId": "vc_9f3a2b1c8d7e6f5a"
        },
        "connection": {
          "id": "clx1a2b3c4d5e6f7g8h9",
          "name": "My storefront",
          "connectionId": "vc_9f3a2b1c8d7e6f5a"
        }
      }
    ],
    "vibeCodedPublishes": [
      {
        "salesChannel": {
          "id": "clx1a2b3c4d5e6f7g8h9",
          "name": "My storefront",
          "connectionId": "vc_9f3a2b1c8d7e6f5a"
        },
        "connection": {
          "id": "clx1a2b3c4d5e6f7g8h9",
          "name": "My storefront",
          "connectionId": "vc_9f3a2b1c8d7e6f5a"
        }
      }
    ],
    "_count": {
      "values": 0
    },
    "translations": {},
    "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/metafield-definitions"
}

{
  "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/metafield-definitions"
}

Create a new metafield definition

Supports Idempotency-Key header for safe retries.

POST
/v1/metafield-definitions
AuthorizationBearer <token>

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

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255

Empty Object

curl -X POST "https://api.brainerce.com/api/v1/metafield-definitions" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{}'
{
  "id": "string",
  "accountId": "string",
  "storeId": "string",
  "name": "Warranty Info",
  "key": "warranty_info",
  "description": "string",
  "type": "string",
  "required": true,
  "isCustomerInput": true,
  "appliesToAllProducts": true,
  "filterable": true,
  "minLength": 0,
  "maxLength": 0,
  "minValue": 0,
  "maxValue": 0,
  "dateAvailability": {},
  "enumValues": [
    {
      "label": "string",
      "value": "string",
      "swatchColor": "string",
      "swatchImageUrl": "string"
    }
  ],
  "defaultValue": "string",
  "position": 0,
  "isActive": true,
  "publishedOn": [
    "SHOPIFY"
  ],
  "platformIds": {
    "property1": "string",
    "property2": "string"
  },
  "platformMetadata": {},
  "syncStatus": {
    "property1": "string",
    "property2": "string"
  },
  "lastSyncedAt": {
    "property1": "string",
    "property2": "string"
  },
  "source": "string",
  "products": [
    {
      "id": "clx1a2b3c4d5e6f7g8h9",
      "name": "Summer Collection"
    }
  ],
  "channelPublishes": [
    {
      "salesChannel": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      },
      "connection": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      }
    }
  ],
  "vibeCodedPublishes": [
    {
      "salesChannel": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      },
      "connection": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      }
    }
  ],
  "_count": {
    "values": 0
  },
  "translations": {},
  "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/metafield-definitions"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/metafield-definitions"
}

{
  "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/metafield-definitions"
}

Get a single metafield definition by ID

GET
/v1/metafield-definitions/{id}
AuthorizationBearer <token>

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

idstring

Metafield Definition ID

curl -X GET "https://api.brainerce.com/api/v1/metafield-definitions/string"
{
  "id": "string",
  "accountId": "string",
  "storeId": "string",
  "name": "Warranty Info",
  "key": "warranty_info",
  "description": "string",
  "type": "string",
  "required": true,
  "isCustomerInput": true,
  "appliesToAllProducts": true,
  "filterable": true,
  "minLength": 0,
  "maxLength": 0,
  "minValue": 0,
  "maxValue": 0,
  "dateAvailability": {},
  "enumValues": [
    {
      "label": "string",
      "value": "string",
      "swatchColor": "string",
      "swatchImageUrl": "string"
    }
  ],
  "defaultValue": "string",
  "position": 0,
  "isActive": true,
  "publishedOn": [
    "SHOPIFY"
  ],
  "platformIds": {
    "property1": "string",
    "property2": "string"
  },
  "platformMetadata": {},
  "syncStatus": {
    "property1": "string",
    "property2": "string"
  },
  "lastSyncedAt": {
    "property1": "string",
    "property2": "string"
  },
  "source": "string",
  "products": [
    {
      "id": "clx1a2b3c4d5e6f7g8h9",
      "name": "Summer Collection"
    }
  ],
  "channelPublishes": [
    {
      "salesChannel": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      },
      "connection": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      }
    }
  ],
  "vibeCodedPublishes": [
    {
      "salesChannel": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      },
      "connection": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      }
    }
  ],
  "_count": {
    "values": 0
  },
  "translations": {},
  "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/metafield-definitions/{id}"
}

{
  "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/metafield-definitions/{id}"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/metafield-definitions/{id}"
}

Update a metafield definition

Supports Idempotency-Key header for safe retries.

PATCH
/v1/metafield-definitions/{id}
AuthorizationBearer <token>

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

idstring

Metafield Definition ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255

Empty Object

curl -X PATCH "https://api.brainerce.com/api/v1/metafield-definitions/string" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{}'
{
  "id": "string",
  "accountId": "string",
  "storeId": "string",
  "name": "Warranty Info",
  "key": "warranty_info",
  "description": "string",
  "type": "string",
  "required": true,
  "isCustomerInput": true,
  "appliesToAllProducts": true,
  "filterable": true,
  "minLength": 0,
  "maxLength": 0,
  "minValue": 0,
  "maxValue": 0,
  "dateAvailability": {},
  "enumValues": [
    {
      "label": "string",
      "value": "string",
      "swatchColor": "string",
      "swatchImageUrl": "string"
    }
  ],
  "defaultValue": "string",
  "position": 0,
  "isActive": true,
  "publishedOn": [
    "SHOPIFY"
  ],
  "platformIds": {
    "property1": "string",
    "property2": "string"
  },
  "platformMetadata": {},
  "syncStatus": {
    "property1": "string",
    "property2": "string"
  },
  "lastSyncedAt": {
    "property1": "string",
    "property2": "string"
  },
  "source": "string",
  "products": [
    {
      "id": "clx1a2b3c4d5e6f7g8h9",
      "name": "Summer Collection"
    }
  ],
  "channelPublishes": [
    {
      "salesChannel": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      },
      "connection": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      }
    }
  ],
  "vibeCodedPublishes": [
    {
      "salesChannel": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      },
      "connection": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      }
    }
  ],
  "_count": {
    "values": 0
  },
  "translations": {},
  "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/metafield-definitions/{id}"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/metafield-definitions/{id}"
}

{
  "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/metafield-definitions/{id}"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/metafield-definitions/{id}"
}

Delete a metafield definition

Supports Idempotency-Key header for safe retries.

DELETE
/v1/metafield-definitions/{id}
AuthorizationBearer <token>

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

idstring

Metafield Definition ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
curl -X DELETE "https://api.brainerce.com/api/v1/metafield-definitions/string" \  -H "Idempotency-Key: string"
Empty

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/metafield-definitions/{id}"
}

{
  "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/metafield-definitions/{id}"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/metafield-definitions/{id}"
}

Replace platform publishing configuration

Supports Idempotency-Key header for safe retries.

PUT
/v1/metafield-definitions/{id}/platforms
AuthorizationBearer <token>

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

idstring

Metafield Definition ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255

Empty Object

curl -X PUT "https://api.brainerce.com/api/v1/metafield-definitions/string/platforms" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{}'
{
  "id": "string",
  "accountId": "string",
  "storeId": "string",
  "name": "Warranty Info",
  "key": "warranty_info",
  "description": "string",
  "type": "string",
  "required": true,
  "isCustomerInput": true,
  "appliesToAllProducts": true,
  "filterable": true,
  "minLength": 0,
  "maxLength": 0,
  "minValue": 0,
  "maxValue": 0,
  "dateAvailability": {},
  "enumValues": [
    {
      "label": "string",
      "value": "string",
      "swatchColor": "string",
      "swatchImageUrl": "string"
    }
  ],
  "defaultValue": "string",
  "position": 0,
  "isActive": true,
  "publishedOn": [
    "SHOPIFY"
  ],
  "platformIds": {
    "property1": "string",
    "property2": "string"
  },
  "platformMetadata": {},
  "syncStatus": {
    "property1": "string",
    "property2": "string"
  },
  "lastSyncedAt": {
    "property1": "string",
    "property2": "string"
  },
  "source": "string",
  "products": [
    {
      "id": "clx1a2b3c4d5e6f7g8h9",
      "name": "Summer Collection"
    }
  ],
  "channelPublishes": [
    {
      "salesChannel": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      },
      "connection": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      }
    }
  ],
  "vibeCodedPublishes": [
    {
      "salesChannel": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      },
      "connection": {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "My storefront",
        "connectionId": "vc_9f3a2b1c8d7e6f5a"
      }
    }
  ],
  "_count": {
    "values": 0
  },
  "translations": {},
  "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/metafield-definitions/{id}/platforms"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/metafield-definitions/{id}/platforms"
}

{
  "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/metafield-definitions/{id}/platforms"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/metafield-definitions/{id}/platforms"
}

Get metafields for a product

GET
/v1/products/{productId}/metafields
AuthorizationBearer <token>

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

productIdstring

Product ID

curl -X GET "https://api.brainerce.com/api/v1/products/string/metafields"
[
  {
    "id": "string",
    "productId": "string",
    "variantId": "string",
    "metafieldDefinitionId": "string",
    "value": "string",
    "translations": {},
    "syncStatus": {},
    "source": "string",
    "lastModifiedBy": "string",
    "createdAt": "2019-08-24T14:15:22Z",
    "updatedAt": "2019-08-24T14:15:22Z",
    "definition": {
      "id": "string",
      "accountId": "string",
      "storeId": "string",
      "name": "string",
      "key": "string",
      "description": "string",
      "type": "string",
      "required": true,
      "minLength": 0,
      "maxLength": 0,
      "minValue": "string",
      "maxValue": "string",
      "enumValues": {},
      "defaultValue": "string",
      "dateAvailability": {},
      "isActive": true,
      "isCustomerInput": true,
      "appliesToAllProducts": true,
      "filterable": true,
      "position": 0,
      "publishedOn": [
        "string"
      ],
      "platformIds": {},
      "platformMetadata": {},
      "syncStatus": {},
      "lastSyncedAt": {},
      "source": "string",
      "translations": {},
      "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/products/{productId}/metafields"
}

{
  "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/products/{productId}/metafields"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/products/{productId}/metafields"
}

Delete a product metafield value

Supports Idempotency-Key header for safe retries.

DELETE
/v1/products/{productId}/metafields/{definitionId}
AuthorizationBearer <token>

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

productIdstring

Product ID

definitionIdstring

Metafield Definition ID

Query Parameters

variantId?string

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
curl -X DELETE "https://api.brainerce.com/api/v1/products/string/metafields/string?variantId=string" \  -H "Idempotency-Key: string"
Empty

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/products/{productId}/metafields/{definitionId}"
}

{
  "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/products/{productId}/metafields/{definitionId}"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/products/{productId}/metafields/{definitionId}"
}

Upsert a product metafield value

Supports Idempotency-Key header for safe retries.

PUT
/v1/products/{productId}/metafields/{definitionId}
AuthorizationBearer <token>

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

productIdstring

Product ID

definitionIdstring

Metafield Definition ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255

Empty Object

curl -X PUT "https://api.brainerce.com/api/v1/products/string/metafields/string" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{}'
{
  "id": "string",
  "productId": "string",
  "variantId": "string",
  "metafieldDefinitionId": "string",
  "value": "string",
  "translations": {},
  "syncStatus": {},
  "source": "string",
  "lastModifiedBy": "string",
  "createdAt": "2019-08-24T14:15:22Z",
  "updatedAt": "2019-08-24T14:15:22Z",
  "definition": {
    "id": "string",
    "accountId": "string",
    "storeId": "string",
    "name": "string",
    "key": "string",
    "description": "string",
    "type": "string",
    "required": true,
    "minLength": 0,
    "maxLength": 0,
    "minValue": "string",
    "maxValue": "string",
    "enumValues": {},
    "defaultValue": "string",
    "dateAvailability": {},
    "isActive": true,
    "isCustomerInput": true,
    "appliesToAllProducts": true,
    "filterable": true,
    "position": 0,
    "publishedOn": [
      "string"
    ],
    "platformIds": {},
    "platformMetadata": {},
    "syncStatus": {},
    "lastSyncedAt": {},
    "source": "string",
    "translations": {},
    "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/products/{productId}/metafields/{definitionId}"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/products/{productId}/metafields/{definitionId}"
}

{
  "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/products/{productId}/metafields/{definitionId}"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/products/{productId}/metafields/{definitionId}"
}

Get email settings for store

GET
/v1/email/settings
AuthorizationBearer <token>

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/email/settings"
{
  "id": "string",
  "accountId": "string",
  "storeId": "string",
  "emailsEnabled": true,
  "defaultFromName": "string",
  "defaultReplyTo": "string",
  "eventSettings": {},
  "hourlyRateLimit": 0,
  "customDomainId": "string",
  "dailyCampaignSendLimit": 0,
  "clickTrackingEnabled": true,
  "footerAddress": "string",
  "footerSocialLinks": {},
  "footerCustomText": "string",
  "fontFamily": "string",
  "deliveryProviderOverride": "string",
  "sesTenantName": "string",
  "marketingSendingPaused": true,
  "marketingPauseReason": "string",
  "marketingPausedAt": "2019-08-24T14:15:22Z",
  "createdAt": "2019-08-24T14:15:22Z",
  "updatedAt": "2019-08-24T14:15:22Z",
  "customDomain": {
    "id": "string",
    "storeId": "string",
    "domain": "mail.mystore.com",
    "status": "string",
    "dnsRecords": {},
    "provider": "string",
    "providerIdentityId": "string",
    "providerRegion": "string",
    "mailFromDomain": "string",
    "dkimStatus": "string",
    "mailFromStatus": "string",
    "resendDomainId": "string",
    "verifiedAt": "2019-08-24T14:15:22Z",
    "lastCheckedAt": "2019-08-24T14:15:22Z",
    "createdAt": "2019-08-24T14:15:22Z",
    "updatedAt": "2019-08-24T14:15:22Z"
  },
  "hasOwnerEmail": true
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/email/settings"
}

{
  "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/email/settings"
}

Update email settings for store

Supports Idempotency-Key header for safe retries.

PUT
/v1/email/settings
AuthorizationBearer <token>

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

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
emailsEnabled?boolean

Whether emails are enabled for this store

Defaulttrue
clickTrackingEnabled?boolean

Track link clicks in marketing campaigns. Off by default, because enabling it starts recording which recipients clicked, so the store owner must opt in rather than inherit it. Opens are never tracked.

defaultFromName?string

Default from name for emails

defaultReplyTo?string

Reply-to email address

eventSettings?object

Per-event settings

Empty Object

hourlyRateLimit?number

Hourly rate limit for emails

Default100
Range1 <= value <= 1000
customDomainId?string

Custom domain ID to use for sending

footerAddress?string

Physical postal address shown in the footer of marketing mail. Required before any marketing-class message can be sent. CAN-SPAM mandates a visible business address, and the send is refused without one.

curl -X PUT "https://api.brainerce.com/api/v1/email/settings" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{}'
{
  "id": "string",
  "accountId": "string",
  "storeId": "string",
  "emailsEnabled": true,
  "defaultFromName": "string",
  "defaultReplyTo": "string",
  "eventSettings": {},
  "hourlyRateLimit": 0,
  "customDomainId": "string",
  "dailyCampaignSendLimit": 0,
  "clickTrackingEnabled": true,
  "footerAddress": "string",
  "footerSocialLinks": {},
  "footerCustomText": "string",
  "fontFamily": "string",
  "deliveryProviderOverride": "string",
  "sesTenantName": "string",
  "marketingSendingPaused": true,
  "marketingPauseReason": "string",
  "marketingPausedAt": "2019-08-24T14:15:22Z",
  "createdAt": "2019-08-24T14:15:22Z",
  "updatedAt": "2019-08-24T14:15:22Z",
  "customDomain": {
    "id": "string",
    "storeId": "string",
    "domain": "mail.mystore.com",
    "status": "string",
    "dnsRecords": {},
    "provider": "string",
    "providerIdentityId": "string",
    "providerRegion": "string",
    "mailFromDomain": "string",
    "dkimStatus": "string",
    "mailFromStatus": "string",
    "resendDomainId": "string",
    "verifiedAt": "2019-08-24T14:15:22Z",
    "lastCheckedAt": "2019-08-24T14:15:22Z",
    "createdAt": "2019-08-24T14:15:22Z",
    "updatedAt": "2019-08-24T14:15:22Z"
  },
  "hasOwnerEmail": true
}

{
  "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/email/settings"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/email/settings"
}

{
  "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/email/settings"
}

Get Storefront Bot settings for every connection on the store

GET
/v1/storefront-bot/settings
AuthorizationBearer <token>

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/storefront-bot/settings"
{
  "connections": [
    {
      "salesChannelId": "string",
      "connectionId": "vc_9f3a2b1c8d7e6f5a",
      "name": "string"
    }
  ],
  "settings": [
    {
      "salesChannelId": "string",
      "connectionId": "string",
      "name": "string",
      "enabled": true,
      "displayName": "Assistant",
      "avatarUrl": "string",
      "personaJson": {},
      "escalationJson": {},
      "retentionJson": {},
      "localeJson": {},
      "dailySpendCeilingUsd": 0
    }
  ]
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/storefront-bot/settings"
}

{
  "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/storefront-bot/settings"
}

Update one connection’s Storefront Bot settings

Only fields present in the body are changed. Supports Idempotency-Key header for safe retries.

PUT
/v1/storefront-bot/settings
AuthorizationBearer <token>

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

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255

Empty Object

curl -X PUT "https://api.brainerce.com/api/v1/storefront-bot/settings" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{}'
{
  "salesChannelId": "string",
  "connectionId": "string",
  "name": "string",
  "enabled": true,
  "displayName": "Assistant",
  "avatarUrl": "string",
  "personaJson": {},
  "escalationJson": {},
  "retentionJson": {},
  "localeJson": {},
  "dailySpendCeilingUsd": 0
}

{
  "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/storefront-bot/settings"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/storefront-bot/settings"
}

{
  "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/storefront-bot/settings"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/storefront-bot/settings"
}

List Storefront Bot conversations

GET
/v1/storefront-bot/conversations
AuthorizationBearer <token>

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

Query Parameters

salesChannelId?string
page?number
limit?number
curl -X GET "https://api.brainerce.com/api/v1/storefront-bot/conversations?salesChannelId=string&page=0&limit=0"
{
  "data": [
    {
      "id": "string",
      "salesChannelId": "string",
      "title": "string",
      "locale": "string",
      "customerName": "string",
      "lastMessage": {
        "role": "string",
        "content": "string",
        "createdAt": "2019-08-24T14:15:22Z"
      },
      "messageCount": 0,
      "unread": true,
      "lastActivityAt": "2019-08-24T14:15:22Z",
      "createdAt": "2019-08-24T14:15:22Z"
    }
  ],
  "meta": {
    "page": 1,
    "limit": 20,
    "total": 137,
    "totalPages": 7
  }
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/storefront-bot/conversations"
}

{
  "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/storefront-bot/conversations"
}

Get one Storefront Bot conversation transcript

GET
/v1/storefront-bot/conversations/{id}
AuthorizationBearer <token>

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

idstring

Conversation id

curl -X GET "https://api.brainerce.com/api/v1/storefront-bot/conversations/string"
{
  "id": "string",
  "salesChannelId": "string",
  "title": "string",
  "locale": "string",
  "customerName": "string",
  "customerEmail": "[email protected]",
  "merchantReadAt": "2019-08-24T14:15:22Z",
  "lastActivityAt": "2019-08-24T14:15:22Z",
  "createdAt": "2019-08-24T14:15:22Z",
  "messages": [
    {
      "id": "string",
      "role": "string",
      "content": "string",
      "createdAt": "2019-08-24T14:15:22Z"
    }
  ],
  "summarizedHistory": "string"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/storefront-bot/conversations/{id}"
}

{
  "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/storefront-bot/conversations/{id}"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/storefront-bot/conversations/{id}"
}

Summarize a Storefront Bot conversation

POST
/v1/storefront-bot/conversations/{id}/summarize
AuthorizationBearer <token>

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

idstring

Conversation id

curl -X POST "https://api.brainerce.com/api/v1/storefront-bot/conversations/string/summarize"
{
  "summarized": true,
  "summary": "string"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/storefront-bot/conversations/{id}/summarize"
}

{
  "statusCode": 402,
  "code": "PLAN_AI_CREDITS_EXHAUSTED",
  "message": "AI credit balance exhausted for this billing period",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/storefront-bot/conversations/{id}/summarize"
}

{
  "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/storefront-bot/conversations/{id}/summarize"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/storefront-bot/conversations/{id}/summarize"
}

Get all email templates for store

GET
/v1/email/templates
AuthorizationBearer <token>

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/email/templates"
{
  "templates": [
    {
      "id": "string",
      "accountId": "string",
      "storeId": "string",
      "name": "Order Confirmation",
      "eventType": "string",
      "language": "string",
      "subject": "Order {{orderNumber}} Confirmed",
      "htmlContent": "string",
      "textContent": "string",
      "variables": {},
      "isDefault": true,
      "isActive": true,
      "createdAt": "2019-08-24T14:15:22Z",
      "updatedAt": "2019-08-24T14:15:22Z"
    }
  ],
  "language": "string",
  "supportedLanguages": [
    "string"
  ]
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/email/templates"
}

{
  "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/email/templates"
}

Create a new email template

Supports Idempotency-Key header for safe retries.

POST
/v1/email/templates
AuthorizationBearer <token>

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

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
namestring

Template display name

eventTypestring

Event type this template is for

Value in"ORDER_CONFIRMATION" | "ORDER_SHIPPED" | "ORDER_CANCELLED" | "ORDER_COMPLETED" | "ORDER_REFUNDED" | "ORDER_NOTE" | "NEW_ORDER_ADMIN" | "NEW_CUSTOMER" | "LOW_STOCK_ALERT" | "CART_ABANDONED" | "EMAIL_VERIFICATION" | "TEAM_INVITATION" | "ORDERS_LINKED" | "STORE_INVITATION" | "STORE_ROLE_CHANGED" | "STORE_DELETED" | "EXPORT_COMPLETE" | "SCHEDULED_BACKUP_SUCCESS" | "SCHEDULED_BACKUP_FAILED" | "PASSWORD_RESET" | "INQUIRY_NEW_ADMIN" | "INQUIRY_REPLY_CUSTOMER" | "INQUIRY_CONFIRMATION_CUSTOMER" | "MARKETING_CAMPAIGN"
languagestring

Locale this template is for (must be in store.supportedLanguages)

subjectstring

Email subject line (supports Handlebars)

htmlContentstring

HTML content of the email (supports Handlebars)

textContent?string

Plain text content of the email

variables?object

Variable schema for this template

Empty Object

isActive?boolean

Whether this template is active

Defaulttrue
curl -X POST "https://api.brainerce.com/api/v1/email/templates" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{    "name": "Order Confirmation",    "eventType": "ORDER_CONFIRMATION",    "language": "en",    "subject": "Order #{{orderNumber}} Confirmed",    "htmlContent": "<h1>Thank you for your order!</h1>"  }'
{
  "id": "string",
  "accountId": "string",
  "storeId": "string",
  "name": "Order Confirmation",
  "eventType": "string",
  "language": "string",
  "subject": "Order {{orderNumber}} Confirmed",
  "htmlContent": "string",
  "textContent": "string",
  "variables": {},
  "isDefault": true,
  "isActive": true,
  "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/email/templates"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/email/templates"
}

{
  "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/email/templates"
}

Get email template by ID

GET
/v1/email/templates/{templateId}
AuthorizationBearer <token>

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

templateIdstring

Template ID

curl -X GET "https://api.brainerce.com/api/v1/email/templates/string"
{
  "id": "string",
  "accountId": "string",
  "storeId": "string",
  "name": "Order Confirmation",
  "eventType": "string",
  "language": "string",
  "subject": "Order {{orderNumber}} Confirmed",
  "htmlContent": "string",
  "textContent": "string",
  "variables": {},
  "isDefault": true,
  "isActive": true,
  "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/email/templates/{templateId}"
}

{
  "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/email/templates/{templateId}"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/email/templates/{templateId}"
}

Delete an email template

Supports Idempotency-Key header for safe retries.

DELETE
/v1/email/templates/{templateId}
AuthorizationBearer <token>

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

templateIdstring

Template ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
curl -X DELETE "https://api.brainerce.com/api/v1/email/templates/string" \  -H "Idempotency-Key: string"
Empty

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/email/templates/{templateId}"
}

{
  "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/email/templates/{templateId}"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/email/templates/{templateId}"
}

Update an email template

Supports Idempotency-Key header for safe retries.

PUT
/v1/email/templates/{templateId}
AuthorizationBearer <token>

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

templateIdstring

Template ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
name?string

Template display name

subject?string

Email subject line (supports Handlebars)

htmlContent?string

HTML content of the email (supports Handlebars)

textContent?string

Plain text content of the email

variables?object

Variable schema for this template

Empty Object

isActive?boolean

Whether this template is active

curl -X PUT "https://api.brainerce.com/api/v1/email/templates/string" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{}'
{
  "id": "string",
  "accountId": "string",
  "storeId": "string",
  "name": "Order Confirmation",
  "eventType": "string",
  "language": "string",
  "subject": "Order {{orderNumber}} Confirmed",
  "htmlContent": "string",
  "textContent": "string",
  "variables": {},
  "isDefault": true,
  "isActive": true,
  "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/email/templates/{templateId}"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/email/templates/{templateId}"
}

{
  "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/email/templates/{templateId}"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/email/templates/{templateId}"
}

Preview an email template with sample data

Supports Idempotency-Key header for safe retries.

POST
/v1/email/templates/{templateId}/preview
AuthorizationBearer <token>

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

templateIdstring

Template ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
variables?object

Sample variables for rendering the template

Empty Object

curl -X POST "https://api.brainerce.com/api/v1/email/templates/string/preview" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{}'
{
  "subject": "string",
  "html": "string",
  "text": "string"
}

{
  "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/email/templates/{templateId}/preview"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/email/templates/{templateId}/preview"
}

{
  "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/email/templates/{templateId}/preview"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/email/templates/{templateId}/preview"
}

Get pending metafield conflicts

GET
/v1/metafield-conflicts
AuthorizationBearer <token>

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/metafield-conflicts"
[
  {
    "id": "string",
    "storeId": "string",
    "productId": "string",
    "metafieldDefinitionId": "string",
    "sourcePlatform": "string",
    "sourceValue": "string",
    "currentValue": "string",
    "status": "string",
    "resolution": "string",
    "resolvedAt": "2019-08-24T14:15:22Z",
    "createdAt": "2019-08-24T14:15:22Z",
    "updatedAt": "2019-08-24T14:15:22Z",
    "product": {
      "id": "string",
      "name": "string",
      "sku": "string"
    },
    "definition": {
      "id": "string",
      "name": "string",
      "key": "string",
      "type": "string"
    }
  }
]

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/metafield-conflicts"
}

{
  "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/metafield-conflicts"
}

Resolve a metafield conflict

Supports Idempotency-Key header for safe retries.

POST
/v1/metafield-conflicts/{conflictId}/resolve
AuthorizationBearer <token>

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

conflictIdstring

Conflict ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255

Empty Object

curl -X POST "https://api.brainerce.com/api/v1/metafield-conflicts/string/resolve" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{}'
{
  "id": "string",
  "storeId": "string",
  "productId": "string",
  "metafieldDefinitionId": "string",
  "sourcePlatform": "string",
  "sourceValue": "string",
  "currentValue": "string",
  "status": "string",
  "resolution": "string",
  "resolvedAt": "2019-08-24T14:15:22Z",
  "createdAt": "2019-08-24T14:15:22Z",
  "updatedAt": "2019-08-24T14:15:22Z",
  "product": {
    "id": "string",
    "name": "string",
    "sku": "string"
  },
  "definition": {
    "id": "string",
    "name": "string",
    "key": "string",
    "type": "string"
  }
}

{
  "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/metafield-conflicts/{conflictId}/resolve"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/metafield-conflicts/{conflictId}/resolve"
}

{
  "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/metafield-conflicts/{conflictId}/resolve"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/metafield-conflicts/{conflictId}/resolve"
}

Ignore a metafield conflict

Supports Idempotency-Key header for safe retries.

POST
/v1/metafield-conflicts/{conflictId}/ignore
AuthorizationBearer <token>

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

conflictIdstring

Conflict ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
curl -X POST "https://api.brainerce.com/api/v1/metafield-conflicts/string/ignore" \  -H "Idempotency-Key: string"
{
  "id": "string",
  "storeId": "string",
  "productId": "string",
  "metafieldDefinitionId": "string",
  "sourcePlatform": "string",
  "sourceValue": "string",
  "currentValue": "string",
  "status": "string",
  "resolution": "string",
  "resolvedAt": "2019-08-24T14:15:22Z",
  "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/metafield-conflicts/{conflictId}/ignore"
}

{
  "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/metafield-conflicts/{conflictId}/ignore"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/metafield-conflicts/{conflictId}/ignore"
}

Get all OAuth provider configurations

GET
/v1/oauth-providers
AuthorizationBearer <token>

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/oauth-providers"
[
  {
    "id": "string",
    "storeId": "string",
    "provider": "GOOGLE",
    "clientId": "string",
    "isEnabled": true,
    "providerConfig": {},
    "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/oauth-providers"
}

{
  "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/oauth-providers"
}

Configure an OAuth provider

Supports Idempotency-Key header for safe retries.

POST
/v1/oauth-providers
AuthorizationBearer <token>

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

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255

Empty Object

curl -X POST "https://api.brainerce.com/api/v1/oauth-providers" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{}'
{
  "id": "string",
  "storeId": "string",
  "provider": "GOOGLE",
  "clientId": "string",
  "isEnabled": true,
  "providerConfig": {},
  "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/oauth-providers"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/oauth-providers"
}

{
  "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/oauth-providers"
}

Get OAuth provider configuration

GET
/v1/oauth-providers/{provider}
AuthorizationBearer <token>

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

providerstring

OAuth provider (GOOGLE, FACEBOOK, GITHUB)

curl -X GET "https://api.brainerce.com/api/v1/oauth-providers/string"
{
  "id": "string",
  "storeId": "string",
  "provider": "GOOGLE",
  "clientId": "string",
  "isEnabled": true,
  "providerConfig": {},
  "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/oauth-providers/{provider}"
}

{
  "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/oauth-providers/{provider}"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/oauth-providers/{provider}"
}

Update OAuth provider configuration

Supports Idempotency-Key header for safe retries.

PATCH
/v1/oauth-providers/{provider}
AuthorizationBearer <token>

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

providerstring

OAuth provider (GOOGLE, FACEBOOK, GITHUB)

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255

Empty Object

curl -X PATCH "https://api.brainerce.com/api/v1/oauth-providers/string" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{}'
{
  "id": "string",
  "storeId": "string",
  "provider": "GOOGLE",
  "clientId": "string",
  "isEnabled": true,
  "providerConfig": {},
  "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/oauth-providers/{provider}"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/oauth-providers/{provider}"
}

{
  "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/oauth-providers/{provider}"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/oauth-providers/{provider}"
}

Delete OAuth provider configuration

Supports Idempotency-Key header for safe retries.

DELETE
/v1/oauth-providers/{provider}
AuthorizationBearer <token>

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

providerstring

OAuth provider (GOOGLE, FACEBOOK, GITHUB)

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
curl -X DELETE "https://api.brainerce.com/api/v1/oauth-providers/string" \  -H "Idempotency-Key: string"
Empty

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/oauth-providers/{provider}"
}

{
  "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/oauth-providers/{provider}"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/oauth-providers/{provider}"
}

List reviews for a product (admin view, includes hidden)

GET
/v1/products/{productId}/reviews
AuthorizationBearer <token>

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

productIdstring

Product ID

Query Parameters

page?number
limit?number
visibility?string
Value in"visible" | "hidden" | "all"
curl -X GET "https://api.brainerce.com/api/v1/products/string/reviews?page=0&limit=0&visibility=visible"
{
  "data": [
    {
      "id": "string",
      "productId": "string",
      "authorName": "string",
      "rating": 1,
      "body": "string",
      "verifiedPurchase": true,
      "hiddenAt": "2019-08-24T14:15:22Z",
      "createdAt": "2019-08-24T14:15:22Z",
      "customerId": "string",
      "authorEmail": "[email protected]",
      "orderId": "string",
      "updatedAt": "2019-08-24T14:15:22Z",
      "images": [
        {
          "id": "string",
          "url": "string",
          "thumbnailUrl": "string",
          "width": 0,
          "height": 0,
          "position": 0,
          "assetKey": "string",
          "approvedAt": "2019-08-24T14:15:22Z",
          "hiddenAt": "2019-08-24T14:15:22Z",
          "createdAt": "2019-08-24T14:15:22Z"
        }
      ]
    }
  ],
  "meta": {
    "page": 1,
    "limit": 20,
    "total": 137,
    "totalPages": 7
  }
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/products/{productId}/reviews"
}

{
  "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/products/{productId}/reviews"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/products/{productId}/reviews"
}

Submit a product review (server-to-server)

Supports Idempotency-Key header for safe retries.

POST
/v1/products/{productId}/reviews
AuthorizationBearer <token>

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

productIdstring

Product ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
authorNamestring

Reviewer display name. 1-100 chars, no HTML angle brackets (a basic XSS guard; full sanitization happens server-side).

Length1 <= length <= 100
authorEmail?string

Optional reviewer email, used for verified-buyer matching. It is never mailed: Brainerce sends no notification when a review is submitted or published, and there is no template for one.

Lengthlength <= 200
ratingnumber

Star rating, 1-5 (integer).

Range1 <= value <= 5
body?string

Review body (≤ 5000 chars). Markdown is rendered as plain text, and links are stripped.

Lengthlength <= 5000
imageKeys?array<string>

Media library storage keys to attach as review photos, in display order. This is the import path: upload the images through the normal media endpoints first, then pass their keys here. Keys must belong to the same store. Photos imported this way publish immediately regardless of the store's approval setting, since a server-to-server caller has already vetted them.

Itemsitems <= 5
curl -X POST "https://api.brainerce.com/api/v1/products/string/reviews" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{    "authorName": "Jane D.",    "rating": 5  }'
{
  "id": "string",
  "productId": "string",
  "authorName": "string",
  "rating": 1,
  "body": "string",
  "verifiedPurchase": true,
  "hiddenAt": "2019-08-24T14:15:22Z",
  "createdAt": "2019-08-24T14:15:22Z",
  "images": [
    {
      "id": "string",
      "url": "string",
      "thumbnailUrl": "string",
      "width": 0,
      "height": 0,
      "position": 0
    }
  ]
}

{
  "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/products/{productId}/reviews"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/products/{productId}/reviews"
}

{
  "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/products/{productId}/reviews"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/products/{productId}/reviews"
}

Hide a review from the storefront

Supports Idempotency-Key header for safe retries.

PATCH
/v1/reviews/{id}/hide
AuthorizationBearer <token>

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

idstring

Review ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
curl -X PATCH "https://api.brainerce.com/api/v1/reviews/string/hide" \  -H "Idempotency-Key: string"
{
  "id": "string",
  "productId": "string",
  "authorName": "string",
  "rating": 1,
  "body": "string",
  "verifiedPurchase": true,
  "hiddenAt": "2019-08-24T14:15:22Z",
  "createdAt": "2019-08-24T14:15:22Z",
  "customerId": "string",
  "authorEmail": "[email protected]",
  "orderId": "string",
  "updatedAt": "2019-08-24T14:15:22Z",
  "images": [
    {
      "id": "string",
      "url": "string",
      "thumbnailUrl": "string",
      "width": 0,
      "height": 0,
      "position": 0,
      "assetKey": "string",
      "approvedAt": "2019-08-24T14:15:22Z",
      "hiddenAt": "2019-08-24T14:15:22Z",
      "createdAt": "2019-08-24T14:15:22Z"
    }
  ]
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/reviews/{id}/hide"
}

{
  "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/reviews/{id}/hide"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/reviews/{id}/hide"
}

Unhide a previously hidden review

Supports Idempotency-Key header for safe retries.

PATCH
/v1/reviews/{id}/show
AuthorizationBearer <token>

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

idstring

Review ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
curl -X PATCH "https://api.brainerce.com/api/v1/reviews/string/show" \  -H "Idempotency-Key: string"
{
  "id": "string",
  "productId": "string",
  "authorName": "string",
  "rating": 1,
  "body": "string",
  "verifiedPurchase": true,
  "hiddenAt": "2019-08-24T14:15:22Z",
  "createdAt": "2019-08-24T14:15:22Z",
  "customerId": "string",
  "authorEmail": "[email protected]",
  "orderId": "string",
  "updatedAt": "2019-08-24T14:15:22Z",
  "images": [
    {
      "id": "string",
      "url": "string",
      "thumbnailUrl": "string",
      "width": 0,
      "height": 0,
      "position": 0,
      "assetKey": "string",
      "approvedAt": "2019-08-24T14:15:22Z",
      "hiddenAt": "2019-08-24T14:15:22Z",
      "createdAt": "2019-08-24T14:15:22Z"
    }
  ]
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/reviews/{id}/show"
}

{
  "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/reviews/{id}/show"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/reviews/{id}/show"
}

Hide a single review photo, leaving the review itself visible

Supports Idempotency-Key header for safe retries.

PATCH
/v1/review-images/{id}/hide
AuthorizationBearer <token>

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

idstring

Review photo ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
curl -X PATCH "https://api.brainerce.com/api/v1/review-images/string/hide" \  -H "Idempotency-Key: string"
{
  "id": "string",
  "url": "string",
  "thumbnailUrl": "string",
  "width": 0,
  "height": 0,
  "position": 0,
  "assetKey": "string",
  "approvedAt": "2019-08-24T14:15:22Z",
  "hiddenAt": "2019-08-24T14:15:22Z",
  "createdAt": "2019-08-24T14:15:22Z"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/review-images/{id}/hide"
}

{
  "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/review-images/{id}/hide"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/review-images/{id}/hide"
}

Show a review photo (also approves one that was awaiting a decision)

Supports Idempotency-Key header for safe retries.

PATCH
/v1/review-images/{id}/show
AuthorizationBearer <token>

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

idstring

Review photo ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
curl -X PATCH "https://api.brainerce.com/api/v1/review-images/string/show" \  -H "Idempotency-Key: string"
{
  "id": "string",
  "url": "string",
  "thumbnailUrl": "string",
  "width": 0,
  "height": 0,
  "position": 0,
  "assetKey": "string",
  "approvedAt": "2019-08-24T14:15:22Z",
  "hiddenAt": "2019-08-24T14:15:22Z",
  "createdAt": "2019-08-24T14:15:22Z"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/review-images/{id}/show"
}

{
  "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/review-images/{id}/show"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/review-images/{id}/show"
}

Get the newsletter signup benefit settings

Returns null when the store has never configured a benefit. enabled: false means configured but switched off — two different states, and a storefront should treat both as "offer nothing".

GET
/v1/newsletter-benefit/settings
AuthorizationBearer <token>

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/newsletter-benefit/settings"
Empty

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/newsletter-benefit/settings"
}

{
  "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/newsletter-benefit/settings"
}

Create or replace the newsletter signup benefit settings

A FULL REPLACEMENT, not a patch: every field is written, so omitting one clears it. Bumps the stored settings version.

Changing these settings never rewrites a promise already made. Signups still waiting for a confirmation click keep the terms they were shown at signup, and coupons already issued are untouched.

Supports Idempotency-Key header for safe retries.

PUT
/v1/newsletter-benefit/settings
AuthorizationBearer <token>

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

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
enabledboolean

Turn the benefit on or off. Off stops new offers immediately; coupons already issued keep working, and signups still waiting for a confirmation click keep the terms they were shown.

discountTypeCouponType
Value in"PERCENTAGE" | "FIXED_AMOUNT"
discountValuenumber

Percent (1-100) for PERCENTAGE, or an amount in the store currency for FIXED_AMOUNT.

minimumOrderAmount?number

Minimum order subtotal required to use the coupon.

maximumDiscount?number

Cap on the discount a percentage benefit may produce. Ignored — and stored as null — for a fixed-amount benefit.

combinesWithOtherboolean

Whether the coupon may stack with other discounts.

validityDaysnumber

Days the coupon stays usable, counted from issuance.

eligibilityTtlHoursnumber

Hours a signup stays eligible, counted from the moment the form was submitted. A confirmation click after this still subscribes the address but earns no coupon — the confirmation LINK itself never expires, so this is the only deadline in the flow.

firstOrderOnlyboolean

Restrict the coupon to buyers with no previous order in this store. Checked against the address the coupon is bound to, including guest orders.

applicableProducts?array<string>

Product IDs the coupon applies to.

excludedProducts?array<string>
applicableCategories?array<string>
excludedCategories?array<string>
salesChannelIds?array<string>

Sales channels the issued coupon may be redeemed on. Leave empty for every enabled channel — an empty list is expanded at issuance, because a coupon with no channel rows is refused on every vibe-coded storefront.

content?object

Localized copy: { "en": { "headline": "...", "terms": "..." }, "he": { ... } }. Shown on the signup form and repeated in the email.

Empty Object

curl -X PUT "https://api.brainerce.com/api/v1/newsletter-benefit/settings" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{    "enabled": true,    "discountType": "PERCENTAGE",    "discountValue": 0,    "combinesWithOther": true,    "validityDays": 0,    "eligibilityTtlHours": 0,    "firstOrderOnly": true  }'
Empty

{
  "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/newsletter-benefit/settings"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/newsletter-benefit/settings"
}

{
  "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/newsletter-benefit/settings"
}

List issued newsletter benefits

Paginated { data, meta }, newest first. redeemed is derived from the coupon having been used, so it is accurate the moment an order completes.

There is deliberately NO email filter: a lookup-by-address would turn this into a "does this person shop here" probe for any leaked key with coupons:read. Filter the page you get back.

GET
/v1/newsletter-benefit/grants
AuthorizationBearer <token>

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

Query Parameters

storeId?string

Store the grants belong to. Required.

page?number
Default1
limit?number
Default20
Rangevalue <= 100
status?string
Value in"PENDING" | "ISSUING" | "ISSUED" | "EXPIRED" | "FAILED"
from?string

Signups created on or after this ISO-8601 date.

to?string

Signups created on or before this ISO-8601 date.

curl -X GET "https://api.brainerce.com/api/v1/newsletter-benefit/grants?storeId=string&page=1&limit=20&status=PENDING&from=string&to=string"
Empty

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/newsletter-benefit/grants"
}

{
  "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/newsletter-benefit/grants"
}

Re-send an issued newsletter benefit

Re-queues the SAME coupon code. It never mints a second one, so a support ticket cannot turn into two discounts. For a benefit whose issuance failed before any coupon existed, this retries the issuance and mails whatever comes out.

Rejects a signup that has not been confirmed yet, and one that lapsed before any coupon was minted: in both cases there is nothing to re-send and nothing that may be created.

POST
/v1/newsletter-benefit/grants/{grantId}/resend
AuthorizationBearer <token>

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

grantIdstring

Grant ID from the grants list

curl -X POST "https://api.brainerce.com/api/v1/newsletter-benefit/grants/string/resend"
Empty

{
  "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/newsletter-benefit/grants/{grantId}/resend"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/newsletter-benefit/grants/{grantId}/resend"
}

{
  "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/newsletter-benefit/grants/{grantId}/resend"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/newsletter-benefit/grants/{grantId}/resend"
}

On this page

No Headings