Need to create rules based on bundles?
Learn more
LogoLogo
APIsChangelog
  • Getting started
  • Rules
  • Conditions
    • Scope
    • Aggregations
    • Nested
    • Dynamic values
  • Actions
    • Types
      • Percentage
      • Fixed amount
      • Fixed price
      • Buy X pay Y
      • Every X discount Y
    • Bundle
      • Balanced
      • Every
    • Aggregation
    • Limit
  • Matchers
  • Operators
  • Check and validation
  • Core API integration
    • Order rules
    • Price rules
    • Virtual relationships
  • Resources
    • Promotions
    • Price lists
  • Use cases
    • Promotions
      • Discount line items based on item's price
      • Get a discount when paying by credit card
      • Discount an order based on promo item and total number of items
      • Offer a specific shipping method for free in a specific country
      • Discount items with large stock availability
      • Discount all the SKU in an order based on the shipping country
      • Discount all the SKU in an order based on the customer email domain
    • Price lists
      • Discount all prices greater than or equal to a specific value
      • Discount specific SKUs for new clients
      • Change strike-through price based on customer email domain
On this page
  • Example
  • Available actions
  • Check and validation
  • Examples
  • Use cases
  1. Resources

Promotions

How to set up promotions with the Rules Engine using order rules

PreviousVirtual relationshipsNextPrice lists

Last updated 2 months ago

The Rule Engine enables you to set up almost any kind of promotions to boost your sales, from the simplest to the most complex, using the proper .

To make the Rule Engine work in conjunction with promotions, you need to leverage Commerce Layer . All you need to do is a new flex promotion and give it a meaningful name, specifying the activation and expiration dates as the values of the related attributes and the order rules that you want to be implemented in the rules object. The discount defined by the related actions will be automatically applied to the orders that match the given conditions.

Example

The following request creates a flex promotion that applies a 10% discount to the whole order if it contains at least 2 units of a promotional product:

curl -g -X POST \
  'https://yourdomain.commercelayer.io/api/flex_promotions' \
  -H 'Accept: application/vnd.api+json' \
  -H 'Authorization: Bearer your-access-token' \
  -H 'Content-Type: application/vnd.api+json' \
  -d '{
  "data": {
    "type": "flex_promotions",
    "attributes": {
      "name": "Personal promotion",
      "starts_at": "2018-01-01T12:00:00.000Z",
      "expires_at": "2018-01-02T12:00:00.000Z",
      "rules": {
        "rules": [
          {
            "name": "10% Promo T-shirt x2",
            "conditions": [
              {
                "field": "order.line_items.sku.code",
                "matcher": "eq",
                "value": "PROMOTSHIRT",
                "nested": {
                  "conditions": [
                    {
                      "field": "order.line_items.quantity",
                      "matcher": "gteq",
                      "value": 2
                    }
                  ]
                }
              }
            ],
            "actions": [
              {
                "type": "percentage",
                "selector": "order",
                "value": "0.1"
              }
            ]
          }
        ]
      }
    }
  }
}'

Available actions

Check and validation

https://{{your_domain}}.commercelayer.io/api/flex_promotions/{{promo_id}}/check/{{order_id}}

Where:

  • {{your_domain}} is the unique subdomain of a Commerce Layer organization for which the Rules Engine is enabled.

  • {{promo_id}} is the ID of the flex promotion whose outcomes you want to check.

  • {{order_id}} is the ID of the order against which you want to validate the flex promotion.

Examples

Matching order

Let's assume you want to check the flex promotion identified by the ID ZOqmZfjPOp against the order identified by the ID NZrQhpRpRZ:

The following flex contains a rule according to which the orders with a total amount greater than 5000 cents will be discounted by 10%:

