Example Loyalty Scheme

This guide provides an end-to-end example of how to setup a loyalty scheme. The example loyalty scheme has the following characteristics:

  • Customers earn 1% points on their purchases that are valid for 1 year
  • Customers are given 100 points for signing up to a newsletter
  • Customers in the loyalty scheme are given early access to a sale
  • Customers can choose to spend their points on their purchases

For this example scheme, customers won't be automatically enrolled in the scheme.

For more details on loyalty schemes see the the following guides:

Properties Setup

The first thing we need to do is create the property we're going to use to uniquely identify entries in the loyalty scheme.

For this scheme it will be a unique customer ID (a UUID):

user id property

Scheme Setup

Next, we'll create the loyalty scheme. Go to the "Loyalty" section of the tooling and click "Add Loyalty Scheme". The "Create Loyalty Scheme" page is displayed:

create loyalty scheme page

Give the scheme a name and choose the "User ID" property as the "Scheme Entry Property".

Now we have a loyalty scheme we can start creating discounts that use the loyalty scheme. However, there are no customers in the scheme and since we don't want customers to be automatically enrolled we need to enrol them.

Enrolling Customers in the Scheme

For this example we will enrol a customer using the management tooling. In a more realistic scenario you may integrate with your customer management system to automatically enrol customers using the Builder API.

On the loyalty scheme listing page, click the scheme you have created and click the "Create New Entry" button. The "Add Entry" sidebar is displayed:

create loyalty scheme entry
  • Add a unique "Entry Identifier" (e.g. 48892b39-2407-4d62-aba5-003a0117aa3a as we are using our customer UUID).
  • Leave the "Balance" as 0 as we don't want to give the customer any points
  • Choose "Staging" for the "Data Instance" as we are testing the scheme.

Click the "Create Entry" button.

Accruing Points

Newsletter Sign-up

The first discount we will create is to give the customer 100 points for signing up to a newsletter.

To do this we'll create some properties so we can notify the system a user has signed-up to a newsletter.

We'll create a top level "Events" property of type Object:

events property

We'll now create a child property called "Name":

event name property

We'll now create a discount that gives 100 points when an event name of SignedUpToNewsletter is passed to the evaluation endpoint of the Processor API. Note, since customers shouldn't be automatically enrolled in the scheme, we add a condition to check they are part of the scheme already.

Go to the Discounts section of the tooling and click the "Add discount" button.

Give the discount a name of "Newsletter Sign-up - Award Loyalty Points".

Click "Add Condition", edit the condition and click "Add" next to "Eligibility". Add an expression as shown below:

eligibility expression

This ensures only customers who are already enrolled in the scheme get the points when they sign-up to the newsletter.

Click "Done" on the "Eligibility" expression sidebar and then "Done" on the "Condition" sidebar.

Click "Add Action" and choose "Accrue Loyalty Points". Edit the action and complete the form as shown below:

accrue points action

Click "Done". The discount should appear as below:

newsletter discount overview

Click the "Save" button.

Processor API Example

Below are example request and responses from the evaluate endpoint.

Request

{
  "events": {
    "name": "SignedUpToNewsletter"
  },
  "customer": {
    "userId": "48892b39-2407-4d62-aba5-003a0117aa3a"
  },
  "settings": {
    "dataInstance": "Staging",
    "commit": true
  }
}

Response

{
  "actions": [
    {
      "id": "c65ff99c-807b-443b-8d0a-0f15fcf2334f",
      "discountId": "d7e43bb5-1de8-4976-b34b-0d01492709d2",
      "type": "AccrueLoyaltyPoints",
      "loyaltySchemeId": "05a35e84-8e55-409f-9f87-76d55cbd0e6c",
      "qualifiedCouponCode": null,
      "pointsAccrued": 100,
      "startDate": null,
      "expiryDate": null,
      "displayMessages": []
    }
  ],
  "basket": null,
  "aggregates": {
    "total": 0,
    "totalAmountOff": 0
  },
  "costs": [],
  "commitId": "37c5e2eb-4079-4dbd-a1db-607bf232b0a6",
  "evaluationLog": null
}

Note, the client application would need validation to only pass this SignedUpToNewsletter event if they haven't signed-up previously otherwise users can keep subscribing/un-subscribing and re-subscribing.

Now if you lookup the entry in the tooling you will see the balance and transactions have been updated:

loyalty scheme entry

Discount to earn points on purchases

Next we'll setup a discount to allow customers to earn 1% of points on their purchases that are valid for 1 year. Note, since customers shouldn't be automatically enrolled in the scheme, we add a condition to check they are part of the scheme already.

Add a discount with the name "Accrue Points for Purchases".

Click "Add Condition", edit the condition and click "Add" next to "Eligibility". Add an expression as shown below:

accrue points for purchases condition

Click "Done" on the "Eligibility" expression sidebar and then "Done" on the "Condition" sidebar.

Click "Add Action" and choose "Accrue Loyalty Points". Edit the action and complete the form as shown below:

accrue points for purchases action

Click "Done". The discount should appear as below:

accrue points discount overview

Click the "Save" button.

Processor API Example

Below are example request and responses from the evaluate endpoint.

Request

{
  "basket": {
    "items": [
      {
        "quantity": 2,
        "price": 58.99
      }
    ]
  },
  "costs": [
    {
      "name": "Shipping",
      "value": 5.99
    }
  ],
  "context": {
    "currencyCode": "GBP"
  },
  "customer": {
    "userId": "48892b39-2407-4d62-aba5-003a0117aa3a"
  },

  "settings": {
    "dataInstance": "Staging",
    "commit": true,
    "explain": false
  }
}

Response

{
  "actions": [
    {
      "id": "f1ea04b4-5d52-4973-ae62-e220194355dd",
      "discountId": "5c0b024b-e371-449e-9339-b1e40ad5138d",
      "type": "AccrueLoyaltyPoints",
      "loyaltySchemeId": "ebc6bec7-0b36-4860-bf1c-112e6bb2ea65",
      "qualifiedCouponCode": null,
      "pointsAccrued": 1,
      "startDate": null,
      "expiryDate": "2025-07-19T15:18:57.2018921",
      "displayMessages": []
    }
  ],
  "basket": {
    "total": 117.98,
    "totalAmountOff": 0,
    "items": [
      {
        "total": 117.98,
        "totalAmountOff": 0,
        "actions": []
      }
    ]
  },
  "aggregates": {
    "total": 123.97,
    "totalAmountOff": 0
  },
  "costs": [
    {
      "name": "Shipping",
      "value": 5.99,
      "totalAmountOff": 0,
      "actions": []
    }
  ],
  "commitId": "d095d352-21ce-4316-ab28-c254ec77bc55",
  "evaluationLog": null
}

Discount to give customers in the loyalty scheme early access to a sale

We'll setup a sale for Black Friday 2024 (29 November) to give 10% off when customers spend 100 GBP.

Firstly we'll create a discount that gives members of the loyalty scheme the discount from the 25 November.

Add a discount with the name "Sale - Early Access". Set the start date to 25 November 00:00 and end date to 29 November 23:59.

Click "Add Condition" and edit the condition. Choose a "Type" of "Minimum Spend" and add an entry for GBP 100. Add an eligibility expression for "Active in scheme" as in the previous discount.

sale early access condition

Click "Done" on the "Eligibility" expression sidebar and then "Done" on the "Condition" sidebar.

Click "Add Action" and choose "Amount Off Basket". Edit the action and complete the form as shown below:

sale early access action

Click "Done". The discount should appear as below:

sale early access discount overview

Click the "Save" button.

We'll now create a discount to give customers not part of the loyalty scheme the discount on 29 November. Note, this discount checks the customer isn't part of the loyalty scheme, otherwise on Black Friday both discounts would apply for members of the loyalty scheme.

Add a discount with the name "Sale". Set the start date to 29 November 00:00 and end date to 29 November 23:59.

Click "Add Condition" and edit the condition. Choose a "Type" of "Minimum Spend" and add an entry for GBP 100. Add an eligibility expression for "Active in scheme" "Is equal to" "false" like below:

sale condition eligibility

Click "Done" on the "Eligibility" expression sidebar and then "Done" on the "Condition" sidebar.

Click "Add Action" and choose "Amount Off Basket". Set this up in the same way as the "Sale - Early Access" discount.

Click "Done". The discount should appear as below:

sale discount overview

Click the "Save" button.

Processor API Example

Request

You can preview the future discount using the utcNow setting. E.g.

{
  "basket": {
    "items": [
      {
        "quantity": 2,
        "price": 58.99
      }
    ]
  },
  "costs": [
    {
      "name": "Shipping",
      "value": 5.99
    }
  ],
  "context": {
    "currencyCode": "GBP"
  },
  "customer": {
    "userId": "48892b39-2407-4d62-aba5-003a0117aa3a"
  },
  "settings": {
    "dataInstance": "Staging",
    "commit": false,
    "explain": false,
    "utcNow": "2024-11-25T10:00:00Z"
  }
}

Response

