About Promotions v2
On this page
Promotions v2 separates the question of when an offer applies from what it gives. That split is what makes offers possible that standard 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. To set one up, see Configure Promotions v2. For ready-made recipes, see Promotions v2 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 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.
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.
:::
Was this article helpful?
Thanks for your feedback! It helps us improve our docs.