{
  "data": {
    "id": "ZOqmZfjPOp",
    "type": "flex_promotions",
    "links": { ... },
    "attributes": {
      "name": "Discount 10% if total greater than 5000 cents",
      "type": "flex_promotions",
      "exclusive": false,
      "priority": null,
      "starts_at": "2025-01-14T14:24:14.975Z",
      "expires_at": "2025-12-14T14:10:51.660Z",
      "total_usage_limit": null,
      "total_usage_count": 0,
      "active": true,
      "status": "active",
      "rules": {
        "rules": [
          {
            "id": "b0dd0bbf-7938-3d99-f556-14ba2b67c5fe",
            "name": "Discount 10% if total greater than 5000 cents",
            "conditions": [
              {
                "field": "order.total_amount_cents",
                "group": "bc285595-bae4-53fd-7471-41ef3048c220",
                "value": 5000,
                "matcher": "gt"
              }
            ],            
            "actions": [
              {
                "type": "percentage",
                "value": 0.1,
                "groups": [ "bc285595-bae4-53fd-7471-41ef3048c220" ],
                "selector": "order"
              }
            ]
          }
        ]
      },
      "disabled_at": null,
      "created_at": "2025-01-14T14:24:15.234Z",
      "updated_at": "2025-01-14T14:40:50.884Z",
      "reference": null,
      "reference_origin": null,
      "rule_outcomes": {},
      "resource_payload": {},
      "metadata": {}
    },
    "relationships": { ... },
    "meta": { ... }
  }
}

The following order has an amount greater than 5000 cents, thus matches the rule's conditions:

{
  "data": {
    "id": "NZrQhpRpRZ",
    "type": "orders",
    "links": { ... },
    "attributes": {
      "number": 2596335,
      "affiliate_code": null,
      "autorefresh": true,
      "place_async": false,
      "status": "pending",
      "payment_status": "unpaid",
      "fulfillment_status": "unfulfilled",
      "guest": true,
      "editable": true,
      "customer_email": "jane@example.com",
      "language_code": "en",
      "currency_code": "USD",
      "tax_included": true,
      "tax_rate": null,
      "freight_taxable": null,
      "payment_method_taxable": null,
      "adjustment_taxable": null,
      "gift_card_taxable": null,
      "requires_billing_info": false,
      "country_code": null,
      "shipping_country_code_lock": null,
      "coupon_code": null,
      "gift_card_code": null,
      "gift_card_or_coupon_code": null,
      "subtotal_amount_cents": 20100,
      "subtotal_amount_float": 201.0,
      "formatted_subtotal_amount": "$201.00",
      "shipping_amount_cents": 0,
      "shipping_amount_float": 0.0,
      "formatted_shipping_amount": "$0.00",
      "payment_method_amount_cents": 0,
      "payment_method_amount_float": 0.0,
      "formatted_payment_method_amount": "$0.00",
      "discount_amount_cents": -2010,
      "discount_amount_float": -20.1,
      "formatted_discount_amount": "-$20.10",
      "adjustment_amount_cents": 0,
      "adjustment_amount_float": 0.0,
      "formatted_adjustment_amount": "$0.00",
      "gift_card_amount_cents": 0,
      "gift_card_amount_float": 0.0,
      "formatted_gift_card_amount": "$0.00",
      "total_tax_amount_cents": 0,
      "total_tax_amount_float": 0.0,
      "formatted_total_tax_amount": "$0.00",
      "subtotal_tax_amount_cents": 0,
      "subtotal_tax_amount_float": 0.0,
      "formatted_subtotal_tax_amount": "$0.00",
      "shipping_tax_amount_cents": 0,
      "shipping_tax_amount_float": 0.0,
      "formatted_shipping_tax_amount": "$0.00",
      "payment_method_tax_amount_cents": 0,
      "payment_method_tax_amount_float": 0.0,
      "formatted_payment_method_tax_amount": "$0.00",
      "adjustment_tax_amount_cents": 0,
      "adjustment_tax_amount_float": 0.0,
      "formatted_adjustment_tax_amount": "$0.00",
      "total_amount_cents": 18090,
      "total_amount_float": 180.9,
      "formatted_total_amount": "$180.90",
      "total_taxable_amount_cents": 20100,
      "total_taxable_amount_float": 201.0,
      "formatted_total_taxable_amount": "$201.00",
      "subtotal_taxable_amount_cents": 20100,
      "subtotal_taxable_amount_float": 201.0,
      "formatted_subtotal_taxable_amount": "$201.00",
      "shipping_taxable_amount_cents": 0,
      "shipping_taxable_amount_float": 0.0,
      "formatted_shipping_taxable_amount": "$0.00",
      "payment_method_taxable_amount_cents": 0,
      "payment_method_taxable_amount_float": 0.0,
      "formatted_payment_method_taxable_amount": "$0.00",
      "adjustment_taxable_amount_cents": 0,
      "adjustment_taxable_amount_float": 0.0,
      "formatted_adjustment_taxable_amount": "$0.00",
      "total_amount_with_taxes_cents": 18090,
      "total_amount_with_taxes_float": 180.9,
      "formatted_total_amount_with_taxes": "$180.90",
      "fees_amount_cents": 0,
      "fees_amount_float": 0.0,
      "formatted_fees_amount": "$0.00",
      "duty_amount_cents": null,
      "duty_amount_float": null,
      "formatted_duty_amount": null,
      "place_total_amount_cents": null,
      "place_total_amount_float": null,
      "formatted_place_total_amount": null,
      "skus_count": 3,
      "line_item_options_count": 0,
      "shipments_count": 0,
      "tax_calculations_count": 0,
      "validations_count": 0,
      "errors_count": 0,
      "payment_source_details": null,
      "token": "123xy080e1d6b1340805e9a1c883d2b2",
      "cart_url": null,
      "return_url": null,
      "privacy_url": null,
      "checkout_url": null,
      "placed_at": null,
      "approved_at": null,
      "cancelled_at": null,
      "payment_updated_at": null,
      "fulfillment_updated_at": null,
      "refreshed_at": null,
      "archived_at": null,
      "subscription_created_at": null,
      "circuit_state": null,
      "circuit_failure_count": null,
      "created_at": "2025-01-14T14:42:08.059Z",
      "updated_at": "2025-01-14T14:45:33.019Z",
      "reference": null,
      "reference_origin": null,
      "metadata": {}
    },
    "relationships": { ... },
    "meta": { ... }
  }
}

