How to build a subscription integration with Simple Bundles app?

Subscription apps (Recharge, Loop, Seal, Appstle, Skio, Ordergroove and others) can integrate with Simple Bundles by reading two things Shopify already stores: variant metafields, which hold each bundle's configuration, and line item properties, which hold the customer's selection. If your app carries the bundle line's properties from the first order onto every renewal, Simple Bundles breaks each renewal down into component SKUs automatically. If your customer portal can also rewrite those properties, subscribers can change their bundle mix for future deliveries without the merchant touching a single order.

This guide is for subscription app developers, agencies, and technical merchants. Merchants setting up subscriptions for the first time should start with How to create bundles with a subscription plan like Recharge, Seal, and Appstle.

In this article

How bundle data flows from first purchase to renewal breakdown
The selection travels on the bundle line as line item properties. On renewal, Simple Bundles reads it back and adds the component lines.

How does Simple Bundles process subscription renewals?

On the first purchase, the storefront bundle widget writes the customer's selection onto the bundle line as line item properties. At checkout, the Simple Bundles cart transform expands one-time bundle purchases into component lines.

Shopify's cart transform does not run on selling plan (subscription) lines, and renewal orders never pass through a cart. A renewal order arrives with the bundle parent line intact at quantity 1, plus whatever line item properties your app copied onto it. When Simple Bundles receives the orders/create  webhook, it:

  1. Recognizes the line as a bundle by its variant ID. The variant has to be a bundle in the merchant's Simple Bundles catalog; properties alone don't make a line a bundle.
  2. Reads the bundle's stored configuration and the line's properties to work out the customer's selection.
  3. Adds the component lines to the order with a Shopify order edit. Each component gets a 100% line discount (description Simple Bundles: <bundle title> | ID: <line id> ), so the order total doesn't change.
  4. Tags the order Simple Bundles 2.0 - Bundle Order  and, if the merchant has order metafields turned on, writes simple_bundles.bundled_line_items .

The parent line stays on the order for pricing and accounting. Fulfillment apps and 3PLs pick the component SKUs.

If Shopify refuses the order edit (it sometimes rejects edits "related to a recurring selling plan"), Simple Bundles skips that line instead of failing the order. Merchants can turn on the associated order fallback, which creates a duplicate order containing the components, or re-run processing with the Shopify Flow action Process Bundle Order. See Handling orders that cannot be edited by Shopify.

Where does Simple Bundles store bundle data?

Where Simple Bundles stores bundle configuration and selections
Configuration lives on the bundle variant. The selection lives on the line.

