Overview
A promotion is a campaign: a percentage off, free shipping, or free items in the cart. Rules decide when it applies, actions decide what it does. A promotion is a rule that exists on its own, set up in advance and lasting across many orders. What lands on any one order is a Discount — the record of money actually coming off. The promotion is the campaign; the discount is what a particular customer got. Promotions can be activated in two ways:- Automatic promotions — applied on their own when the rules are met (e.g. free shipping on orders over $50)
- Coupon code promotions — applied when a customer enters a valid code during checkout
When promotions compete
Several promotions can qualify for the same item at once. Spree applies only the one that saves the customer the most, and records just that one. Losing candidates aren’t stored or tracked — they’re simply reconsidered on every cart change. So a promotion that loses today can win tomorrow, when the basket changes, without any bookkeeping to keep straight. What an applied promotion leaves behind on the order is a Discount — a permanent record that survives the promotion being edited or deleted.Promotion Attributes
Multi-Code Promotions
For promotions that need unique codes per customer (e.g. influencer campaigns), Spree supports bulk code generation:
Generated codes follow the pattern
{prefix}{random}, e.g. SUMMER22A0F62A230BD919.
Rules
Rules decide when a promotion is eligible. You can combine multiple rules and configure whether all must match or any is enough (viamatch_policy).
You can also build your own rules for business-specific conditions.
Actions
Actions define what happens when a promotion applies.Order discount (CreateAdjustment)
A discount on the whole order. The amount is distributed proportionally across the line items — there is no single order-level row, so per-item reporting and partial returns always know their share.
Default calculator: FlatPercentItemTotal (percentage off the order). Also available: FlatRate, FlexiRate, TieredFlatRate, TieredPercent.
Use case: “10% off your order”, “$20 off orders over $100”.
Item discount (CreateItemAdjustments)
A discount on individual line items. Only items that match the promotion’s rules receive it — if a Category rule says “Electronics”, only electronics are discounted.
Default calculator: PercentOnLineItem (percentage off each item).
Use case: “15% off shoes”, “Buy 2+ shirts get 10% off each”.
Free shipping (FreeShipping)
Writes a discount on each fulfillment covering its delivery cost. The row is kept even when the cost is zero — its presence is what marks the order as having free shipping.
Use case: “Free shipping on orders over $75”, “Free shipping with code FREESHIP”.
Free items (CreateLineItems)
Adds specified products to the cart when the promotion is eligible, checking stock first, and discounts them to zero. A customer who already has the gift in their cart gets theirs free rather than a duplicate; any further copies they bought stay paid for. The gift is taken back when the promotion stops applying.
No second action is needed to make the gift free — pairing this with an order discount set to the gift’s price discounts it twice.
Use case: “Free gift with purchase”, “Spend $100 get a free sample”.
Managing Promotions
Create and manage promotions via the Admin API. Rules and actions can be supplied inline on create — each is a{ type, preferences } draft using the rule and action types described above:
/api/v3/admin/promotion_rules/types and /api/v3/admin/promotion_actions/types, together with their preference schemas. The dashboard’s promotion editor is built on these endpoints, so custom types show up there without any UI work.
Deleting a promotion doesn’t disturb the orders that used it. Their discount rows stay, keeping the code and value they were created with — only the link back to the promotion is cleared.
Coupon Codes
Coupon codes track promotion usage and can be single-use or multi-use.- Single code — set the
codeattribute on the promotion;usage_limitcontrols how many times it can be redeemed. - Multi-code — bulk-generated codes each track their own state (
unused/used). A code is used up only when an order is placed with it. Applying it to a cart just holds it there, and a shopper who presents a code held by an abandoned cart takes it over, so an unfinished checkout never burns a code.
Promotion Flow
- Cart change — every change to a cart (item added, address entered, code applied) triggers a recalculation
- Eligibility — each connected promotion is checked: active dates, usage limits, then rules per
match_policy - Competition — candidate discounts are computed for every eligible promotion; per item, per fulfillment, and order-wide, the largest saving wins
- Persistence — winning discounts are written as rows; anything stale from the previous pass is removed; tax is then estimated on the discounted amounts
- Placement — once the order is placed, its discount rows are frozen; usage counts are recorded against the promotion
Related Documentation
- Discounts — the rows promotions write, and manual discounts
- Build Custom Promotion Rules & Actions — step-by-step guide to custom rules, actions and adjusters
- Calculators — promotion calculators
- Carts — the cart lifecycle promotions act on
- Admin SDK Resources — the
client.promotions.*resource-client pattern used above