Let's call the validation endpoint to check the rule's outcomes:

The following request validates the rule identified by the ID ZOqmZfjPOp against the order identified by the ID NZrQhpRpRZ:

curl -g -X GET \
  'http://yourdomain.commercelayer.io/api/flex_promotions/ZOqmZfjPOp/check/NZrQhpRpRZ' \
  -H 'Authorization: Bearer your-access-token' \
  -H 'Accept: application/vnd.api+json'

The outcomes payload clearly shows that the order matches the rule, listing the order ID conditions.matches array and displaying the action that will be applied on the matching order within the actions.resources array:

{
  "data": [
    {
      "id": "b0dd0bbf-7938-3d99-f556-14ba2b67c5fe",
      "name": "Discount 10% if total greater than 5000 cents",
      "priority": 0,
      "match": true,
      "conditions_logic": "and",
      "conditions": [
        {
          "field": "order.total_amount_cents",
          "group": "bc285595-bae4-53fd-7471-41ef3048c220",
          "value": 5000,
          "matcher": "gt",
          "match": true,
          "matches": [ 
            { 
              "order": "NZrQhpRpRZ",
              "group": "bc285595-bae4-53fd-7471-41ef3048c220"
            } 
          ],
          "scope": "any"
        }
      ],
      "actions": [
        {
          "resources": [
            {
              "resource_type": "orders",
              "id": "NZrQhpRpRZ",
              "group": "bc285595-bae4-53fd-7471-41ef3048c220",
              "quantity": null,
              "value": 0.1,
              "action_type": "percentage"
            }
          ]
        }
      ]
    }
  ]
}

Non-matching order

Let's assume you want to check the same flex promotion against the order identified by the ID PgojhLQLQR instead:

The following flex contains a rule according to which the orders with a total amount greater than 5000 cents will be discounted by 10%:

{
  "data": {
    "id": "ZOqmZfjPOp",
    "type": "flex_promotions",
    "links": { ... },
    "attributes": {
      "name": "Discount 10% if total greater than 5000 cents",
      "type": "flex_promotions",
      "exclusive": false,
      "priority": null,
      "starts_at": "2025-01-14T14:24:14.975Z",
      "expires_at": "2025-12-14T14:10:51.660Z",
      "total_usage_limit": null,
      "total_usage_count": 0,
      "active": true,
      "status": "active",
      "rules": {
        "rules": [
          {
            "id": "b0dd0bbf-7938-3d99-f556-14ba2b67c5fe",
            "name": "Discount 10% if total greater than 5000 cents",
            "conditions": [
              {
                "field": "order.total_amount_cents",
                "group": "bc285595-bae4-53fd-7471-41ef3048c220",
                "value": 5000,
                "matcher": "gt"
              }
            ],            
            "actions": [
              {
                "type": "percentage",
                "value": 0.1,
                "groups": [ "bc285595-bae4-53fd-7471-41ef3048c220" ],
                "selector": "order"
              }
            ]
          }
        ]
      },
      "disabled_at": null,
      "created_at": "2025-01-14T14:24:15.234Z",
      "updated_at": "2025-01-14T14:40:50.884Z",
      "reference": null,
      "reference_origin": null,
      "rule_outcomes": {},
      "resource_payload": {},
      "metadata": {}
    },
    "relationships": { ... },
    "meta": { ... }
  }
}

The following order has an amount lower than 5000 cents, thus doesn't match the rule's conditions:

{
  "data": {
    "id": "PgojhLQLQR",
    "type": "orders",
    "links": { ... },
    "attributes": {
      "number": 2345678,
      "affiliate_code": null,
      "autorefresh": true,
      "place_async": false,
      "status": "pending",
      "payment_status": "unpaid",
      "fulfillment_status": "unfulfilled",
      "guest": true,
      "editable": true,
      "customer_email": "john@example.com",
      "language_code": "en",
      "currency_code": "USD",
      "tax_included": true,
      "tax_rate": null,
      "freight_taxable": null,
      "payment_method_taxable": null,
      "adjustment_taxable": null,
      "gift_card_taxable": null,
      "requires_billing_info": false,
      "country_code": null,
      "shipping_country_code_lock": null,
      "coupon_code": null,
      "gift_card_code": null,
      "gift_card_or_coupon_code": null,
      "subtotal_amount_cents": 3000,
      "subtotal_amount_float": 30.0,
      "formatted_subtotal_amount": "$30.00",
      "shipping_amount_cents": 0,
      "shipping_amount_float": 0.0,
      "formatted_shipping_amount": "$0.00",
      "payment_method_amount_cents": 0,
      "payment_method_amount_float": 0.0,
      "formatted_payment_method_amount": "$0.00",
      "discount_amount_cents": 0,
      "discount_amount_float": 0.0,
      "formatted_discount_amount": "$0.00",
      "adjustment_amount_cents": 0,
      "adjustment_amount_float": 0.0,
      "formatted_adjustment_amount": "$0.00",
      "gift_card_amount_cents": 0,
      "gift_card_amount_float": 0.0,
      "formatted_gift_card_amount": "$0.00",
      "total_tax_amount_cents": 0,
      "total_tax_amount_float": 0.0,
      "formatted_total_tax_amount": "$0.00",
      "subtotal_tax_amount_cents": 0,
      "subtotal_tax_amount_float": 0.0,
      "formatted_subtotal_tax_amount": "$0.00",
      "shipping_tax_amount_cents": 0,
      "shipping_tax_amount_float": 0.0,
      "formatted_shipping_tax_amount": "$0.00",
      "payment_method_tax_amount_cents": 0,
      "payment_method_tax_amount_float": 0.0,
      "formatted_payment_method_tax_amount": "$0.00",
      "adjustment_tax_amount_cents": 0,
      "adjustment_tax_amount_float": 0.0,
      "formatted_adjustment_tax_amount": "$0.00",
      "total_amount_cents": 3000,
      "total_amount_float": 30.0,
      "formatted_total_amount": "$30.00",
      "total_taxable_amount_cents": 3000,
      "total_taxable_amount_float": 30.0,
      "formatted_total_taxable_amount": "$30.00",
      "subtotal_taxable_amount_cents": 3000,
      "subtotal_taxable_amount_float": 30.0,
      "formatted_subtotal_taxable_amount": "$30.00",
      "shipping_taxable_amount_cents": 0,
      "shipping_taxable_amount_float": 0.0,
      "formatted_shipping_taxable_amount": "$0.00",
      "payment_method_taxable_amount_cents": 0,
      "payment_method_taxable_amount_float": 0.0,
      "formatted_payment_method_taxable_amount": "$0.00",
      "adjustment_taxable_amount_cents": 0,
      "adjustment_taxable_amount_float": 0.0,
      "formatted_adjustment_taxable_amount": "$0.00",
      "total_amount_with_taxes_cents": 3000,
      "total_amount_with_taxes_float": 30.0,
      "formatted_total_amount_with_taxes": "$30.00",
      "fees_amount_cents": 0,
      "fees_amount_float": 0.0,
      "formatted_fees_amount": "$0.00",
      "duty_amount_cents": null,
      "duty_amount_float": null,
      "formatted_duty_amount": null,
      "place_total_amount_cents": null,
      "place_total_amount_float": null,
      "formatted_place_total_amount": null,
      "skus_count": 2,
      "line_item_options_count": 0,
      "shipments_count": 0,
      "tax_calculations_count": 0,
      "validations_count": 0,
      "errors_count": 0,
      "payment_source_details": null,
      "token": "a22478a9225aa3bdb5fbb9f9e21bbc6d",
      "cart_url": null,
      "return_url": null,
      "terms_url": null,
      "privacy_url": null,
      "checkout_url": null,
      "placed_at": null,
      "approved_at": null,
      "cancelled_at": null,
      "payment_updated_at": null,
      "fulfillment_updated_at": null,
      "refreshed_at": null,
      "archived_at": null,
      "subscription_created_at": null,
      "circuit_state": null,
      "circuit_failure_count": null,
      "created_at": "2025-01-14T14:43:26.947Z",
      "updated_at": "2025-01-14T14:44:45.220Z",
      "reference": null,
      "reference_origin": null,
      "metadata": {}
    },
    "relationships": { ... },
    "versions": { ... },
    "meta": { ... }
  }
}