Bundle variant metafields (the bundle's configuration)

Any app with the read_products  scope can read these:

Metafield Contents
simple_bundles.bundled_variants The component list. Each component has variant_id , product_id , sku , variant_title , product_title , price , quantity_in_bundle , inventory_item_id , plus variant_options  (infinite options bundles) and selection_token  (build-your-own bundles). This is the public source of truth for a bundle's contents.
simple-bundles.byob_validations Pack rules for build-your-own bundles, stored on each pack variant. Note the hyphen in the namespace.
simple_bundles.variant_options_v2 Option groups for infinite options bundles, for example [[{"optionName": "Flavor 1", "optionValues": "Berry,Citrus"}]] .

To check whether a subscription line is a bundle, look for a simple_bundles.bundled_variants  metafield on its variant.

Metafields in the $app:simple_bundles  namespace are owned by Simple Bundles and power the cart transform. Shopify doesn't let other apps read them, so don't build against them.

Line item properties (the customer's selection)

These sit on the bundle parent line and should be carried from cart to order to subscription contract line:

Property Used for
Visible option properties, for example Flavor 1: Berry Drive the breakdown for infinite options bundles. Matched case-insensitively against each component's variant_options .
_bundle_selection Drives the breakdown for build-your-own bundles and is the cart transform's input at checkout.

Component lines expanded at checkout may also carry _sb_bundle_title , _sb_bundle_group  and _sb_bundle_variant_id_qty  when the merchant turns on cart transform bundle line item properties. Simple Bundles regenerates these, so they don't belong on a subscription contract line.

What's on the order after breakdown?

  • The order tag Simple Bundles 2.0 - Bundle Order  (and Simple Bundles - Associated Order  when the fallback is used).
  • Component lines at a 100% discount.
  • Optionally, the simple_bundles.bundled_line_items  order metafield: one JSON row per component, mapping the parent line (bundle_line_item_id , bundle_line_item_sku , bundle_line_item_properties  and so on) to its component lines. See Enabling bundle details in Shopify order metafields.

How is the customer's selection encoded?

How a bundle selection is encoded in line item properties
BYOB selections use tokens. Infinite options selections use visible properties.

Build-your-own bundles

Every component has a stable selection_token , published in simple_bundles.bundled_variants  and in the API. You don't need to compute it yourself.

_bundle_selection  lists one token per slot, with duplicates repeated, joined by space, <> , space. A customer who picks 2 Berry Boxes (token a1b2c3d4 ) and 1 Citrus Box (token 9f8e7d6c ) has:

_bundle_selection = "a1b2c3d4 <> a1b2c3d4 <> 9f8e7d6c"

On breakdown, each component's quantity is quantity_in_bundle × parent quantity × token count . Unknown tokens are dropped silently, so always build tokens from current metafield or API data.

Infinite options bundles

The selection is stored twice, and both copies need to stay in sync:

  • One visible property per option group, such as Flavor 1: Berry  and Flavor 2: Citrus . These are what the renewal breakdown matches against.
  • _bundle_selection , with the chosen values joined by <>  between option groups (and ++  between values inside a multi-select group), for example Berry <> Citrus .

Fixed bundles

Fixed bundles have no selection. Components come from the bundle's configuration and properties are ignored, so the only change a portal can make is swapping to a different bundle variant.

How do I build "edit my bundle" in a customer portal?

The selection on a subscription only stays fixed because nothing updates the contract line's properties after the first purchase. A portal that rewrites them changes every future renewal.

  1. Detect bundles: the line's variant has a simple_bundles.bundled_variants  metafield.
  2. Render the picker. For BYOB, list the components from bundled_variants  and read the pack size and section limits from simple-bundles.byob_validations . For infinite options, show one selector per option group from simple_bundles.variant_options_v2 . Pre-fill from the line's current properties.
  3. Validate the new selection against the pack rules (next section). Simple Bundles doesn't validate selections on renewal, so your portal needs to.
  4. Write the new selection to the subscription contract line's properties (Recharge line item properties , Loop line attributes, or your platform's equivalent). For BYOB, rebuild _bundle_selection . For infinite options, rewrite both the visible properties and _bundle_selection .
  5. Keep the parent line at quantity 1. A bigger box is a different pack variant (for example 3-Pack instead of 2-Pack), so swap the variant and update the properties instead of raising the quantity.

That's all. The next renewal carries the new properties and Simple Bundles breaks it down into the new mix. There's nothing to call on the Simple Bundles side.

Example edit your bundle screen in a subscription customer portal
Example "Edit your bundle" screen in a subscription portal, with the pack minimum enforced.

Renewal pricing comes from the subscription contract line, not from the selection. Changing the mix inside one BYOB pack variant doesn't change the price. If the customer changes pack size, or the bundle uses custom per-selection pricing, update the line's variant or price following your platform's rules.

How do I enforce pack rules?

Build-your-own pack variants store their rules as JSON in simple-bundles.byob_validations . Each pack size has its own rules:

{
  "rules": [
    { "type": "itemCount",  "count": 3 },
    { "type": "perItemMax", "max": 2 },
    { "type": "groupRange", "section": "Snacks", "min": 2, "max": 4,
      "message": "Pick 2-4 snacks" },
    { "type": "itemMax", "productId": "10240566853952", "max": 1 }
  ]
}
Rule Meaning
itemCount Total picks required. Defaults to the pack's own item count when missing.
perItemMax Maximum picks of any one variant. 1  means no duplicates.
groupRange Minimum and maximum picks within one section, by section name.
itemMax Cap for one specific variantId  or productId  (numeric IDs).
message Optional shopper-facing error text.

A merchant's "at least 2 boxes" minimum is the itemCount  of their smallest pack variant. Offering a bigger box means offering the next pack variant. For more on how BYOB works on the storefront, see How to create a BYOB experience.

Can I use the Simple Bundles API instead of metafields?

Yes, for reading bundle configuration server-side. The API needs Simple Bundles 2.0 on a paid plan, and the merchant creates a token under Settings → API tokens.

  • Base URL https://api.simplebundles.io , auth header Authorization: Bearer <token> , 60 requests per minute.
  • GET /api/v1/bundles  lists bundles, with type , status  and updated_at_min /updated_at_max  filters.
  • GET /api/v1/bundles/{shopify_variant_id}  returns one bundle, including selectors  (infinite options) and bundled_variants  with selection_token  (BYOB).

There's no endpoint that validates a selection or expands it into components, so read the configuration and validate in your app. Full reference: How to use Simple Bundles API.

What's different on Simple Bundles 1.0?

Renewals work the same way on both versions: Simple Bundles breaks the parent line down with an order edit after the order is created. On 1.0 the first purchase is also broken down after checkout, because there's no cart transform. The other differences:

  • The order tag is Simple Bundles - Bundle Order .
  • Infinite options groups live in simple_bundles.variant_options  (a flat list) instead of variant_options_v2 .
  • There are no build-your-own bundles, no byob_validations  metafield, and no public API. Read configuration from simple_bundles.bundled_variants .

FAQ

Can subscribers change their bundle today without a portal integration?

Only if the merchant edits each renewal order by hand, or edits the subscription line's properties in the subscription app's admin. The portal integration in this guide automates that second option.

Would switching from Recharge to Loop (or another app) fix renewals?

No. Every subscription app uses the same mechanism: a contract line that carries line item properties onto each renewal order. What matters is that the properties survive onto every renewal, which mainstream subscription apps do by default.

Can I add component lines with the selling plan at the cart instead?

Yes, some merchants expand bundles into components at add-to-cart so each component carries the selling plan. See Breaking down bundles at the cart level when using Loop and other subscription apps. With that setup the subscription contract holds components instead of a bundle parent, and the editing approach in this guide doesn't apply.

Does Simple Bundles validate the selection on renewal?

No. Your portal needs to enforce pack rules before saving. Tokens or option values that don't match the bundle are dropped at breakdown.

Will this change once Shopify supports cart transform on subscriptions?

Shopify is working on native subscription support for cart transform bundles, which should let component lines flow through subscription contracts directly. Until then, the property-based integration in this guide is the supported path.

Building an integration and have questions? Email us at hello@simplebundles.io.

Still need help? Contact Us Contact Us