docs
  1. SCAYLE Resource Center
  2. Developer Guides
  3. Pricing & Promotions
  4. Promotions

Promotions

Fundamentals of a promotion

Fundamentals of a promotion

  1. Promotions can be assigned to specific shops and are then only valid in these shops. Promotions can also be valid in several shops at the same time.
  2. Within one shop, a promotion can be configured to be valid in specific countries. Promotions can also be valid in multiple countries at the same time.
  3. Audiences are finely tuned customer segments that enable precise customer outreach. You can define for which audiences a promotion is valid. This is optional, a promotion can also be valid for all audiences.
  4. Each promotion has a set of basic configurations.

Promotion configuration

NameDescription
NameEach promotion has a specific name, that you can freely choose. The name is not unique, so multiple promotions can have the same name.
Display nameEach promotion can have a Display name. This is the name that will be displayed in the Shop, and therefore visible to the end user. If you do not set a name, the system will automatically set it to Null, and the shop will resolve this into a string, with the default translation of voucher. You can update this translation via your crowdin project under the string: fallbackCodeName
CombinableConfiguration which states whether the promotion can be combined with others and if so, with which ones. You can either include or exclude other promotions.
SchedulePeriod in which the promotion is valid.
StateA promotion can be either active or inactive. Promotions in the state inactivate are not returned by the Storefront API, and are also not valid when applying them to items in a basket/order.
PriorityPriority refers to the ranking of a promotion when compared to other promotions. Useful in the storefront, when running multiple promotions and you want to evaluate, which one has a higher priority by comparing.
Custom DataCustom data represents a JSON object, that you can use freely to persist any data that you like.
Supported EffectAutomatic Discount or Buy X Get Y.
Promotion ConditionsConditions are special rules that must be satisfied in order for the promotion to be applied. Read more about them below.

Promotion conditions

To each promotion, conditions can be assigned. Conditions are special rules that must be satisfied in order for the promotion to be applied. A distinction is made between two types of conditions:

Global conditions

Global conditions are checked against general customer- or basket/order data. The promotion is only considered valid if the global condition is met. If the global condition isn't met, the entire promotion is invalid, regardless of whether the item conditions are met. Typical examples for global conditions are:

  • Minimum order value is greater or equal than €100
  • Customer is in group “VIP”
  • Basket/order contains more than 3 items of a specific brand

Item Conditions

Item conditions are checked against the item you are trying to apply the promotion to. This is useful, when you want to limit the set of items, which can benefit from a promotion, for instance

  • 15% off of jeans
  • 10% off of all items with attribute group “deal”
  • Conditions can be connected by “and” or “or” operators as well.

By mix and matching global and item conditions, you can create specific promotions that serve your individual promotion cases.

The conditions are defined using the Google Common Expression Language.

The implementation is based on Go, you can find the language definition here. Cel-go has multiple extensions, currently we support the Math extension, which supports two additional functions: math.greatest() and math.least()

Note that the attributes on payload.customer.* are only validated in the Checkout, not the Storefront.

If you want to evaluate certain customer conditions, we suggest using the audiences-feature, as audiences are already validated in the Storefront.

Condition examples

payload.items.filter(it, it.price == math.greatest(payload.items.map(i, i.price)))[0].price == item.price
payload.items.filter(it, it.price == math.least(payload.items.map(i, i.price)))[0].price == item.price

Admin API

The Admin API offers several endpoints for managing promotions.

Create promotion

SCAYLE allows you to create promotions.

Method Signature

let response = await client.apis.Promotions.createPromotion({}, {requestBody: promotion});
let createdPromotion = response.body;

Options

The createPromotion method can be used with an optional query parameter:

ParameterDetails
with

String
The parameter allows you to include the following nested resources in the response:

  • customData

Request Body

Properties marked with a * are required.

PropertyDescription
name*

String

Defines the name of the promotion.

status*

String

Defines the status of the promotion.