Let's call the validation endpoint to check the rule's outcomes:

The following request validates the rule identified by the ID ZOqmZfjPOp against the order identified by the ID PgojhLQLQR:

curl -g -X GET \
  'http://yourdomain.commercelayer.io/api/flex_promotions/ZOqmZfjPOp/check/PgojhLQLQR' \
  -H 'Authorization: Bearer your-access-token' \
  -H 'Accept: application/vnd.api+json'

The outcomes payload clearly shows that the order doesn't match the rule, resulting in empty conditions.matches array — consequently, the actions array is also empty and the promotions won't be applied:

{
  "data": [
    {
      "id": "b0dd0bbf-7938-3d99-f556-14ba2b67c5fe",
      "name": "Discount 10% if total greater than 5000 cents",
      "priority": 0,
      "match": false,
      "conditions_logic": "and",
      "conditions": [
        {
          "field": "order.total_amount_cents",
          "group": "bc285595-bae4-53fd-7471-41ef3048c220",
          "value": 5000,
          "matcher": "gt",
          "match": false,
          "matches": [],
          "scope": "any"
        }
      ],
      "actions": []
    }
  ]
}

Use cases

The following are enabled on the flex promotions endpoint:

Once you've created a flex promotion (and defined one or more order rules) you can validate it against a specific order to check the rules outcomes (e.g. if and how the related discount will be applied in the case in point). You just need the flex promotion and the order IDs so that you can send (using a or access token) a GET request to the endpoint:

If you need to check more examples of promotions based on the Rule Engine, feel free to explore the of this documentation:

action types
Percentage
Fixed price
Fixed amount
Buy X pay Y
Every X discount Y
related section
Promotions
order rules
sales channel
integration
flex promotions
create