Endpoints
All paths are relative to https://live.bb.eight-cdn.com/api/headless/v1 and require Authorization: Bearer kit_live_….
List bundles
Section titled “List bundles”/bundles Returns published bundles (capped at 100). Lightweight — no sections or product data.
Response 200
[ { "id": 42, "name": "Build your own box", "description": "Pick any 3 items", "imageUrl": "https://cdn.shopify.com/…", "type": "native", "bundlingOption": "bundles", "published": true }]| Field | Type | Description |
|---|---|---|
id | number | Bundle ID. |
name | string | Display name. |
description | string | Merchant description (may be empty). |
imageUrl | string | Bundle image (may be empty). |
type | string | single-product | multiple-products | native. |
bundlingOption | string | Normally bundles. Legacy upsells bundles are still returned — handle defensively. |
published | boolean | Always true here. |
Get a bundle
Section titled “Get a bundle”/bundles/:id Full configuration: sections, discount, limit rules, required products and the conditions-engine graph. Product/variant detail comes from the products endpoint.
Response 200
{ "id": 42, "name": "Build your own box", "description": "Pick any 3 items", "imageUrl": "https://cdn.shopify.com/…", "type": "native", "bundlingOption": "bundles", "layout": "multiple-sections", "published": true, "discount": { "type": "percentage", "value": "10.00", "flatOrTiered": "flat", "minimum": null, "operator": "max", "tiers": [] }, "sections": [ { "id": 1, "name": "Choose a shirt", "description": "", "imageUrl": "", "order": 0, "autoNextSection": true, "products": [ { "bundleItemId": 7, "shopifyProductId": "111", "variantIds": ["1001", "1002"] } ] } ], "requiredProducts": [ { "shopifyProductId": "222", "variantIds": [], "quantity": 1 } ], "limitRules": [ { "type": "total-number-of-products", "operation": "gte", "value": "3", "sectionId": null } ], "weightUnit": "kg", "conditionsEngineEnabled": false, "conditionsEngineNodes": [], "conditionsPartial": false}| Field | Type | Description |
|---|---|---|
layout | string | single-page | multiple-sections. |
discount | object | null | Tiers carry type, value, operation, discount and customText (merchant copy override, null when unset). See Discounts. |
sections[] | array | id, name, description, imageUrl, order, autoNextSection, products[]. |
sections[].autoNextSection | boolean | The merchant’s “advance to the next step once this one is satisfied” setting. Mirror it rather than hardcoding the behaviour. |
sections[].products[] | array | bundleItemId, shopifyProductId, variantIds[] (empty = all variants allowed). |
requiredProducts[] | array | shopifyProductId, variantIds[], quantity. |
limitRules[] | array | type, operation, value, sectionId. See Limit rules. |
weightUnit | string | Shop weight unit (g/kg/oz/lb) for the amount-of-weight rule. |
conditionsEngineEnabled | boolean | Whether the bundle has conditions-engine logic. |
conditionsEngineNodes[] | array | Serialised rule graph (present when the shop’s conditions feature is on). The SDK evaluates it client-side. |
conditionsPartial | boolean | true when the bundle uses conditions the headless evaluator can’t run yet (cascade-variant) and they were stripped. |
sellingPlans[]? | array | Live Kitenzo Subscriptions plans — present only for shops in the subscriptions beta with live programs on this bundle. See Conditions & subscriptions. |
personalisation? | object | Per-product personalisation field definitions, keyed by Shopify product ID. Present only when the shop’s personalisation feature is on. |
Bundle products
Section titled “Bundle products”/bundles/:id/products Full product and variant data for the bundle — live pricing, SKU, availability, inventory, options and content fields.
Query parameters
| Param | Type | Description |
|---|---|---|
countryCode | string | Optional ISO 3166-1 alpha-2 country. Adds Shopify Markets presentment pricing + market availability per variant. |
Response 200
{ "products": [ { "shopifyProductId": "111", "shopifyProductGid": "gid://shopify/Product/111", "title": "Classic Tee", "handle": "classic-tee", "imageUrl": "https://cdn.shopify.com/…", "status": "ACTIVE", "tags": ["summer", "tees"], "descriptionHtml": "<p>Our classic tee…</p>", "options": [ { "name": "Size", "position": 1, "values": ["Small", "Medium"], "swatches": {} } ], "variants": [ { "shopifyVariantId": "1001", "shopifyVariantGid": "gid://shopify/ProductVariant/1001", "title": "Small / Black", "price": "29.99", "compareAtPrice": null, "sku": "TEE-S-BLK", "available": true, "inventoryQuantity": 42, "grams": 180, "optionValues": ["Small", "Black"] } ] } ]}Product fields
| Field | Type | Description |
|---|---|---|
status | string | ACTIVE | ARCHIVED | DRAFT. Archived/draft products can’t be added to the cart — don’t render them as pickable. |
tags | string[] | Product tags (Kitenzo’s internal bundle-builder-* control tags are stripped). |
descriptionHtml | string | Raw Shopify product HTML — sanitise before rendering. |
options[] | array | Option definitions (name, position, values[], swatches) in Shopify’s order, aligned with each variant’s optionValues. |
Variant fields
| Field | Type | Description |
|---|---|---|
price | string | Shop-currency price, 2 decimals. |
compareAtPrice | string | null | The variant’s own compare-at price, when set. |
grams | number | Weight in grams (what the amount-of-weight rule measures). |
optionValues | string[] | Option values in the same order as the product’s options. |
image | string? | Variant-level image — present only when the variant has one of its own; fall back to the product image. |
available | boolean | Base-store purchasability (see availability rules). |
presentmentPrice / presentmentCurrency / priceInShopCurrency / availableForSale | — | Present only when countryCode was supplied. availableForSale: false means “not sold in that market” — distinct from available. |
See Sections, products & variants for the full semantics.
Configure a bundle
Section titled “Configure a bundle”/bundles/:id/configure Validates a customer’s selection, creates a configured bundle, and returns the data needed to add it to the Shopify cart — including, for native bundles, a cart-ready payload any HTTP client can write straight to a cart (this is what the TapCart guide uses).
Request body
{ "type": "native", "products": [ { "variant": "1001", "product": "111", "section": 1 }, { "variant": "1002", "product": "111", "section": 1 } ], "countryCode": "US"}| Field | Type | Required | Description |
|---|---|---|---|
type | string | Yes | native | multiple-products | single-product. |
products | array | Yes | Non-empty list; each item needs variant, product, section. Max 200. |
countryCode | string | No | For market-aware pricing. |
subscription | object | No | id (recurring group), email, frequency, unit — creates a pending subscription. |
bundleContent | object | No | Conditions-engine discount override (conditionsDiscount) — sent automatically by the SDK; the server validates and signs it. |
Response 200
{ "configured_bundle_id": 9876, "variant_id": "44556677", "product_id": "111", "discount": "<encrypted-discount-string>", "subscription_id": null, "pricing": { "originalPrice": "59.98", "discountedPrice": "53.98", "discountType": "percentage", "discountValue": "10.00", "currency": "USD" }, "cart": { "line_items": [ { "id": 1001, "quantity": 1, "properties": { "_bundle_data": "9876#44556677#<uuid>" } }, { "id": 1002, "quantity": 1, "properties": { "_bundle_data": "9876#44556677#<uuid>" } } ], "attributes": { "_bundles": "{\"9876\": {\"configuredBundleId\": 9876, \"discount\": \"…\", \"title\": \"…\", \"items\": [{\"variantId\": 1001, \"count\": 1}], \"image\": null, \"note\": null, \"id\": 42}}" } }}| Field | Type | Description |
|---|---|---|
configured_bundle_id | number | The configured bundle’s ID. |
variant_id | string | Shopify variant to add to cart (the bundle’s ghost variant, for non-native paths). |
product_id | string | Shopify product for the bundle. |
discount | string | Encrypted, server-signed discount string (what the Cart Transform decrypts). |
subscription_id | number | null | Set when a recurring subscription option was chosen. |
pricing | object | Same shape as the price endpoint. |
cart | object | null | Cart-ready payload — native bundles only; null for other types (their discount is in the variant price). |
cart.line_items[] | array | Shopify /cart/add.js-shaped items (id, quantity, properties). Each carries _bundle_data = configuredBundleId#parentVariantId#uniqueId; all entries of one call share the same uniqueId. |
cart.attributes._bundles | string | Stringified one-entry map for the _bundles cart attribute, keyed by configuredBundleId. Merge it into any existing _bundles value — don’t overwrite. |
Price a selection
Section titled “Price a selection”/bundles/:id/price Calculates pricing for a selection without creating a configured bundle. Same request body as /configure (minus type): products, plus optional countryCode and bundleContent.
Response 200
{ "originalPrice": "59.98", "discountedPrice": "53.98", "discountType": "percentage", "discountValue": "10.00", "currency": "USD"}Amounts are always in the shop’s base currency — countryCode makes the discount maths market-correct but doesn’t change the response currency. For displaying localised prices, use the SDK’s presentment helpers.
Shop settings
Section titled “Shop settings”/settings Response 200
{ "activeFeatures": ["headless_api", "subscriptions"], "currency": "USD", "moneyFormat": "${{amount}}", "weightUnit": "kg"}| Field | Type | Description |
|---|---|---|
activeFeatures | string[] | Enabled Kitenzo features for the shop. |
currency | string | Shop currency code. |
moneyFormat | string | Shopify money format string (used by formatMoney). |
weightUnit | string | Shop weight unit (g/kg/oz/lb). |