Valid values are:

  • archived
    Promotion cannot be changed or applied.
  • inactive
    Promotion is disabled and cannot be applied.
  • active
    Promotion is enabled and can be changed and applied.
schedule*

Object

Defines the time range when a promotion is active.

See Schedule

companyIds*

Integer[]

A list of company ids where the promotion takes place.

shopCountryIds*

Object

Defines the shop countries in which the promotion will be available.

See ShopCountryIds

effect*Object

The effect that will be applied if all conditions are satisfied.

See Effects
activationType*

String

Defines the activation type of the promotion.

Valid values are:

  • automatic
    This promotion type does not require a customer to enter a code.
  • code
    This promotion type requires a customer to enter a code to apply the promotion (similar to a voucher).
level*

String

Defines the level on which the promotion is applied.

Valid values are:

  • item
    The promotion is applied on the basket item level.
  • basket
    The promotion is applied on the basket level (e.g. discount for the whole basket)
conditions*Object[]

Defines a list of conditions that must be satisfied for applying a promotion to the customers basket.

See Conditions
displayName

Object

Localization of the name to be displayed.

Example

{
  "de_DE": "Weihnachtsaktion",
  "en_GB": "Christmas promotion"
}
priorityInteger

Defines the priority of the promotion.
siblingPromotions

See Sibling Promotions
audiencesObject



See Audiences
customData

Object

A list of key-value pairs that are individually defined.

Example

{
  "legalText": "Supported neglected met she therefore unwilling discovery remainder. Way sentiments two indulgence uncommonly own."
}
tiersObject[]

Defines a list of tiers that allow configuring different effect values depending on the minimum order value.

See Tiers
usageLimitObject

Usage limit configuration

See Usage Limit

Schedule

PropertyDescription
from*

String

Defines the start date of the promotion.

Example:
2024-04-01T12:00:00+00:00

to*String

Defines the end date of the promotion.

Example:
2024-05-01T12:00:00+00:00

ShopCountryIds

The properties allowList and blockList are mutually exclusive, meaning, you cannot pass both at the same time. At the same time, one of both is required.

PropertyDescription
allowList

Integer[]

A list of shop country ids where the promotion is active.

blockListInteger[]

A list of shop county ids where the promotion is not active. This means, the promotion will be active in all other shop countries.

Effects

PropertyDescription
type*

String

Defines the type of the promotion.

Valid values are:

additionalData*Object

Supplementary data required to complete the effect definition.

See Buy X Get Y or Automatic Discount

Effect - Additional Data - Buy X Get Y

When the promotion conditions are satisfied, the customer will receive n items for free. (The list of which items will be given for free and how many can be defined during promotion creation).

PropertyDescription
variantIds*

Integer[]


A list of variant ids that can be given away for free in case of the promotion conditions being satisfied.

The customer will be able to pick n items from the list.

maxCount*

Integer

How many free items the customer is entitled for. This number is multiplicative on how many times the promotion conditions were satisfied.

Example

“For each blue shirt you buy, get 2 free socks”


The maxCount, for this scenario, would be 2.

maxCountType

String

Has to be configured together with eligibleItemsQuantity.

Valid values are:

  • empty string
  • per_eligible_unique_items
    Calculation of limit amount free items will count only unique items, but not the quantity of each unique item in basket.
  • per_eligible_items_quantity
    Calculation of limit free items will count the quantity of each item in basket that fits the condition.
eligibleItemsQuantityInteger

Has to be configured together with maxCountType.
Used for calculating of limit free items.

Effect - Additional Data - Automatic Discount

When the promotion conditions are satisfied, the customer will receive a discount on specific items or the whole basket.

PropertyDescription
type*

String

The type of the discount that will be applied.

Valid values are:

  • relative
  • absolute
    Example
    1200 => 12 EUR discount*
value*

Float

Integer value that depicts percentage or absolute amount - currency is considered from the shop settings.

Examples

relative

  • 1200 => 12.0%
  • 1050 => 10.5%

absolute

  • 1200 => 12.00 EUR*
  • 1050 => 10.50 EUR*

