---
title: "About Promotions v2"
source: https://support.storeconnect.com/articles/advanced-promotions
type: article
format: markdown
site: StoreConnect Support — product and developer documentation for StoreConnect
site_index: https://storeconnect.com/llms.txt
docs_index: https://support.storeconnect.com/llms.txt
note: Append .md to any page or article URL on this site to get its Markdown form.
---
# About Promotions v2

Promotions v2 separates the question of when an offer applies from what it gives. That split is what makes offers possible that [standard promotions](promotions) cannot express: spend thresholds, tiers, free products, happy-hour windows, and promotions that need no code at all.

StoreConnect version 21 introduced this engine, alongside the original one described in [Promotions](promotions). To set one up, see [Configure Promotions v2](configure-advanced-promotions). For ready-made recipes, see [Promotions v2 examples](advanced-promotion-examples).

## How a Promotion v2 is assembled

Four records work together, and a fifth is optional.

| Record | Purpose |
|---|---|
| **Condition** | What must be true, such as a cart reaching $50 |
| **Reward** | What the customer gets, such as 10% off |
| **Promotion v2** | The header holding scheduling, scoping, stacking, and the coupon code |
| **Promotion v2 Condition** | Joins one condition to one reward under a promotion, with a priority |
| **Promotion v2 Product Scope** | Optional. Limits a condition or reward to given products, categories, brands, or shipping zones |

Conditions and rewards are reusable, so the same "cart reaches $100" condition can drive several promotions. The join record is what actually makes a promotion fire, and a promotion with no join record does nothing.

## Compared with standard promotions

| Capability | Standard promotions | Promotions v2 |
|---|---|---|
| Coupon-code promotions | Yes | Yes |
| Automatic promotions, no code | No | Yes |
| Cart conditions, such as spend or item count | No | Yes |
| Scheduling | Date range only | Dates, times, daily windows, days of week, weekly recurrence |
| Multiple reward tiers | No | Yes, tiered or stacked |
| Stacking between promotions | Always combines | Configurable per promotion |
| Channel scoping | No | Store or outlet |
| Customer scoping | Contact, account, or membership, with multiple combinations per promotion | Contact, account, or membership, one value per promotion |
| Usage limits | Total only | Total and per customer |
| Codes per cart | One | Set by `promotions.max_coupon_codes_per_cart` |
| Usage tracking | Promotion Credits | Promotion 2 Usage, Reward Usage, and Order Item Reward |

## Conditions

A condition decides whether its reward applies. Set the threshold in the condition's **Amount** field where one is needed.

| Type | Applies when |
|---|---|
| `always_applies` | Always. Use it when the only restriction is on the promotion itself |
| `total_value` | The value of eligible cart items reaches **Amount** |
| `total_quantity` | The quantity of eligible items reaches **Amount** |
| `product_count` | The number of distinct eligible products reaches **Amount** |
| `liquid_condition` | A Liquid expression evaluates as true |

Attach **Promotion v2 Product Scope** records to a condition to filter the cart before the condition is measured. With scopes attached, only matching items count toward the amount or quantity. A shipping zone scope instead restricts the condition to carts delivering into that zone.

The **Currency** field limits a condition to carts in one currency. Leave it blank to apply to all of them.

### Liquid conditions

A `liquid_condition` reads **Liquid Condition (Web)** on the storefront and **Liquid Condition (POS)** at the register. Each non-blank line is evaluated as its own test and every line must be true. A blank template passes, and an expression that errors fails the condition.

Three variables are available: `condition`, `cart`, and `cart_items`, where `cart_items` holds the items left after product scoping.

This qualifies only carts shipping to Australia that hold at least three eligible items:

```liquid

cart.shipping_country_code == "AU"
cart_items.size >= 3
```

:::warning
These variables are the underlying Salesforce records, not the storefront Liquid objects of the same name, so the attribute names are different and the [Cart Liquid object reference](cart-liquid-object-reference) does not apply here. Writing `cart.sub_total` or `cart.shipping_address.state`, which are storefront attributes, silently fails the condition rather than reporting an error.
:::

