Skip to main content
Bundles are available on request. Get in touch and we will enable it for your store.

Set up bundles

1

Navigate to Products → Bundles

In your Supercycle dashboard, go to Products and select Bundles.
2

Create a new bundle

Click Create bundle and give it a name.
3

Add component products

Add each product that makes up the bundle. Before adding a component, ensure it has the relevant methods enabled in Supercycle. The methods configured on each component determine which options are available when creating intents at checkout.
4

Save the bundle

Save the bundle. Supercycle will tag the parent product as Supercycle bundle product and each component with Bundle component: <parent-product-handle>, and attach the supercycle.bundle metafield to the parent.

Bundle metafields

Parent bundle products will have the tag Supercycle bundle product and a supercycle.bundle metafield:
{
  "components": [
    {
      "quantity": 1,
      "product": {
        "shopifyId": 10149040324891,
        "handle": "slim-fit-suit-jacket",
        "title": "Slim Fit Suit Jacket"
      }
    },
    {
      "quantity": 1,
      "product": {
        "shopifyId": 10149040324892,
        "handle": "slim-fit-suit-trousers",
        "title": "Slim Fit Suit Trousers"
      }
    },
    {
      "quantity": 1,
      "product": {
        "shopifyId": 10149040324893,
        "handle": "slim-fit-suit-waistcoat",
        "title": "Slim Fit Suit Waistcoat"
      }
    }
  ]
}
Component products will have the tag Bundle component: <parent-product-handle>. You can access each component product’s full Shopify object in Liquid:
{{ all_products['<product-handle>'] }}
Each component product has a configuration metafield for every method enabled on it:
MethodConfiguration metafield
Calendarproduct.metafields.supercycle.calendar_configuration
Membershipproduct.metafields.supercycle.membership_configuration
Subscriptionproduct.metafields.supercycle.subscription_configuration
Resaleproduct.metafields.supercycle.resale_configuration
You can iterate over all components and read the relevant configuration:
{% for component in product.metafields.supercycle.bundle.components %}
  {% assign component_product = all_products[component.product.handle] %}
  {{ component_product.metafields.supercycle.calendar_configuration }}
{% endfor %}
Each configuration metafield contains an options array with a global_id for each option. For example, a calendar configuration:
{
  "rental_periods": [
    {
      "global_id": "gid://supercycle/CalendarRental::RentalPeriod/1",
      "name": "3 days"
    },
    {
      "global_id": "gid://supercycle/CalendarRental::RentalPeriod/2",
      "name": "4 days"
    }
  ],
  "fixed_fees": []
}
See Metafields for the full schema of each configuration metafield.

Build a frontend experience

Each component product must be added to the cart as its own line item with the correct Supercycle attributes. The steps below walk through checking availability, creating intents, and adding all components to the cart in one request.
1

Check availability

Use the Product availability API to confirm all component products are available for the selected dates.Extract component IDs from the bundle metafield in Liquid:
{% assign component_productIds = product.metafields.supercycle.bundle.components | map: "product.shopifyId" | join: "," %}
Then check availability:
const component_productIds = [{{ component_productIds }}];

const availability = await fetch("/apps/supercycle/product_availability_checks", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    productIds: component_productIds,
    rentalStart: "2025-01-01",
  }),
}).then((res) => res.json());
2

Create an intent for each component

Each component product needs an intent created via the Intent API. The intent returns an attributes object containing everything Supercycle needs to process the line item as a cycle, including _cycle, _validations, and selling_plan.You will need to build a UI that lets the customer select an option for each component product, for example a dropdown of rental periods per item. Each option has a global_id in the component’s configuration metafield, which is what you pass to the intent endpoint:
async function createIntent(variantId, optionGlobalId, rentalStart) {
  return fetch("/apps/supercycle/intents", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      variantId,
      option: {
        globalId: optionGlobalId,
        params: { rentalStart },
      },
    }),
  }).then((res) => res.json());
}
Call this for every component before adding to the cart:
const intent = await createIntent(
  variantId,
  selectedOptionGlobalId,
  "2025-01-01",
);
// intent.attributes contains selling_plan, _cycle, _validations, etc.
3

Add all components to the cart

Once you have an intent for each component, add all variants to the cart in a single request using Shopify’s multiple items cart API.Use FormData and set all intent.attributes keys directly. Each attribute key is already the correct form field name:
const components = [
  {
    variantId: 12345678901,
    optionGlobalId: "gid://supercycle/...",
    quantity: 1,
  },
  {
    variantId: 12345678902,
    optionGlobalId: "gid://supercycle/...",
    quantity: 1,
  },
  {
    variantId: 12345678903,
    optionGlobalId: "gid://supercycle/...",
    quantity: 1,
  },
];

const rentalStart = "2025-01-01";
const formData = new FormData();

await Promise.all(
  components.map(async ({ variantId, optionGlobalId, quantity }, index) => {
    const intent = await createIntent(variantId, optionGlobalId, rentalStart);

    formData.set(`items[${index}][id]`, variantId);
    formData.set(`items[${index}][quantity]`, quantity);

    Object.entries(intent.attributes).forEach(([key, value]) => {
      formData.set(`items[${index}][${key}]`, value);
    });
  }),
);

await fetch("/cart/add.js", { method: "POST", body: formData });