* The currency is inherited from the shop country the promotion is being applied to.

Conditions

Conditions are special rules that must be satisfied in order for the promotion to be applied. These rules must follow the Google Common Expression Language syntax.

Conditions can be applied on two different levels, global and item. Global conditions are applied only once per payload / basket, while item conditions are checked for each item in the basket. Anything other than item related conditions should be put into conditions on level global.

  • When only item level conditions are satisfied, effects are applied for each item.
  • When only global level conditions are satisfied, effects are applied on basket level.
  • When both, global and item level conditions, are satisfied, they are applied accordingly.
PropertyDescription
level*

String

Valid values are:

  • global
  • item
key*String

Key of the condition. Can be used to identify which condition failed in the validate endpoint.

Example
mov_100
condition*String

A Google Common Expression Language valid condition.

Example
payload.items.exists(i, 11003 in i.variant.id)

Sibling Promotions

Sibling promotions allow to create a new promotion combined with other promotions defined in siblingPromotions.

The properties allowList and blockList are mutually exclusive, meaning, you cannot pass both at the same time. At the same time, one of both is required.

PropertyDescription
level*

String

Allow users to define at which level the sibling promotion can be applied.

Valid values are:

  • basket
enabled*

Boolean

Allows users to enable or disable sibling promotions for a promotion.

  • If false, then a promotion can not be applied to other promotions.
  • If true, the allowList and blockList are combined with the current promotion
allowListString

The list of promotion ids that can be applied together with the promotion (enabled has to be true).
blockListString

The list of promotion ids that cannot be applied together with the promotion (enabled has to be true).

Audiences

Allows promotion engine users to tailor promotions to particular group of customers. An audience is a collection of customers grouped to be used effectively in/for a promotion.

Example

Black Friday Influencers , VIP_Members , High Value Customers, Trend Lovers

The properties allowList and blockList are mutually exclusive, meaning, you cannot pass both at the same time. At the same time, one of both is required.

PropertyDescription
allowList

String[]

The list of audience ids that can use this promotion. When included in the list, the promotion is only visible to customers that belong to an audience listed in the allowList.

blockListString[]

A list of audience ids that cannot use this promotion. When providing blockList, it is visible to all customers except the ones that are part of an audience that it listed in the blockList.

Tiers

Promotion tiers allow configuring different effect values depending on the MOV (Minimum Order Value). effect.additional_data can have different values for each individual tier.

Basically, current tier effect values are merged with promotion.effect. That means, it is not necessary to include all effect.additional_data values in the tier.effect, it is enough to add only values that are changed.

Example

  • when MOV reaches 100 EUR, customer gets 10% discount
  • when MOV reaches 150 EUR, customer gets 15% discount
PropertyDescription
name*

String

Defines the name of the tier.

effect*Object

The effect that will be applied if all conditions are satisfied.

See Tier Effect
mov*Integer

Minimal order value requirement for a tier. Value should be in a fractional currency (e.g. cents), if a currency has fractions.

Example
10000

Tier Effect

PropertyDescription
additionalData*Object

Supplementary data required to complete the effect definition.

See Buy X Get Y or Automatic Discount

Usage Limit

PropertyDescription
promotion*

Object


See Usage Limit - Promotion

promotionCodes*Object

See Usage Limit - Promotion Codes

Usage Limit Promotion

PropertyDescription
count*

Integer


Amount of promotion usages. Default: 0.


0 = unlimited promotion usages

Usage Limit Promotion Codes

PropertyDescription
count*

Integer


Amount of promotion code usages. Default: 0.


0 = unlimited promotion code usages

type*

String

Codes usage limit count type.

Valid values are:

  • order
  • customer

Example

