Skip to main content

Knit Bundles — Headless Integration Guide

Knit Bundles exposes public endpoints for headless storefronts, mobile apps, and custom integrations that don’t run on the standard Shopify Liquid theme. There are two ways to integrate, depending on how much control you want: Both modes are served from the Knit app origin, not from your myshopify.com storefront. This is deliberate — see Why the app origin? below.

Base URL

All headless endpoints live under /headless/*. Every request identifies the target store with a shop query parameter:

Prerequisites

  1. Install the Knit Bundles app on your Shopify store.
  2. Configure at least one active bundle.
  3. Open the app once in the Shopify admin — this provisions the Storefront access token the headless endpoints need. (Until this happens, the endpoints return APP_NOT_INSTALLED.)
You do not need to embed the theme app extension for the headless endpoints to work.

Endpoint 1 — Bundle Data

Returns all active bundles for the shop as fully normalized JSON: resolved products, typed conditions and rewards, and the assets (JS/CSS) associated with each bundle. Use this when you want to build your own bundle UI.

Query parameters

Example

Response

Caching

Successful responses are sent with:
Bundle changes may take up to ~5 minutes to propagate through caches.

Endpoint 2 — Embed (iframe)

Returns a self-contained HTML page that loads the Knit widget runtime, fetches the applicable bundles, and renders the bundle widget for the given product. Drop it into an <iframe> and you’re done rendering — you only need to handle add-to-cart (see postMessage bridge).

Query parameters

Usage

The embed page is served with Cache-Control: no-store and without X-Frame-Options / frame-ancestors, so it is framable from any origin.

postMessage bridge

The embed iframe communicates with your host page over window.postMessage. It renders the widget and manages selection state itself, but it cannot add to your cart — that’s your job. You must listen for the messages below. Every message includes source, v (version), and type. Ignore any message whose source/v you don’t recognize.

Messages from iframe → your page

source: "nameless-bundle", v: 1

Messages from your page → iframe

source: "nameless-bundle-host", v: 1

lineItems shape

Critical: the __FB_ATC_UID attribute must reach Shopify as a line-item property. The Knit Discount Function uses it to group bundle lines and apply the tier discount. If your cart layer strips custom line-item properties, the discount will not apply.

Minimal host handler

If you don’t reply to an addToCart within ~10 seconds, the widget resolves the action as failed (CART_TIMEOUT) and shows an error to the shopper.

Response schema reference

The /headless/bundles endpoint returns { ok: true, bundles: Bundle[] }. Each Bundle has this shape (TypeScript):

How pricing works

Bundle discounts are expressed as conditions → rewards, not as precomputed prices:
  • A conditionSet fires when its conditions are met for the referenced selector(s) — e.g. a quantity condition with minimum: 2, maximum: 2 fires when the shopper picks quantity 2.
  • The matching rewardSet.rewards then apply — e.g. percentageDiscount of 19% off, or a fixedDiscount amount.
To display “Buy 2, save 19%” tiers, map each conditionSet to its exact quantity (minimum === maximum) and read the reward for that selector. See the example renderer for a complete implementation pattern.

Error responses

Errors return { ok: false, code, message } with an HTTP status: The /headless/embed endpoint returns plain-text 400 / 404 bodies for the same missing-param / not-enabled conditions.

Why the app origin?

Headless clients can’t frame {shop}.myshopify.com directly: Shopify sends Content-Security-Policy: frame-ancestors 'none' on every storefront response, and it isn’t merchant-configurable. So the embed page is served from the Knit app origin, which sets no such header and is therefore framable anywhere. Because the page lives on the app origin (not the storefront), the widget fetches its data from /headless/bundles (same-origin, no CORS) rather than the storefront App Proxy.

Note on the App Proxy endpoints

You may see references to https://{shop}.myshopify.com/apps/proxy/bundles and /apps/proxy/embed. Those are HMAC-signed App Proxy endpoints intended for hosts that can frame the storefront origin. They return the same normalized Bundle[] schema documented above. For headless apps and mobile/WebView hosts, use the /headless/* endpoints in this guide instead.

Support

Include your shop domain, the endpoint URL, and the full JSON error response (or the [nameless] console logs from the embed iframe) when contacting support.