API ReferenceEndpointsPublic API (v1)Set 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 → 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"
}

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