{
  "actions": [
    {
      "id": "2a918852-4b2e-45b2-b62d-e6e2e75aee5d",
      "discountId": "5c0b024b-e371-449e-9339-b1e40ad5138d",
      "type": "AccrueLoyaltyPoints",
      "loyaltySchemeId": "ebc6bec7-0b36-4860-bf1c-112e6bb2ea65",
      "qualifiedCouponCode": null,
      "pointsAccrued": 1,
      "startDate": null,
      "expiryDate": "2025-11-25T10:00:00",
      "displayMessages": []
    },
    {
      "id": "69f63a33-2f3d-4e61-814e-87d41662ac11",
      "discountId": "bd56920e-2e2b-4223-b11f-3e1e7ed4575b",
      "type": "AmountOffBasket",
      "qualifiedCouponCode": null,
      "amountOffType": "PercentOff",
      "value": 10,
      "amountOff": 11.79,
      "displayMessages": []
    }
  ],
  "basket": {
    "total": 106.19,
    "totalAmountOff": 11.79,
    "items": [
      {
        "total": 106.19,
        "totalAmountOff": 11.79,
        "actions": [
          {
            "id": "69f63a33-2f3d-4e61-814e-87d41662ac11",
            "subItemId": 1,
            "amountOff": 5.9
          },
          {
            "id": "69f63a33-2f3d-4e61-814e-87d41662ac11",
            "subItemId": 2,
            "amountOff": 5.89
          }
        ]
      }
    ]
  },
  "aggregates": {
    "total": 112.18,
    "totalAmountOff": 11.79
  },
  "costs": [
    {
      "name": "Shipping",
      "value": 5.99,
      "totalAmountOff": 0,
      "actions": []
    }
  ],
  "commitId": null,
  "evaluationLog": null
}

Discount to allow customers to spend points on purchases

We need to create a property to notify the evaluation process if the customer has chosen to spend their points. Note this isn't required if customer's points should be automatically spent.

We'll create a boolean property called "SpendPoints" on the "Customer" object:

spend points property

Now we'll create the discount to spend points.

Add a discount with the name "Pay with Points".

Click "Add Condition", edit the condition and click "Add" next to "Eligibility". Add an expression as shown below:

pay with points condition eligibility

Click "Done" on the "Eligibility" expression sidebar and then "Done" on the "Condition" sidebar.

Click "Add Action" and choose "Pay with Loyalty Points". Edit the action and complete the form as shown below:

pay with points action

A ratio of 0.01 points to GBP is used (100 points gives 1 GBP off)

Click "Done". The discount should appear as below:

pay with points discount overview

Click the "Save" button.

Processor API Example

Below are example request and responses from the evaluate endpoint.

Request

{
  "basket": {
    "items": [
      {
        "quantity": 2,
        "price": 58.99
      }
    ]
  },
  "costs": [
    {
      "name": "Shipping",
      "value": 5.99
    }
  ],
  "context": {
    "currencyCode": "GBP"
  },
  "customer": {
    "userId": "48892b39-2407-4d62-aba5-003a0117aa3a",
    "spendPoints": true
  },
  "settings": {
    "dataInstance": "Staging",
    "commit": false,
    "explain": false
  }
}

Response

{
  "actions": [
    {
      "id": "f4e08406-25c1-4500-9ac4-453abad6bea4",
      "discountId": "5c0b024b-e371-449e-9339-b1e40ad5138d",
      "type": "AccrueLoyaltyPoints",
      "loyaltySchemeId": "ebc6bec7-0b36-4860-bf1c-112e6bb2ea65",
      "qualifiedCouponCode": null,
      "pointsAccrued": 1,
      "startDate": null,
      "expiryDate": "2025-07-19T16:10:16.294221",
      "displayMessages": []
    },
    {
      "id": "631b54e9-d830-424a-a087-217a1f5f48bd",
      "discountId": "d5222096-c086-4d96-8675-87f8f4b17962",
      "type": "AmountOffBasket",
      "qualifiedCouponCode": null,
      "amountOffType": "AmountOff",
      "value": 1,
      "amountOff": 1,
      "displayMessages": []
    },
    {
      "id": "8f29726b-72dd-470d-bc0d-f04a49d86f10",
      "discountId": "d5222096-c086-4d96-8675-87f8f4b17962",
      "type": "RedeemLoyaltyPoints",
      "loyaltySchemeId": "ebc6bec7-0b36-4860-bf1c-112e6bb2ea65",
      "qualifiedCouponCode": null,
      "pointsRedeemed": 100,
      "displayMessages": []
    }
  ],
  "basket": {
    "total": 116.98,
    "totalAmountOff": 1,
    "items": [
      {
        "total": 116.98,
        "totalAmountOff": 1,
        "actions": [
          {
            "id": "631b54e9-d830-424a-a087-217a1f5f48bd",
            "subItemId": 1,
            "amountOff": 0.5
          },
          {
            "id": "631b54e9-d830-424a-a087-217a1f5f48bd",
            "subItemId": 2,
            "amountOff": 0.5
          }
        ]
      }
    ]
  },
  "aggregates": {
    "total": 122.97,
    "totalAmountOff": 1
  },
  "costs": [
    {
      "name": "Shipping",
      "value": 5.99,
      "totalAmountOff": 0,
      "actions": []
    }
  ],
  "commitId": null,
  "evaluationLog": null
}

