Skip to main content
A volume discount bundle rewards customers for buying more: the discount grows as they cross quantity or spend thresholds. For example, “Buy 3, save 10% · Buy 6, save 20%”. Appstle applies the best matching tier automatically. This guide covers running a volume discount bundle from your own storefront UI. You use the same metafield, cart-line attributes, and Shopify Function that Appstle’s built-in widget uses — only the UI is yours. Read the headless overview first for the automatic-discount model.
bundleTypeVOLUME_DISCOUNT
discountTypeTIERED_DISCOUNT
Read fromappstle_bundles.bundle_rules shop metafield
Discount applied byAppstle automatic discount (Shopify Function)

How pricing works

The tiers live in the rule’s tieredDiscount field as a JSON-encoded string. Parse it into an array of tier objects:
Tier fieldTypeMeaning
discountBasedOnstringQUANTITY (threshold on item count) or AMOUNT (threshold on spend, in the store currency’s major unit).
valueintegerThe threshold to reach this tier (e.g. 3 items, or 50 for $50 spend).
discountnumberThe discount magnitude.
discountTypestringPERCENTAGE or FIXED_AMOUNT.
titleLabel / subtitleLabel / badgeLabelstringOptional display labels you can reuse in your UI.
Tier selection — Appstle evaluates every qualifying tier and applies the one with the highest discount:
  1. Among QUANTITY tiers, keep those where cartQuantity ≥ value.
  2. Among AMOUNT tiers, keep those where subtotal ≥ value.
  3. From all qualifying tiers, apply the one with the largest discount.

Step 1 — Read the rule and parse the tiers

Read the active rules from the appstle_bundles.bundle_rules shop metafield — the Shopify-native source, with no call to Appstle’s servers (see Reading bundle configuration). Filter to volume discount bundles by bundleType, then parse the tieredDiscount JSON string.
{% assign bundle_rules = shop.metafields.appstle_bundles.bundle_rules.value %}
<script>window.MY_BUNDLE_RULES = {{ bundle_rules | json }};</script>
const rules = window.MY_BUNDLE_RULES || [];
const [rule] = rules.filter(
  r => r.bundleType === 'VOLUME_DISCOUNT' && r.status === 'ACTIVE'
);

// tieredDiscount is a JSON string inside the rule — parse it.
const tiers = JSON.parse(rule.tieredDiscount || '[]');
// [{ discountBasedOn: "QUANTITY", value: 3, discount: 10, discountType: "PERCENTAGE" }, ...]
Fields you need:
FieldUse
uniqueRefAttach to every cart line as _appstle-bb-id.
nameDisplay label; attach as __appstle-bb-name.
tieredDiscountJSON string of tier objects (parse it).
products / variantsEligible products and variants (JSON).

Step 2 — Render the tier ladder

Build your own UI from the parsed tiers:
  • Show the ladder (e.g. “Buy 3 → 10% off · Buy 6 → 20% off”) using value, discount, and the optional labels.
  • Track the running quantity and subtotal, highlight the tier the customer currently qualifies for, and nudge them toward the next one.

Step 3 — Add to cart

Add the eligible variants with the bundle attributes. For volume discount, _appstle_bundles_type is VOLUME_DISCOUNT.
const line = (variantId, quantity) => ({
  id: variantId,
  quantity,
  properties: {
    '_appstle-bb-id': rule.uniqueRef,
    '_appstle_bundles_type': 'VOLUME_DISCOUNT',
    '__appstle-bb-name': rule.name,
  },
});

await fetch(`${Shopify.routes.root}cart/add.js`, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ items: selection.map(s => line(s.variantId, s.quantity)) }),
});
mutation AddVolumeBundle($cartId: ID!) {
  cartLinesAdd(
    cartId: $cartId
    lines: [
      {
        merchandiseId: "gid://shopify/ProductVariant/123"
        quantity: 6
        attributes: [
          { key: "_appstle-bb-id", value: "summer-set-ref" }
          { key: "_appstle_bundles_type", value: "VOLUME_DISCOUNT" }
          { key: "__appstle-bb-name", value: "Summer Skincare Set" }
        ]
      }
    ]
  ) {
    cart { id }
    userErrors { field message }
  }
}

Tier restriction (optional)

When the merchant enables tier restriction, the discount is pinned to one explicitly chosen tier rather than auto-selecting the best qualifying one. In that mode, also attach the selected tier’s identifier:
AttributeValue
_appstle_bundle_combo_idThe identifier of the tier the customer selected.
properties: {
  '_appstle-bb-id': rule.uniqueRef,
  '_appstle_bundles_type': 'VOLUME_DISCOUNT',
  '__appstle-bb-name': rule.name,
  '_appstle_bundle_combo_id': selectedTierId,  // only when tier restriction is on
}
Only attach _appstle_bundle_combo_id when tier restriction is enabled for the bundle. Without restriction, omit it and the Function applies the best qualifying tier automatically.

Step 4 — Discount applies automatically

Appstle’s automatic discount evaluates the qualifying tiers against the cart and applies the best one (or the pinned tier, under restriction) in cart and checkout. As the customer adds or removes quantity, the applied tier updates automatically — no calls from your side.

Previewing the discount

Replicate the best-tier selection for a live preview (cart prices in cents):
function previewVolumeDiscount(tiers, quantity, subtotalCents) {
  const subtotalMajor = subtotalCents / 100;

  const qualifying = tiers.filter(t =>
    (t.discountBasedOn === 'QUANTITY' && quantity >= t.value) ||
    (t.discountBasedOn === 'AMOUNT' && subtotalMajor >= t.value)
  );
  if (qualifying.length === 0) return { discountCents: 0, totalCents: subtotalCents, tier: null };

  // Apply the tier with the largest discount.
  const best = qualifying.reduce((a, b) => (b.discount > a.discount ? b : a));

  const discountCents =
    best.discountType === 'PERCENTAGE'
      ? Math.round(subtotalCents * (best.discount / 100))
      : Math.min(Math.round(best.discount * 100), subtotalCents);

  return { discountCents, totalCents: subtotalCents - discountCents, tier: best };
}
This preview is display-only. The Shopify Function applies the authoritative discount at checkout from the same tiers, so they agree as long as you read tieredDiscount from the live rule.

Checklist

1

Bundle is Active in the Appstle admin

2

tieredDiscount parsed from its JSON string

3

Every line carries _appstle-bb-id and _appstle_bundles_type = VOLUME_DISCOUNT

4

_appstle_bundle_combo_id attached only when tier restriction is enabled

5

Verify the applied tier in the cart matches your preview

Next steps

Fixed pricing bundle

Sell a curated set for one fixed price.

Dynamic pricing bundle

Build-your-own with a percentage or amount discount.