> ## Documentation Index
> Fetch the complete documentation index at: https://docs.fluid.app/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> For new direct REST integrations, use the v2026-04 surfaces. The @fluid-app FairShare SDK continues to use its own published public-v2025-06 contract.
> Authenticate with the header Authorization: Bearer <token>; public storefront read endpoints require no auth.
> Lists use cursor pagination via the page[cursor] and page[limit] query params; follow meta.pagination.next_cursor until it is null.
> Never use /api/company/v1 or /api/v1 paths, page/per_page params, or offset pagination — they are legacy.
> The OpenAPI specs under api-reference/ are the authoritative contracts; prefer them over prose when in doubt. api-reference/storefront-v2026-04.yaml covers the v2026-04 storefront surface (/api/v202604/... paths); api-reference/auth-v0.yaml covers the unversioned auth surface (/api/... paths — authentication, MFA, social auth, and token exchange); api-reference/checkout-v2026-04.yaml covers the v2026-04 checkout surface (/api/checkout/v2026-04/... paths — carts, cart auth, discounts, items, subscriptions, orders, enrollments, and store config); api-reference/public-v2025-06.yaml covers the Public SDK surface used by the @fluid-app FairShare SDK, including its parallel cart lifecycle, browser integrations, versioned payment callbacks, unversioned public utilities, and the cart price-override operation; api-reference/payment-v2026-04.yaml covers the v2026-04 payment gateway admin surface (/api/payment/v2026-04/... paths, bearer-authenticated — gateway CRUD, gateway purchase/authorize/$0-verify, transaction list/show and capture/void/credit, and merchant payment configuration); api-reference/payments-v2026-04.yaml covers the v2026-04 cart payment surface (/api/payments/v2026-04/carts/{cart_token}/... paths, authenticated by the cart token in the path with no bearer — payment-method selection, VGS card tokenization, 3D Secure verification, and PayPal/Braintree/Klarna/Apple Pay flows); api-reference/commerce-v2026-04.yaml covers the v2026-04 commerce order-editing surface (/api/v202604/orders/{order_id}/edits paths, bearer-authenticated — post-checkout order edits that atomically insert items and add adjustments/discounts, with an optional dry-run preview); api-reference/webhooks-v0.yaml covers the unversioned webhooks surface (/api/... paths — webhook registration, delivery payloads, callback registrations, company events, and webhook/callback schemas).
> Successful responses wrap the resource payload alongside a top-level integer status and a meta object.

# Build a product bundle selector

> Render bundle groups, collect shopper selections, and add the configured bundle to a cart.

A bundle selector has three jobs: render the available groups, collect a valid choice for each
group, and send the selected variants with the parent bundle.

## Read the bundle structure

Fetch the product from the public storefront Product show operation. Its response exposes
`is_bundle` and a top-level `bundle_groups` array on `product`. Non-bundle products return an empty
`bundle_groups` array.

Use `product.bundle_groups[]`, not the legacy `product.product_bundle_groups[]` shape. Each group
provides `id`, `title`, `min_selections`, `max_selections`, `selection_type`, and
`bundle_group_items`. Each item identifies its selectable variant with `bundled_variant_id`.

```liquid theme={null}
{% if product.is_bundle and product.bundle_groups.size > 0 %}
  {% for group in product.bundle_groups %}
    <fieldset data-bundle-group="{{ group.id }}">
      <legend>{{ group.title }}</legend>

      {% for item in group.bundle_group_items %}
        <label>
          <input
            type="checkbox"
            value="{{ item.bundled_variant_id }}"
            data-bundle-item
          >
          Variant {{ item.bundled_variant_id }}
        </label>
      {% endfor %}
    </fieldset>
  {% endfor %}
{% endif %}
```

Use the group's selection fields to validate the shopper's choices before enabling your add
button. Treat that client-side check as feedback, not as a replacement for cart validation.

## Build the cart selection

Create one child entry for every selected variant. Keep the group `id` with each choice so checkout
can associate it with the correct bundle group.

```javascript theme={null}
const bundledItems = Array.from(
  document.querySelectorAll("[data-bundle-item]:checked"),
).map((input) => ({
  variant_id: Number(input.value),
  quantity: 1,
  product_bundle_group_id: Number(
    input.closest("[data-bundle-group]").dataset.bundleGroup,
  ),
}));
```

For a direct checkout integration, the Add Items contract accepts `bundled_items` on each parent
item. Every child requires `variant_id` and `quantity`. A child can also carry
`product_bundle_group_id`, `subscription`, and `subscription_plan_id`. See the generated Checkout
endpoints reference for the full contract.

## Add the bundle with FairShare

The FairShare SDK uses its own cart lifecycle. Pass the child selections through
`addCartItems()` with the parent bundle variant:

```javascript theme={null}
await window.FairShareSDK.addCartItems([
  {
    variant_id: 278318,
    quantity: 1,
    bundled_items: bundledItems,
  },
]);
```

The SDK forwards the nested child objects at runtime. Its published TypeScript type includes
`variant_id`, `quantity`, `display_to_customer`, `subscription`, and `subscription_plan_id`, but
may not yet declare `product_bundle_group_id`. JavaScript callers can send the group identifier.
TypeScript callers may need a local type extension until the SDK declaration catches up.

See [Cart API](/sdk/cart-api) for both `addCartItems()` call forms and subscription examples.

## Test the workflow

Test a bundle with minimum and maximum choices, subscriptions, and more than one group. Confirm
that your selector produces the intended child variants and group identifiers before you test the
complete cart flow.