Discount Ordering

Note, with the current discount ordering AccrueLoyaltyPoints is evaluated first. This is not what we want here because this will accrue points on the total before it has been discounted.

In the example request and response below, the customer has over 20000 points:

Request

{
  "basket": {
    "items": [
      {
        "quantity": 1,
        "price": 200
      }
    ]
  },
  "context": {
    "currencyCode": "GBP"
  },
  "customer": {
    "userId": "48892b39-2407-4d62-aba5-003a0117aa3a"
  },
  "settings": {
    "dataInstance": "Staging",
    "commit": false,
    "explain": false
  }
}

Response

{
  "actions": [
    {
      "id": "7ec108e6-09ad-498d-9802-4cc3bd3278b1",
      "discountId": "5c0b024b-e371-449e-9339-b1e40ad5138d",
      "type": "AccrueLoyaltyPoints",
      "loyaltySchemeId": "ebc6bec7-0b36-4860-bf1c-112e6bb2ea65",
      "qualifiedCouponCode": null,
      "pointsAccrued": 2,
      "startDate": null,
      "expiryDate": "2025-07-22T13:51:11.1155437",
      "displayMessages": []
    },
    {
      "id": "e3ee1003-19b1-4ad6-ba5f-a7d326a2b42b",
      "discountId": "d5222096-c086-4d96-8675-87f8f4b17962",
      "type": "AmountOffBasket",
      "qualifiedCouponCode": null,
      "amountOffType": "AmountOff",
      "value": 200,
      "amountOff": 200,
      "displayMessages": []
    },
    {
      "id": "f68580ac-ada6-4c5f-a1a9-6b1acd78c56b",
      "discountId": "d5222096-c086-4d96-8675-87f8f4b17962",
      "type": "RedeemLoyaltyPoints",
      "loyaltySchemeId": "ebc6bec7-0b36-4860-bf1c-112e6bb2ea65",
      "qualifiedCouponCode": null,
      "pointsRedeemed": 20000,
      "displayMessages": []
    }
  ],
  "basket": {
    "total": 0,
    "totalAmountOff": 200,
    "items": [
      {
        "total": 0,
        "totalAmountOff": 200,
        "actions": [
          {
            "id": "e3ee1003-19b1-4ad6-ba5f-a7d326a2b42b",
            "subItemId": 1,
            "amountOff": 200
          }
        ]
      }
    ]
  },
  "aggregates": {
    "total": 0,
    "totalAmountOff": 200
  },
  "costs": [],
  "commitId": null,
  "evaluationLog": null
}

The total has been reduced to zero, but they have accrued 2 points.

We will re-sequence the discounts so accruing points is done last. Generally you want discounts with AccrueLoyaltyPoints and PayWithLoyaltyPoints actions to be after discounts that take money off the order.

Go to the Discount section of the tooling and click the "Priorities" link in the sub nav. Re-order the discounts:

discount priorities

Click the "Save" button. Now when we call the evaluation endpoint no points are accrued.

{
  "actions": [
    {
      "id": "30a2bdc6-20d3-4b76-95ae-a16b9a6fb540",
      "discountId": "d5222096-c086-4d96-8675-87f8f4b17962",
      "type": "AmountOffBasket",
      "qualifiedCouponCode": null,
      "amountOffType": "AmountOff",
      "value": 200,
      "amountOff": 200,
      "displayMessages": []
    },
    {
      "id": "40a33fee-641f-4d37-82b8-828334b87da3",
      "discountId": "d5222096-c086-4d96-8675-87f8f4b17962",
      "type": "RedeemLoyaltyPoints",
      "loyaltySchemeId": "ebc6bec7-0b36-4860-bf1c-112e6bb2ea65",
      "qualifiedCouponCode": null,
      "pointsRedeemed": 20000,
      "displayMessages": []
    }
  ],
  "basket": {
    "total": 0,
    "totalAmountOff": 200,
    "items": [
      {
        "total": 0,
        "totalAmountOff": 200,
        "actions": [
          {
            "id": "30a2bdc6-20d3-4b76-95ae-a16b9a6fb540",
            "subItemId": 1,
            "amountOff": 200
          }
        ]
      }
    ]
  },
  "aggregates": {
    "total": 0,
    "totalAmountOff": 200
  },
  "costs": [],
  "commitId": null,
  "evaluationLog": null
}