let response = await adminApi.apis.Promotions.createPromotion({
    with: "customData"
}, {
    requestBody: {
        "name": "Christmas Promotion",
        "status": "active",
        "shopCountryIds": {
            "allowList": [139, 554]
        },
        "companyIds": [100],
        "schedule": {
            "from": "2023-05-10T10:00:00+01:00",
            "to": "2024-05-10T10:00:00+01:00"
        },
        "effect": {
            "type": "buy_x_get_y",
            "additionalData": {
                "variantIds": [12389244, 23985437],
                "maxCount": 2,
                "maxCountType": "per_eligible_items_quantity",
                "eligibleItemsQuantity": 1
            }
        },
        "activationType": "automatic",
        "level": "basket",
        "priority": 10,
        "displayName": {
            "de_DE": "Weihnachtsaktion",
            "en_US": "Christmas Promotion"
        },
        "customData": {
            "anything": "you want to provide"
        },
        "audiences": {
            "allowList": ["645e0c241a93369ff53f26e0"]
        },
        "siblingPromotions": {
            "enabled": true,
            "blockList": ["646235bf496f8f56171c3762"],
            "level": "basket"
        },
        "usageLimit": {
            "promotion": {
                "count": 10
            },
            "promotionCodes": {
                "count": 10,
                "type": "order"
            }
        },
        "conditions": [
            {
                "level": "global",
                "key": "mov_100",
                "condition": "payload.items.exists(i. 11003 in i.variant.id)"
            },
            {
                "level": "item",
                "key": "mov_100",
                "condition": "item.color == 'yellow'"
            }
        ]
    }
});

let createdPromotion = response.body;

console.log(createdPromotion.id);

Update promotion

Updating an existing promotion follows the same schema as for creating new promotions. Therefore, you can simply refer to the request body and schema above.

Method signature

let response = await client.apis.Promotions.updatePromotion(
    {promotionId: "promotionId"},
    {requestBody: promotion}
);
let updatedPromotion = response.body;

Request Body

Please refer to the create promotion request body.

Example

let promotion = {
    "name": "Christmas Promotion",
    "status": "active",
    "shopCountryIds": {
        "blockList": [ 139, 554 ]
    },
    "activationType": "code",
    "level": "basket",
    "companyIds": [ 100 ],
    "schedule": {
        "from": "2023-05-10T10:00:00+01:00",
        "to": "2024-05-10T10:00:00+01:00"
    },
    "effect": {
        "type": "buy_x_get_y",
        "additionalData": {
            "variantIds": [12389244, 23985437],
            "maxCount": 2,
            "maxCountType": "per_eligible_items_quantity",
            "eligibleItemsQuantity": 1
        }
    }
}

let response = await client.apis.Promotions.updatePromotion(
    {promotionId: "645e0c241a93369ff53f26e0", with: "customData"},
    {requestBody: promotion}
);
let updatedPromotion = response.body;

console.log(updatedPromotion.id);

List promotions

You can also request several promotions. We suggest to fine-tune your search, e.g. by combining filters and using pagination. As search results are paginated, you can set the amount of promotions displayed per page.

Method signature

let response = await adminApi.apis.Promotions.getPromotions(options);
let promotions = response.body.entities;

Options

The getPromotions endpoint can be used with the following optional query parameters:

ParameterDetails
filters[id]

String

Comma-separated list of IDs of entities that should be used for filtering.

Example

filters[id]=645e0c241a93369ff53f26e0,f0c07b9967fa64f3ab0bdec0
filters[name]

String

Entity name value.

Example

filters[name]=Christmas%20Promotion
filters[companyId]

String

Comma-separated list of company IDs that should be used for filtering.

Example

filters[companyId]=139,200
filters[activeAt]

String

When provided, only promotions that are active during the specified time will be returned.

Example

filters[activeAt]=2021-01-23T11%3A30%3A58%2B00%3A00
filters[shopCountryId]

String

Comma-separated list of shop country IDS that should be used for filtering.

Example

filters[shopCountryId]=1,2,3
filters[status]

String

Comma-separated list of statuses to filter for.

Valid values are:

  • archived
  • inactive
  • active


Example

filters[status]=active,inactive,archived
filters[effect]

String

