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

# Volume discount bundle with a custom storefront

> Run tiered 'buy more, save more' discounts based on quantity or spend, using your own storefront UI and Appstle's automatic discount.

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](/bundles/headless-overview) first for the automatic-discount model.

|                         |                                               |
| ----------------------- | --------------------------------------------- |
| **`bundleType`**        | `VOLUME_DISCOUNT`                             |
| **`discountType`**      | `TIERED_DISCOUNT`                             |
| **Read from**           | `appstle_bundles.bundle_rules` shop metafield |
| **Discount applied by** | Appstle 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 field                                    | Type    | Meaning                                                                                                    |
| --------------------------------------------- | ------- | ---------------------------------------------------------------------------------------------------------- |
| `discountBasedOn`                             | string  | `QUANTITY` (threshold on item count) or `AMOUNT` (threshold on spend, in the store currency's major unit). |
| `value`                                       | integer | The threshold to reach this tier (e.g. `3` items, or `50` for \$50 spend).                                 |
| `discount`                                    | number  | The discount magnitude.                                                                                    |
| `discountType`                                | string  | `PERCENTAGE` or `FIXED_AMOUNT`.                                                                            |
| `titleLabel` / `subtitleLabel` / `badgeLabel` | string  | Optional 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](/bundles/headless-overview#reading-bundle-configuration)). Filter to volume discount bundles by `bundleType`, then parse the `tieredDiscount` JSON string.

```liquid theme={null}
{% assign bundle_rules = shop.metafields.appstle_bundles.bundle_rules.value %}
<script>window.MY_BUNDLE_RULES = {{ bundle_rules | json }};</script>
```

```javascript theme={null}
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:

| Field                   | Use                                            |
| ----------------------- | ---------------------------------------------- |
| `uniqueRef`             | Attach to every cart line as `_appstle-bb-id`. |
| `name`                  | Display label; attach as `__appstle-bb-name`.  |
| `tieredDiscount`        | JSON string of tier objects (parse it).        |
| `products` / `variants` | Eligible 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](/bundles/headless-overview#required-cart-line-attributes). For volume discount, `_appstle_bundles_type` is `VOLUME_DISCOUNT`.

<CodeGroup>
  ```js Shopify theme (Ajax Cart API) theme={null}
  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)) }),
  });
  ```

  ```graphql Headless (Storefront API) theme={null}
  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 }
    }
  }
  ```
</CodeGroup>

### 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:

| Attribute                  | Value                                             |
| -------------------------- | ------------------------------------------------- |
| `_appstle_bundle_combo_id` | The identifier of the tier the customer selected. |

```js theme={null}
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
}
```

<Note>
  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.
</Note>

## 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):

```js theme={null}
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 };
}
```

<Note>
  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.
</Note>

## Checklist

<Steps>
  <Step title="Bundle is Active in the Appstle admin" />

  <Step title="tieredDiscount parsed from its JSON string" />

  <Step title="Every line carries _appstle-bb-id and _appstle_bundles_type = VOLUME_DISCOUNT" />

  <Step title="_appstle_bundle_combo_id attached only when tier restriction is enabled" />

  <Step title="Verify the applied tier in the cart matches your preview" />
</Steps>

## Next steps

<CardGroup cols={2}>
  <Card title="Fixed pricing bundle" icon="box" href="/bundles/fixed-pricing-bundle">
    Sell a curated set for one fixed price.
  </Card>

  <Card title="Dynamic pricing bundle" icon="boxes-stacked" href="/bundles/dynamic-pricing-bundle">
    Build-your-own with a percentage or amount discount.
  </Card>
</CardGroup>
