The properties
The line item properties Bundle Builder writes on every component line — what each one holds, when it's there, and which ones your templates should read.
Grouping in Liquid works because every product added from a builder carries identifying properties. They're written when the shopper adds the box to the cart, and every value is plain text. Bundle SKU is the only one a shopper can see; every other name starts with an underscore. This page is the reference; the template pages use them.
The ones you should read
| Property | Example | What it's for |
|---|---|---|
Bundle SKU | BOX-COFFEE-6 | Visible, human-facing. Your parent product's SKU. Only present when the setting is on and the parent product has a SKU. |
_bundle_id | bundle-65f1c2a4b3d8e9a1f2c3d4e5-1730000000001 | The grouping key. The word bundle, the builder's ID, then the moment the box was added to the cart (a millisecond timestamp). Unique per box the shopper built — two boxes from the same builder in one order get two different ids, so they render as two groups. Present on every line, in both cart line modes. |
_bundle_builder_id | 65f1c2a4b3d8e9a1f2c3d4e5 | Which builder the box came from. It's the long ID at the end of the builder's address in the app (…/app/bundle-builder-set/<id>), not the Unique ID shown on the Settings tab. Use it if you want different markup per builder. |
_slot_index | 0, 1, 2 … | Position in the box. It's also what keeps two slots that hold the same product as two separate lines. |
The ones that may or may not be there
These exist for the app's own pricing and merging logic, and which ones a line carries depends on the builder's Show products as individual lines in the cart setting. Read them only as fallbacks, and always guard with != blank.
| Property | When it's present |
|---|---|
_oxify_builder | Individual cart-line mode only. A compact JSON string with the box's id, the discount type and value, and the bundle name (the Bundle Name from General Settings) — for example {"id":"bundle-65f1c2a4b3d8e9a1f2c3d4e5-1730000000001","dt":"percentage","dv":"10","n":"Coffee Box"}. A q key (the item count the discount was earned at) is added when Allow customers to add more items than the selected quantity is on. |
_bundle_cart_mode | Individual mode only — the value is individual. |
_bundle_discount | Merged mode, current format: the discount type and value joined by a colon, e.g. percentage:50, fixed-price:49.99 or no-discount:0. |
_bundle_discount_type, _bundle_discount_value, _bundle_name, _bundle_image_url, _bundle_parent_variant | Merged mode, legacy format only — older orders, and builders whose settings hadn't yet synced to the Cart Transform. Don't rely on _bundle_name or _bundle_image_url being there; fall back to the first component's image. |
Underscored properties are hidden from customers, not from you
Shopify's checkout doesn't show _-prefixed properties, and themes such as Dawn skip them when they list properties, but they are always visible on the admin order page. That's a Shopify behaviour with no way to turn it off — it's why the app writes as few of them as it can. Any loop you write yourself over line_item.properties has to skip them too (see below).
Getting the bundle's name
No single property always holds the name. A merged box ordered with the current format doesn't carry it on the line at all — the name lives in the app's Cart Transform configuration instead — so templates resolve it in this order:
Native group title. On packing slips, a merged bundle exposes line_item.groups; the non-deliverable group's title is the bundle's name.
_oxify_builder. In individual mode, split the JSON string on "n":" and take what's before the next quote — Liquid can't parse JSON, so string-splitting is the only option. (A name that contains a double quote or a backslash comes out cut short or doubled.)
_bundle_name. Present on legacy merged-mode lines only.
A literal fallback, like Bundle.
Each template page implements this chain for you — you don't have to write it yourself.
In the email, Order Printer and theme snippets, a merged box ordered today ends at the fallback. To show its real name in an email, on an invoice or in your theme, map _bundle_builder_id to a name yourself. The builder's ID is the last part of its address in the app. Paste this right after the snippet's _bundle_name lookup, inside the same if block, and use your snippet's own variable names (line and bundle_name in emails, line_item and b_name in Order Printer):
Reading a property in Liquid
Two syntaxes, both valid. Use the bracket form when the name has a space, like Bundle SKU:
To print a row's properties yourself, skip the ones you've already displayed in the header, and every property whose name starts with an underscore. A loop over line_item.properties includes those — Dawn filters them with this same first-character check:
Next
Packing slips — the full worked example.