Filter for a specific effect of promotions.

Valid values are:

  • buy_x_get_y
  • automatic_discount


Example

filters[effect]=automatic_discount
filters[audienceId]

String

Comma-separated list of audience IDs (strings) that should be used for filtering

Example

filters[audienceId]=48f75314ba216a213d32868e,02bc1c8cb9b7bcd39247ebd0
filters[activationType]

String

Filter for a specific activation type.

Valid values are:

  • automatic
  • code


Example

filters[activationType]=code
filters[promotionCodes]

String

Comma-separated list of promotion codes that should be used for filtering

Example

filters[promotionCodes]=d27d75a754bf13d014296633,9a22a66d2f1101cdb5b54502
with

String

The parameter allows you to include the following nested resources in the response:

  • customData

Example

with=customData
limitInteger

Maximum number of entities in the result. When fetching entities with children, the limit always applies to the amount of parent entities (max. 1000).
cursor

String

A valid cursor that can be used for pagination, base64-encoded.

Example

cursor=Mgo%3D

Example

let response = await adminApi.apis.Promotions.getPromotions({
    "filters[id]": ["645e0c241a93369ff53f26e0", "f0c07b9967fa64f3ab0bdec0"],
    "filters[name]": "Christmas Promotion",
    "filters[activeAt]": "2024-04-09T10:00:00+01:00",
    "filters[shopCountryId]": [1, 2, 3],
    "filters[companyId]": [100, 200],
    "filters[status]": ["active", "inactive", "archived"],
    "filters[effect]": "automatic_discount",
    "filters[audienceId]": ["48f75314ba216a213d32868e", "02bc1c8cb9b7bcd39247ebd0"],
    "filters[activationType]": "code",
    "filters[promotionCodes]": ["d27d75a754bf13d014296633", "9a22a66d2f1101cdb5b54502"],
    "cursor": "Mgo=",
    "limit": 100,
    "with": "customData"
});

let promotions = response.body.entities;

promotions.forEach(
    promotion => console.log(promotion.id)
);

Get promotion

SCAYLE allows you to retrieve a promotion by its ID.

Method signature

let response = await adminApi.apis.Promotions.getPromotion(options);
let promotions = response.body;

Options

The createPromotion endpoint can be used with an optional query parameter:

ParameterDetails
with

String

The parameter allows you to include the following nested resources in the response:

  • customData

Example

with=customData

Example

let response = await adminApi.apis.Promotions.getPromotion({
    promotionId: "645e0c241a93369ff53f26e0",
    with: "customData"
});

let promotions = response.body;

console.log(promotion.id);

Delete promotion

SCAYLE allows you to delete a promotion by its ID. The request returns a 204 No Content on success.

Method signature

adminApi.apis.Promotions.deletePromotion({
    promotionId: "promotionId"
});

Example

adminApi.apis.Promotions.deletePromotion({
    promotionId: "645e0c241a93369ff53f26e0"
});

Deprecated (will be removed in near future)

Use Admin API:

Required parameters

ParameterTypeExampleDefinition
namestringChristmas PromotionDefines the name of the promotion
schedule.fromstring2023-05-10T10:00:00+01:00Defines the start date of the promotion
schedule.tostring2024-05-10T10:00:00+01:00Defines the end date of the promotion
isActivebooleantrue
Not active promotions will not be applicable
shopIdarray[ ”10001”, ”10002” ]Defines in which applications the promotion will be available. You can add several applications IDs comma separated.
companyIdarray[ ”1000” ]Defines the company ID and is always related to the application ID.
effectobject{​ "id": "buy_x_get_y",​ "additionalData": {​ "variant_ids": [​ 12389244,​ 23985437​ ],​ "max_count": 1,​ "max_count_type": "per_eligible_items_quantity",​ "eligible_items_quantity": 6​ }​ }Defines the type of the promotion.
effect.idstring"id": "buy_x_get_y"buy_x_get_y must be used for free gifts. automatic_discount must be used for relative or absolute reductions.
effect.additionalDataobject"additionalData": {​ "variant_ids": [​ 12389244,​ 23985437​ ],​ "max_count": 1,​ "max_count_type": "per_eligible_items_quantity",​ "eligible_items_quantity": 6​ }​ }Depends on the effect.id.

