API ReferenceEndpointsProductsUpdate a variant

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 → 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

Lengthlength <= 191
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

Lengthlength <= 500
attributes?object

Variant attribute map

price?number

Variant price in the store currency, as a decimal in major units (19.99 means 19.99, not cents). Same unit as the product basePrice; a variable product basePrice is its lowest variant price. When omitted the variant is sold at the product basePrice.

salePrice?number | null

Variant sale price in the store currency, a decimal in major units like price. Greater than 0 and lower than the variant price (the one sent with it, or the stored one), otherwise the request is refused with a 400. Pass null to take the variant off sale.

salePriceStartsAt?string | null

Variant sale-price effective window start (ISO 8601). Overrides the product window for this variant. Pass null to clear.

salePriceEndsAt?string | null

Variant sale-price effective window end (ISO 8601). See salePriceStartsAt. Pass null to clear.

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?string | object

Variant-specific image (URL string or image 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)

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.

curl -X PATCH "https://api.brainerce.com/api/v1/products/string/variants/string" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{}'
{
  "id": "clx1a2b3c4d5e6f7g8h9",
  "productId": "clx1a2b3c4d5e6f7g8h9",
  "sku": "TSHIRT-BLK-M",
  "name": "Black / M",
  "attributes": {
    "size": "S",
    "color": "Red"
  },
  "price": "19.90",
  "salePrice": "24.90",
  "costPrice": "12.00",
  "salePriceStartsAt": "2019-08-24T14:15:22Z",
  "salePriceEndsAt": "2019-08-24T14:15:22Z",
  "displayPrice": "29.90",
  "displaySalePrice": "24.90",
  "displayCurrency": "USD",
  "image": "string",
  "position": 0,
  "status": "active",
  "inventory": {
    "trackingMode": "TRACKED",
    "total": 42,
    "reserved": 3,
    "available": 39,
    "inStock": true,
    "canPurchase": true,
    "lastInventorySyncAt": "2019-08-24T14:15:22Z"
  },
  "isDownloadable": true,
  "downloadData": {
    "files": [
      {
        "id": "clx1a2b3c4d5e6f7g8h9",
        "name": "photos.zip",
        "url": "https://cdn.example.com/downloads/photos.zip",
        "storageKey": "downloads/photos.zip",
        "size": 5242880,
        "mimeType": "application/zip"
      }
    ],
    "downloadLimit": 3,
    "downloadExpiry": 30
  },
  "shippingClassId": "clx1a2b3c4d5e6f7g8h9",
  "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}"
}

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