Attributes confirmed to work on `cart` are `shipping_street`, `shipping_city`, `shipping_state`, `shipping_state_code`, `shipping_postal_code`, `shipping_country`, `shipping_country_code`, the matching `billing_` fields, `currencyisocode`, and `total`. On each entry in `cart_items` you can use `quantity`, `unit_price`, and `total_price`. Use `cart_items.size` for a count.

Reach for a Liquid condition only when a built-in condition type cannot express the rule. A spend threshold is better set with `total_value` and an item count with `total_quantity`, both of which respect product scoping without any code.

:::note
Each line is wrapped in an `{% if %}` test, so write a comparison rather than a tag or an output statement.
:::

## Rewards

| Type | What it does |
|---|---|
| `discount_on_subtotal` | Takes a percentage or fixed amount off the cart subtotal |
| `discount_on_products` | Takes a percentage or fixed amount off matched or specified products |
| `discounted_shipping` | Reduces shipping. Set **Discount Percentage** to `100` for free shipping |
| `free_product` | Adds free items, chosen by product scopes with scope type `free_product` |
| `fixed_price_for_n_products` | Sets **Fixed Amount** as the price for **Quantity** matching items |

Product-level rewards take four more settings: **Apply to** chooses between the items the condition matched and the reward's own scoped items; **Apply first to** discounts the most or least expensive first; **Max Eligible** caps how many items are discounted; and **Exclude Discounted** and **Exclude Free** skip items that already carry a discount or already cost nothing.

**Frequency** decides how often a reward repeats. `once` applies it a single time. `no_limit` repeats it for each qualifying group, which is how "10% off for every three items" is built.

## Reward strategy within a promotion

**Reward Strategy** on the promotion header controls what happens when several of its conditions match at once.

| Strategy | Behavior |
|---|---|
| Tiered | Only the matching condition with the lowest priority number applies. This is the default and is what spend-and-save tiers need |
| Stacked | Every matching condition applies its reward |

## Stacking between promotions

Several promotions can be active at once, and these settings decide whether they combine.

| Field | Value | Behavior |
|---|---|---|
| **Stacking Behaviour** | `stackable` | Combines with other promotions. This is the default |
| **Stacking Behaviour** | `not_stackable` | Conflicts with any other promotion in the cart |
| **Stacking Priority** | `existing_promotions` | On a conflict, keep what is already applied and reject the new one |
| **Stacking Priority** | `biggest_reward` | On a conflict, keep whichever gives the larger discount |
| **Stop After This Promo** | Checked | Stop evaluating further promotions once this one applies |

Promotions are evaluated in ascending **Priority**, so lower numbers run first. Where a conflict involves a promotion set to `not_stackable` with `existing_promotions`, the incoming promotion is rejected outright.

The store variable `promotions.calculation_strategy` decides how combined discounts are calculated: `independent`, the default, works from the original item prices, while `cumulative` works from the running subtotal after earlier discounts. See [Store variables](store-variables).

## Scheduling

| Field | Description |
|---|---|
| **Start Date** | First date the promotion is active. A promotion with no start date never applies |
| **End Date** | Last active date. Leave blank to run indefinitely |
| **Start Time**, **End Time** | Time of day on the start and end dates only, not a daily window |
| **From Time**, **To Time** | The daily window, applied every active day |
| **Days of Week** | Day numbers where `0` is Sunday and `6` is Saturday |
| **Week Frequency** | Recurrence in weeks from the start date, so `2` means every second week |
| **Timezone** | Timezone for every time field. Defaults to the store's timezone |

## Scoping

**Channel scope** restricts a promotion to one **Channel Scope Store** or one **Channel Scope Outlet**. The two are mutually exclusive, and leaving both blank allows the promotion on every channel.

**Channel Scope Outlet** matches whichever outlet the order belongs to. That is the outlet a shopper has selected on the web, for collection or local delivery, and the register's own outlet at the point of sale. It is not a way to limit a promotion to the point of sale.

## Coupon codes and usage limits

Leave **Coupon Code** blank for a promotion that applies automatically. Populate it to require the customer to type the code at checkout. Code matching is not case sensitive.

**Total Usage Limit** caps uses across all customers and **Per Customer Usage Limit** caps them per customer. Leave either blank for no limit. Both are counted from the **Promotion 2 Usage** records written when an order is placed.

## What the customer sees

Automatic promotions apply as soon as the cart qualifies, with no customer action, and appear in the cart totals. They cannot be removed by the customer, and they withdraw on their own when the cart stops qualifying.

Coupon-code promotions appear under **Applied codes** in the checkout **Promo Codes** panel, and the customer can remove them. Automatic promotions are listed separately under **Auto-Applied Discounts**.

A free product reward adds its item to the cart at no charge, as its own line. If the cart stops qualifying, the free line is removed.

:::note
The store variable `promotions.max_coupon_codes_per_cart` caps how many codes one cart accepts. When it is not set, there is no limit.
:::

## How Promotions v2 and standard promotions interact

Both engines evaluate independently, so a cart can receive discounts from a standard promotion and a Promotion v2 at the same time. To prevent overlap, scope your Promotions v2 records by channel or customer, or move your offers to one engine entirely.

:::warning
Products excluded from promotions are skipped only by standard promotions. The **Promotions** value of **Exclude From Price Reductions** has no effect on Promotions v2, so use a **Promotion v2 Product Scope** with scope type `exclude` to keep a product out of one. See [Exclude products from price reductions](exclude-products-from-price-reductions).
:::

---

## Follow StoreConnect

- [Email Newsletter](https://storeconnect.com/c/lp-newsletter)
- [LinkedIn Newsletter](https://www.linkedin.com/build-relation/newsletter-follow?entityUrn=7444956928444862464)
- [YouTube](https://www.youtube.com/channel/UCngKdP2x8l1wcbAKW3tvU8g)
- [LinkedIn](https://www.linkedin.com/company/storeconnect)
- [X / Twitter](https://x.com/storeconnecthq)

## Popular Links

- [Partners](https://storeconnect.com/partners)
- [Become a Partner](https://storeconnect.com/become-a-partner)
- [News](https://storeconnect.com/articles/news)
- [Events](https://storeconnect.com/articles/events)
- [Live Events](https://storeconnect.com/live-events)
- [Feature Comparison](https://storeconnect.com/how-we-compare)
- [Download a free trial](https://appexchange.salesforce.com/appxListingDetail?listingId=a0N3A00000FMkeKUAT)
- [Book a Demo](https://storeconnect.com/contact)

## Documentation

- [Help documentation](https://support.storeconnect.com/help-documentation)
- [AI agents](https://support.storeconnect.com/ai)
- [Videos & tutorials](https://support.storeconnect.com/videos-tutorials)
- [Developer reference](https://support.storeconnect.com/developer-reference)
- [Release notes](https://support.storeconnect.com/release-notes)
- [Troubleshooting](https://support.storeconnect.com/troubleshooting)
- [Trust Center](https://trust.getstoreconnect.com/)
- [Status Page](https://status.storeconnect.com/)

## Contact

- info@getstoreconnect.com
- US +1 415 745 3230
- AUS +61 2 8365 2308

100 S Ashley Dr, Suite 600-2461
Tampa FL 33602-600 USA

Level 22, Sydney Place
180 George Street
Sydney, NSW, 2000, AUS

## Machine-readable

- [Site index for agents](https://storeconnect.com/llms.txt): curated map of the StoreConnect site in llms.txt format
- [Documentation index for agents](https://support.storeconnect.com/llms.txt): full technical and product documentation map

Every page and article on this site has a Markdown rendering: append `.md` to its URL.

Continue in Markdown: [Help documentation](https://support.storeconnect.com/help-documentation.md) · [Developer reference](https://support.storeconnect.com/developer-reference.md) · [Videos & tutorials](https://support.storeconnect.com/videos-tutorials.md) · [Release notes](https://support.storeconnect.com/release-notes.md)

---

StoreConnect Support — https://support.storeconnect.com/articles/advanced-promotions