Optional parameters

ParameterTypeExampleDefinition
sibling-Promotionsobject"siblingPromotions": {​ "enabled": true,​ "allowList": [​ "645e0c241a93369ff53f26e0",​ "646235bf496f8f56171c3762"​ ],​ "blockList": [​ "64623787496f8f56171c3763"​ ],​ "level": "basket"​ }You could have several applicable promotions for one basket.
sibling-Promotions.enabledbooleantrueYou can only use block list or allow list; they are mutually exclusive.
sibling-Promotions.allowListarray[ "645e0c241a93369ff53f26e0", "646235bf496f8f56171c3762" ]A list of promotions IDs that can be applied together with this promotion.
sibling-Promotions.blocklistarray[ "64623787496f8f56171c3763" ]A list of promotions IDs that cannot be applied together with this promotion.
sibling-Promotions.levelstring"level": "basket"Defines on which level the promotions can be combined.
audiencesobject"audiences": {​ "allowList": [​ "645e0c241a93369ff53f26e0",​ "646235bf496f8f56171c3762"​ ],​ "blockList": [​ "64623787496f8f56171c3763"​ ]​ }Audiences defines for which customers the promotion is valid. Audiences can be set up in the SCAYLE Panel.
audiences.allowListarray"allowList": [​ "645e0c241a93369ff53f26e0",​ "646235bf496f8f56171c3762"​ ]The list of audience ids that can use this promotion.
audiences.blocklistarray"blockList": [​ "64623787496f8f56171c3763"​ ]The list of audience ids that can not use this promotion.
globalConditionsarray"globalConditions": [​ {​ "key": "mov_100",​ "condition": "payload.items.exists(i, 11003 in i.variant.id)"​ }​ ]Defines the condition it self.
globalConditions.keystring"key": "mov_100"Key of the condition. Can be used to identify which condition failed in the validation endpoint.
globalConditions.conditionstring"condition": "payload.items.exists(i, 11003 in i.variant.id)"Defines the condition it self.
itemConditionsarray[​ {​ "key": "mov_100",​ "condition": "item.color == 'yellow'"​ }​ ]List of conditions that determines whether the promotion is applicable or not. Item conditions are order item related.
itemConditions.keystring

"key"
"condition"

Key of the condition. Can be used to identify which condition failed in the validation endpoint.
itemConditions.conditionstringitem.color == 'yellow'Defines the condition it self.
prioritystring"1"In case several promotions are applicable but not combinable the priority decides.
additionalDataobject{​ "variant_ids": [​ 12389244,​ 23985437​ ],​ "max_count": 1,​ "max_count_type": "per_eligible_items_quantity",​ "eligible_items_quantity": 6​ }​ }
tiersarray[​ {​ "name": "Tier 1",​ "effect": {​ "additionalData": {​ "variant_ids": [​ 12389244,​ 23985437​ ],​ "max_count": 1​ }​ },​ "mov": 10000​ }​ ]

Tierer promotions are minimum order value coupons:

  • tier names must be unique
  • MOV values must be in ascending order

Storefront API

Use the Storefront API to:

In the SCAYLE Panel

You can manage Promotions in the SCAYLE Panel.
Use it to create, update and delete promotions.

SCAYLE Panel allows you to manage following Promotion types:

  • Advanced
  • Automatic discount
  • Item Discount
  • Buy X get Y
  • Flash Sales

Additionally, you can manage:

  • Price Campaigns
  • Tiered Discounts
  • Vouchers

It is not required to add a voucher code or similar in order to activate a promotion. The promotion is valid as soon as the promotion conditions are fulfilled.

Advanced promotions

Use one of our templates to create a promotion. By using the supported_effect parameter, you can quickly create: