Rules-Based Promotion Expressions

Suggest an edit
Todd Menier

Published by Todd Menier

September 20th, 2018

Writing custom rule-base expressions for your order promotions may seem intimidating at first, but once you get the hang of it you'll realize there are virtually limitless possibilities which provide the flexibility to meet almost any business need. In fact, custom order promotions are a great example of how our core value Flexibility over Features informs everything we build (and don't build) into the platform. We've found that allowing developers to define their own custom if-then statements is incredibly robust and solves more problems than providing a limited set of non-customizable promotions out-of-the-box.

Promotions Overview

Every promotion requires two rule expressions, an EligibleExpression, which evaluates the current state of the order and returns true or false indicating whether the promotion can be applied to the order, and a ValueExpression, which evaluates the order and returns a monetary value, which is then subtracted from the order subtotal.

There are several properties that you can include in both expressions to build your custom promotion: order supports the same properties as the Order model returned from v1/orders API endpoints, including xp. items supports the following functions:

  • items.any() (true if any item on the order matches filter)
  • items.all() (true if all items match filter)
  • items.quantity()(returns sum of line item quantities matching your specified condition)
  • items.count()(returns number of line items on the order matching your specified condition)
  • items.total() (compare result to a dollar amount)

The following operators are all supported in rule engine expressions:

  • Comparison: = , < , >, <=, >=
  • Logical: and, or and not
  • Mathematical:+, -,*, / and %

Additionally, you can wrap any of your expressions in these functions:

  • min(...) (returns the smaller of the two arguments)
  • max(...) (returns the larger of the two arguments)

Examples

$10 off any order greater than $50:
EligibleExpression: "order.Subtotal > 50"
ValueExpression: "10"

Free Shipping when you spend $60:
EligibleExpression: "order.Subtotal >= 60"
ValueExpression: "order.ShippingCost"

BOGO (limited to 1 free item):
EligibleExpression: "items.quantity(ProductID = 'ABC') > 1"
ValueExpression: "items.total(ProductID = 'ABC') / items.quantity(ProductID = 'ABC')"

$5 off the order total when any line item has a given product ID:
EligibleExpression: "items.any(ProductID = '123')"
ValueExpression: "5"

15% off all line items with a product assigned to a given CategoryID:
EligibleExpression: "item.product.incategory('Bikes')"
ValueExpression: "item.LineSubtotal * .15"

10% off when all products are on sale with a maximum promotion discount of $20 (utilizing product xp):
EligibleExpression: "items.all(Product.xp.OnSale = true)"
ValueExpression: "min(order.Subtotal * .1, 20)"

30% off when you buy 10 or more products assigned to a given CategoryID:
EligibleExpression: "items.quantity(product.incategory('GuitarAccessories')) >= 10"
ValueExpression: "items.total(product.incategory('GuitarAccessories')) * .3"

20% off when you buy these 2 products together (no quantity limit):
EligibleExpression: "items.any(ProductID = 'ABC') and items.any(ProductID = 'XYZ')"
ValueExpression: "(items.total(ProductID = 'ABC') + items.total(ProductID = 'XYZ')) * .2"

10% off your entire order when you spend more than $200 in these categories:
EligibleExpression: "items.total(product.incategory('Kitchen')) + items.total(product.incategory('Bedding')) + items.total(product.incategory('Bathroom')) > 200"
ValueExpression: "order.Subtotal * .1"

$50 off line items from a given supplier when you spend more than $100 on that supplier's products:
EligibleExpression: "item.SupplierID = '123' and items.total(SupplierID = '123') >= 100"
ValueExpression: "50 / items.count(SupplierID = '123')"

25% off a user's first order (utilizing user xp):
EligibleExpression: "order.FromUser.xp.FirstOrder = true"
ValueExpression: "order.Subtotal * .25"

BOGO (scales with quantity):
EligibleExpression: "items.quantity(ProductID = 'XYZ') > 1"
ValueExpression: "((items.quantity(ProductID='XYZ')/2) - (items.quantity(ProductID='XYZ') % 2 * .5)) * items.total (ProductID='XYZ') / items.quantity(ProductID='XYZ')"

That's a lot of flexibility. What else should I know?

Considerations

  • Both EligibleExpression and ValueExpression are limited to 400 characters each. Use them wisely. If you're bumping up against the character limit, there's a good chance you could optimize your promo expressions in some way.
  • Promotion eligibility is evaluated by the api at the time the promotion is applied, and then again on order submit.

Enhancements Roadmap

  • Line item level promotions (released in v1.0.158)
  • v1/orders/{direction}/{orderID}/validate endpoint that will check validity of an order in it's current state, including, but not limited to, applied promotions (released in v1.0.131)
  • Support for min(...) and max(...) functions (released in v1.0.165)
  • product.inparentcategory() - the current function product.incategory() only recognizes direct assignments, the new function will look for products assigned to any child categories of the parent that is passed in.

Other Resources