Beta Release
This is pre-release documentation for an API in public beta and is subject to change.
Loyalty API

Manage Loyalty Accounts Using the Loyalty API

After a Square loyalty program is set up, you can use the Loyalty API to create loyalty accounts for buyers and allow them to earn points and redeem rewards.

List loyalty programs Permalink Get a link to this section

To list the loyalty programs in a seller's account, call ListLoyaltyPrograms. Currently, a seller account can contain only one loyalty program.

List Loyalty Programs
  • 1
  • 2
  • 3
  • 4
curl https://connect.squareupsandbox.com/v2/loyalty/programs \
  -H 'Square-Version: 2021-03-17' \
  -H 'Authorization: Bearer {ACCESS_TOKEN}' \
  -H 'Content-Type: application/json'

Note

You cannot use the Loyalty API to create or manage Square loyalty programs from third-party applications. Loyalty programs can be created and managed only from the Square Seller Dashboard.

The following is an example ListLoyaltyPrograms response. It represents a loyalty program that offers two percentage-based reward tiers and allows buyers to accrue one point for every $2 spent. For more information about Square loyalty programs, see Loyalty Program Overview.

{
  "programs": [
    {
      "id": "8031c1b2-d749-4c76-9c40-ae547example",
      "status": "ACTIVE",
      "reward_tiers": [
        {
          "id": "7b897fee-572a-4516-a3d3-476763example",
          "points": 15,
          "name": "10% off entire sale",
          "definition": { 
            "scope": "ORDER",
            "discount_type": "FIXED_PERCENTAGE",
            "percentage_discount": "10"
          },
          "created_at": "2020-12-16T17:39:54Z",
          "pricing_rule_reference": {
            "object_id": "ESNLTB2A77example",
            "catalog_version": "56402example"
          }
        },
        {
          "id": "46c2716e-f559-4b75-c015-764897example",
          "points": 30,
          "name": "25% off entire sale",
          "definition": {
            "scope": "ORDER",
            "discount_type": "FIXED_PERCENTAGE",
            "percentage_discount": "25"
          },
          "created_at": "2020-12-16T17:47:22Z",
          "pricing_rule_reference": {
            "object_id": "GR4C4RSNLFexample",
            "catalog_version": "73802example"
          }
        }
      ],
      "terminology": {
        "one": "Point",
        "other": "Points"
      },
      "location_ids": [
        "S8GWD5R9QB376"
      ],
      "created_at": "2020-12-16T23:35:41Z",
      "updated_at": "2020-12-20T02:00:02Z",
      "accrual_rules": [
        {
          "accrual_type": "SPEND",
          "points": 1,
          "spend_amount_money": {
            "amount": 200
          }
        }
      ]
    }
  ]
}

To get information about the reward discount, call the RetrieveCatalogObject endpoint using the object_id and catalog_version from the pricing_rule_reference field. Make sure to set include_related_objects to true. For more information, see Integration with the Catalog API.

Note

The ListLoyaltyPrograms response currently includes the deprecated definition field, which is replaced by the pricing_rule_reference field. You should use pricing_rule_reference.

For information about how the Loyalty API integrates with other Square APIs, see Integration with the Orders API and Integration with the Customers API.

Create a loyalty account Permalink Get a link to this section

To enroll a buyer in a loyalty program, you create a loyalty account for the buyer in the program. You must provide the program ID and the buyer's phone number. A phone number can be mapped to only one loyalty account in a loyalty program.

To create a loyalty account, call CreateLoyaltyAccount as shown. In the request:

  • program_id identifies the loyalty program. You can call ListLoyaltyPrograms to get the program ID.

  • mappings identifies the buyer by phone number, which must be specified in E.164 format. You can obtain a buyer's phone number from your application flow.

    Create Loyalty Account
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    curl https://connect.squareupsandbox.com/v2/loyalty/accounts \
      -X POST \
      -H 'Square-Version: 2021-03-17' \
      -H 'Authorization: Bearer {ACCESS_TOKEN}' \
      -H 'Content-Type: application/json' \
      -d '{
        "loyalty_account": {
          "mappings": [
            {
              "type": "PHONE",
              "value": "+16295551234"
            }
          ],
          "program_id": "8031c1b2-d749-4c76-9c40-ae547example"
        },
        "idempotency_key": "{UNIQUE_KEY}"
      }'

After receiving the request, Square:

  • Uses the phone number to determine whether a customer profile exists in the seller's Customer Directory (see Customers). If it does not exist, Square creates a customer profile.

  • Creates an account in the loyalty program and returns the following response:

    {
       "loyalty_account":{
          "id":"716cefbc-3d71-4d7c-bdc8-9c7f8example",
          "mappings":[
             {
                "id":"6377d589-3f09-4f00-a0e8-56d7dexample",
                "type":"PHONE",
                "value":"+16295551234",
                "created_at":"2020-01-30T00:11:58Z"
             }
          ],
          "program_id":"8031c1b2-d749-4c76-9c40-ae547example",
          "balance":0,
          "lifetime_points":0,
          "customer_id":"REK96J96AS5AN2Y8Z4Hexample",
          "created_at":"2020-01-30T00:11:58Z",
          "updated_at":"2020-01-30T00:11:58Z"
       }
    }
    

    The response shows the associated customer profile ID (customer_id). Both balance and lifetime_points are 0 because the buyer has not yet accrued any loyalty points.

The phone number that you provide to create a loyalty account must use the E.164 format (for example, +16295551234). The country code of the phone number must correspond to a market where Square Loyalty is available. Otherwise, the CreateLoyaltyAccount endpoint returns an INVALID_PHONE_NUMBER error code. For information about market availability, see Square Loyalty International Availability.

Terms of service and text notifications Permalink Get a link to this section

After a loyalty account is created, Square can contact a buyer using the phone number provided and send text messages such as "you have a reward available". However, this requires the buyer to first agree to the terms of service.

  • When a buyer enrolls in a loyalty program at the Square Point of Sale, the buyer is shown the terms of service that must be accepted before Square can send any text messages.

  • When you create an account through the Loyalty API, unless and until a buyer makes a purchase through a Point of Sale, Square cannot show the terms of service and therefore the buyer cannot receive any text messages.

Access a loyalty account Permalink Get a link to this section

You can retrieve a buyer's loyalty account using an account ID, search for accounts by phone number or customer ID, or retrieve all loyalty accounts.

  • Call RetrieveLoyaltyAccount if you know the ID of the loyalty account. The following example uses 716cefbc-3d71-4d7c-bdc8-9c7f8example as the account ID:

    Retrieve Loyalty Account
    • 1
    • 2
    • 3
    • 4
    curl https://connect.squareupsandbox.com/v2/loyalty/accounts/716cefbc-3d71-4d7c-bdc8-9c7f8example \
      -H 'Square-Version: 2021-03-17' \
      -H 'Authorization: Bearer {ACCESS_TOKEN}' \
      -H 'Content-Type: application/json'

    The following is an example RetrieveLoyaltyAccount response:

    {
      "loyalty_account": {
        "id": "716cefbc-3d71-4d7c-bdc8-9c7f8example",
        "mappings": [
          {
            "id": "66aaab3f-da99-49ed-8b19-b87f8example",
            "type": "PHONE",
            "value": "+16295551234",
            "created_at": "2020-01-30T00:11:58Z"
          }
        ],
        "program_id": "8031c1b2-d749-4c76-9c40-ae547example",
        "balance": 10,
        "lifetime_points": 20,
        "customer_id": "Q8002FAM9V1EZ0ADB2T5609Xexample",
        "created_at": "2020-05-08T21:44:32Z",
        "updated_at": "2020-05-08T21:44:32Z"
      }
    }
    

    If the account cannot be found, the Loyalty API returns a 404 NOT_FOUND error.

Accumulate points Permalink Get a link to this section

Buyers accrue points for the purchases they make. After the buyer pays for the order, you call AccumulateLoyaltyPoints to add points to the buyer's account for the purchase. The following examples use 716cefbc-3d71-4d7c-bdc8-9c7f8example as the account ID:

  • If you are using the Orders API to process orders, you provide the order ID in the AccumulateLoyaltyPoints request. The endpoint reads the order data, determines the loyalty points using the purchase amount, and adds those points to the account. The endpoint uses the pretax purchase amount to compute the loyalty points.

    Accumulate Loyalty Points
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    curl https://connect.squareupsandbox.com/v2/loyalty/accounts/716cefbc-3d71-4d7c-bdc8-9c7f8example/accumulate \
      -X POST \
      -H 'Square-Version: 2021-03-17' \
      -H 'Authorization: Bearer {ACCESS_TOKEN}' \
      -H 'Content-Type: application/json' \
      -d '{
        "accumulate_points": {
          "order_id": "cb9LSpDgOH3rITBaZ6eIexample"
        },
        "location_id": "S8GWD5example",
        "idempotency_key": "{UNIQUE_KEY}"
      }'
  • If you are using your own order processing system, and not the Orders API, you must compute the loyalty points on the client side and specify those points in the AccumulateLoyaltyPoints request.

    Accumulate Loyalty Points
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    curl https://connect.squareupsandbox.com/v2/loyalty/accounts/716cefbc-3d71-4d7c-bdc8-9c7f8example/accumulate \
      -X POST \
      -H 'Square-Version: 2021-03-17' \
      -H 'Authorization: Bearer {ACCESS_TOKEN}' \
      -H 'Content-Type: application/json' \
      -d '{
        "accumulate_points": {
          "points": 7
        },
        "location_id": "S8GWD5example",
        "idempotency_key": "{UNIQUE_KEY}"
      }'

Redeem loyalty rewards Permalink Get a link to this section

A loyalty program can offer one or more rewards, which are also referred to as reward tiers. The higher the reward tier, the better the reward. This encourages buyers who accumulate more points to qualify for better rewards. For example:

  • Reward tier 1: If a buyer has 10 points in the account, the buyer qualifies for 10% off the entire purchase.

  • Reward tier 2: If a buyer has 15 points in the account, the buyer qualifies for 20% off the entire purchase.

While building the order, depending on the available point balance in the buyer's loyalty account, your application can offer the buyer the option to redeem points for a reward. For example, if the buyer has a balance of 12 points, you can offer the buyer the option to redeem reward tier 1. If the buyer has 17 points, you can offer the buyer the option to redeem reward tier 2. For information about retrieving reward tier information, see Get discount details for a reward tier.

If the buyer chooses to redeem the reward, you call CreateLoyaltyReward. In the request, you provide the:

  • Buyer's loyalty account ID from which to take the points.

  • Reward tier ID that the buyer chose.

  • Order ID, if your application uses the Orders API to manage orders.

    Create Loyalty Reward
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    curl https://connect.squareupsandbox.com/v2/loyalty/rewards \
      -X POST \
      -H 'Square-Version: 2021-03-17' \
      -H 'Authorization: Bearer {ACCESS_TOKEN}' \
      -H 'Content-Type: application/json' \
      -d '{
        "reward": {
          "order_id": "cb9LSpDgOH3rITBaZ6eIexample",
          "loyalty_account_id": "716cefbc-3d71-4d7c-bdc8-9c7f8example",
          "reward_tier_id": "46c2716e-f559-4b75-c015-764897example"
        },
        "idempotency_key": "{UNIQUE_KEY}"
      }'

The CreateLoyaltyReward endpoint:

  • Removes points from the buyer's account, as defined by the reward tier).

  • Returns a reward object of type LoyaltyReward. The object identifies the points taken from the buyer's account and the reward tier being redeemed.

    {
      "reward":{
        "id": "46c2716e-f559-4b75-c015-764897example",
        "status": "ISSUED",
        "loyalty_account_id": "716cefbc-3d71-4d7c-bdc8-9c7f8example",
        "reward_tier_id": "46c2716e-f559-4b75-c015-764897example",
        "points": 10,
        "order_id": "cb9LSpDgOH3rITBaZ6eIexample",
        "created_at": "2020-03-13T00:00:51Z",
        "updated_at": "2020-03-13T00:00:51Z"
      }
    }
    
  • If the CreateLoyaltyReward request included an order ID, the endpoint attaches the reward to the order, applies the corresponding discounts to qualifying line items, and adjusts the order amount accordingly. If order ID is not provided, it is your responsibility to apply the appropriate discount to the shopping cart.

Note

The Orders API allows you to add items and rewards to an order in any sequence. If you add a reward first, your application should verify that the item in the reward is also added to the order. For example, if the reward gives the buyer a free coffee, make sure the order includes coffee. If not, Square deletes the reward when the order is paid and returns the points to the buyer's loyalty account.

Reward state Permalink Get a link to this section

When CreateLoyaltyReward is called, initially the reward state is ISSUED. If the buyer chooses not to redeem the reward, you should call DeleteLoyaltyReward to delete the reward. The endpoint does the following:

  • Returns points to the buyer's account.

  • Sets the reward state to DELETED.

  • If an order ID was specified when creating the reward, the endpoint updates the order by removing the discount and updating the order amount accordingly.

If the buyer pays for the order, depending on whether an order ID was specified when creating the reward, the following happens:

  • The order ID was specified when creating the reward. Square sets the reward state to:

    • REDEEMED if the reward resulted in a discount on the order.

    • DELETED if the reward did not result in a discount on the order. In this case, the points are returned to the buyer's loyalty account. This happens asynchronously and changes might not be immediately visible if you fetch the reward using RetrieveLoyaltyReward.

  • The order ID was not specified when creating the reward. You must explicitly call RedeemLoyaltyReward to set the reward state to REDEEMED.

The reward state REDEEMED is a terminal state, after which no changes can be made (for example, you cannot delete the reward and return the points to the buyer's account).

If the reward does not get redeemed (the reward state remains ISSUED), it remains in a dangling state, where the points are simply locked (not available for the buyer to use for any other rewards). It is your responsibility to clean up any dangling rewards, for instance by deleting them on orders left open for too long.

Integration with the Customers API Permalink Get a link to this section

Every loyalty account has a customer profile associated with it in the seller's Customer Directory (see Customers). When a loyalty account is created for a buyer, only the buyer's phone number is required and the customer ID is optional. However, you should provide the customer ID when creating the loyalty account for a better experience.

If you do not provide a customer ID, Square uses the phone number to search for a customer profile. If a matching customer profile cannot be found, Square creates a new customer profile and associates it with the loyalty account. This can result in multiple customer profiles in the Customer Directory for a buyer. For example, if you previously created a customer profile for the buyer using an email address (and not a phone number), then that customer profile is not found using a phone number and another customer profile is created.

You can search for a loyalty account by customer ID. For more information, see Access a loyalty account.

Integration with the Orders API Permalink Get a link to this section

The Loyalty API is integrated with the Orders API and you should leverage this in your applications. Some of the benefits are:

  • A simplified point accrual flow. The following Loyalty API endpoints can easily compute the points to earn using information in orders created using the Orders API.

  • A simplified reward redemption flow. The Loyalty API provides the CreateLoyaltyReward endpoint to create a reward and turn the reward into appropriate discounts by automatically updating the order. The example walkthroughs shows how the integration works. For more information, see Loyalty Walkthrough 1 and Loyalty Walkthrough 2.

    The Orders API provides an added benefit when working with multiple discounts on an order. The pricing engine that Square provides can aggregate multiple discounts from various sources. Without the Orders API, you need to write code to accomplish this task.

  • Useful reports on the Seller Dashboard. The reports on the Seller Dashboard provide useful metrics about how the loyalty program is working:

    • Visits report. The Visits report uses the order data to show the number of first-time (and repeat) loyalty buyers and the average visits by loyalty (and non-loyalty) buyers for a given period.

    • Sales report. The Sales report shows sales amounts by loyalty (and non-loyalty) buyers and the average amount spent by loyalty (and non-loyalty) buyers.

      The Seller Dashboard also shows the Top Customers report.

You might choose not to use the Orders API for order processing. Depending on the loyalty program you choose, expect added development costs. However, the following loyalty programs are easier to implement without using the Orders API:

  • A loyalty program that offers visit-based accrual. Consider this points accrual rule: "earn one point for every visit, with a minimum purchase of $10." In this case, you can add points to the buyer's account with minimal code. You do not need itemized orders. For example, suppose a buyer pays $15 for an order. You can use the CalculateLoyaltyPoints endpoint to compute loyalty points and then call AccumulateLoyaltyPoints to add the points to the buyer's account.

  • A loyalty program that offers dollar-based accrual. Consider this point accrual rule: "Earn one point for each dollar spent." You can use the CalculateLoyaltyPoints endpoint to compute loyalty points and then call AccumulateLoyaltyPoints to add the points to the loyalty account of the buyer. This does not require matching any order line items to compute the loyalty points.

Note that the loyalty points are calculated based on pretax amounts. If you use the Orders API, the amount of taxes you paid are known and the points are computed appropriately without the need of additional code.

Integration with the Catalog API Permalink Get a link to this section

A loyalty program offers one or more reward tiers that define how buyers can redeem points for discounts. For example, a reward tier might represent a reward such as "Redeem 10 points for 10% off your entire purchase." A loyalty reward tier uses PRICING_RULE, DISCOUNT, and PRODUCT_SET catalog objects to define the discount details for a reward.

Get discount details for a reward tier Permalink Get a link to this section

The following high-level steps describe how to get discount details and product information that correspond to the reward tiers in a loyalty program:

Step 1: Get the reward tiers for the loyalty program. Call ListLoyaltyProgram in the Loyalty API. Then, check whether the loyalty account balance has enough points to qualify for a reward tier.

Step 2: Get discount details for the reward tier. Call RetrieveCatalogObject in the Catalog API to get the PRICING_RULE, DISCOUNT, and PRODUCT_SET catalog objects that define the discount for each qualifying reward tier. You must retrieve these objects at a specific catalog version.

Step 3: Get current product information. Call BatchRetrieveCatalogObjects in the Catalog API to get the CATEGORY and ITEM_VARIATION catalog objects that the discount applies to. This step ensures that you show the most recent category or item information to the buyer.

Step 1: Get the reward tiers for the loyalty program Permalink Get a link to this section

To get the reward tiers for a loyalty program, call the ListLoyaltyPrograms endpoint.

List Loyalty Programs
  • 1
  • 2
  • 3
  • 4
curl https://connect.squareupsandbox.com/v2/loyalty/programs \
  -H 'Square-Version: 2021-03-17' \
  -H 'Authorization: Bearer {ACCESS_TOKEN}' \
  -H 'Content-Type: application/json'

The following is an excerpt of a ListLoyaltyProgram response. The reward_tiers field contains a list of LoyaltyProgramRewardTier objects that represent the reward tiers in the loyalty program.

{
  "programs": [
    {
      "id": "946651f0-229e-488c-bb97-183e3example",
      "status": "ACTIVE",
      "reward_tiers": [
        {
          "id": "e1b39225-9da5-43d1-a5db-782c4example",
          "points": 10,
          "name": "10% off entire sale",
          "definition": {
            "scope": "ORDER",
            "discount_type": "FIXED_PERCENTAGE",
            "percentage_discount": "10"
          },
          "created_at": "2020-04-20T16:55:11Z",
          "pricing_rule_reference": {
            "object_id": "74C4JSHESNLTB2A7Iexample",
            "catalog_version": "160548example"
          }
        }
      ],
      ...
    }
  ]
}

Each reward tier in the response contains the following fields:

  • id. The reward tier ID that you use to create a loyalty reward for a buyer.

  • points. The number of points that must be redeemed to claim the reward.

  • name. The seller-defined name for the reward tier.

  • pricing_rule_reference. The specific version of a PRICING_RULE catalog object that defines the discount details for the reward tier.

Note

The ListLoyaltyPrograms response currently includes the deprecated definition field, which is replaced by the pricing_rule_reference field. You should use pricing_rule_reference.

Step 2: Get the discount details for the reward tier Permalink Get a link to this section

To get the discount details for a reward tier, call the RetrieveCatalogObject endpoint in the Catalog API. You must retrieve these objects at a specific catalog version. The request must:

  • Include the object_id and catalog_version from the corresponding pricing_rule_reference field.

  • Set include_related_objects to true to return all catalog objects that define discount details.

Retrieve Catalog Object
  • 1
  • 2
  • 3
  • 4
curl https://connect.squareupsandbox.com/v2/catalog/object/74C4JSHESNLTB2A7Iexample?include_related_objects=true&catalog_version=160548example \
  -H 'Square-Version: 2021-03-17' \
  -H 'Authorization: Bearer {ACCESS_TOKEN}' \
  -H 'Content-Type: application/json'

The following table describes where you can find discount details in the PRICING_RULE, DISCOUNT, and PRODUCT_SET catalog objects from the response:

Discount propertyCatalog API mapping
Type of discount offered by the reward tierThe discount_data.discount_type field of the DISCOUNT catalog object.

Valid values are FIXED_AMOUNT or FIXED_PERCENTAGE.
Fixed percentage of a FIXED_PERCENTAGE discountThe discount_data.percentage field of the DISCOUNT catalog object.

This value is a string representation of a decimal number. For example, a 7.25% discount is represented as "7.25".
Maximum money amount of a FIXED_PERCENTAGE discountThe discount_data.maximum_amount_money field of the DISCOUNT catalog object.

Optional. This field is a Money object, which specifies amount using the smallest denomination.
Fixed money amount of a FIXED_AMOUNT discountThe discount_data.amount_money field of the DISCOUNT catalog object.

This field is a Money object, which specifies amount using the smallest denomination.
Scope of the rewardThe discount_target_scope field of the PRICING_RULE catalog object and the product_set_data field of the PRODUCT_SET catalog object. You can determine which items the discount applies to, as follows:

If the discount applies to the entire order:
discount_target_scope is WHOLE_PURCHASE.
product_set_data contains "all_products": true.

If the discount applies to specific categories of items:
discount_target_scope is LINE_ITEM.
product_set_data contains a product_ids_any field that references one or more CATEGORY catalog objects.

If the discount applies to specific items:
discount_target_scope is LINE_ITEM.
product_set_data contains a product_ids_any field that references one or more ITEM_VARIATION catalog objects.

The following are example RetrieveCatalogObject responses for the different scopes.

Example response for the order scope (entire sale)

{
  "object": {
    "type": "PRICING_RULE",
    "id": "L63DXGEYPJJTAM5JBX32ZBR5",
    "updated_at": "2020-12-16T22:48:32.75Z",
    "version": 1602888512750,
    "is_deleted": false,
    "present_at_all_locations": true,
    "pricing_rule_data": {
      "discount_id": "ZSWOAGU2E75BO7OI33AFIUMU",
      "match_products_id": "UHB65AGFKMS2IYVA7KHXMDYU",
      "application_mode": "ATTACHED",
      "discount_target_scope": "WHOLE_PURCHASE"
    }
  },
  "related_objects": [
    {
      "type": "DISCOUNT",
      "id": "ZSWOAGU2E75BO7OI33AFIUMU",
      "updated_at": "2020-12-16T22:48:32.75Z",
      "version": 1602888512750,
      "is_deleted": false,
      "present_at_all_locations": true,
      "discount_data": {
        "name": "$10.00 Off Entire Sale",
        "discount_type": "FIXED_AMOUNT",
        "amount_money": {
          "amount": 1000,
          "currency": "USD"
        },
        "application_method": "MANUALLY_APPLIED"
      }
    },
    {
      "type": "PRODUCT_SET",
      "id": "UHB65AGFKMS2IYVA7KHXMDYU",
      "updated_at": "2020-12-16T22:48:32.75Z",
      "version": 1602888512750,
      "is_deleted": false,
      "present_at_all_locations": true,
      "product_set_data": {
        "all_products": true
      }
    }
  ]
}

Example response for the category scope

{
  "object": {
    "type": "PRICING_RULE",
    "id": "74C4JSHESNLTB2A7ITO5HO6F",
    "updated_at": "2020-12-16T00:26:42.527Z",
    "version": 1605486402527,
    "is_deleted": false,
    "present_at_all_locations": true,
    "pricing_rule_data": {
      "discount_id": "ZLSTXZUW6BJED7ZZON4QZ4F5",
      "match_products_id": "RJM4MYXSBWZICCRS4N2B63QV",
      "application_mode": "ATTACHED",
      "discount_target_scope": "LINE_ITEM",
      "max_applications_per_attachment": 1
    }
  },
  "related_objects": [
    {
      "type": "DISCOUNT",
      "id": "ZLSTXZUW6BJED7ZZON4QZ4F5",
      "updated_at": "2020-12-16T00:26:42.527Z",
      "version": 1605486402527,
      "is_deleted": false,
      "present_at_all_locations": true,
      "discount_data": {
        "name": "50% Off One Hot Drink",
        "discount_type": "FIXED_PERCENTAGE",
        "percentage": "50.0",
        "application_method": "MANUALLY_APPLIED",
        "maximum_amount_money": {
          "amount": 250,
          "currency": "USD"
        }
      }
    },
    {
      "type": "PRODUCT_SET",
      "id": "RJM4MYXSBWZICCRS4N2B63QV",
      "updated_at": "2020-12-16T00:26:42.527Z",
      "version": 1605486402527,
      "is_deleted": false,
      "present_at_all_locations": true,
      "product_set_data": {
        "product_ids_any": [
          "TWCJXQJZMIAJJ3MQJNCWLCQG"
        ],
        "quantity_exact": 1
      }
    },
    {
      "type": "CATEGORY",
      "id": "TWCJXQJZMIAJJ3MQJNCWLCQG",
      ...
    }
  ]
}

Example response for the item variation scope

{
  "object": {
    "type": "PRICING_RULE",
    "id": "AZH2XITJVQQTYSWSOEXIW57Z",
    "updated_at": "2020-12-08T21:01:37.385Z",
    "version": 1607461297385,
    "is_deleted": false,
    "present_at_all_locations": true,
    "pricing_rule_data": {
      "discount_id": "2PS6ILT65FVO6UY227FZ4HGM",
      "match_products_id": "P35KAELYZUVV7YHR3DLCWRJN",
      "application_mode": "ATTACHED",
      "discount_target_scope": "LINE_ITEM",
      "max_applications_per_attachment": 1
    }
  },
  "related_objects": [
    {
      "type": "DISCOUNT",
      "id": "2PS6ILT65FVO6UY227FZ4HGM",
      "updated_at": "2020-12-08T21:01:37.385Z",
      "version": 1607461297385,
      "is_deleted": false,
      "present_at_all_locations": true,
      "discount_data": {
        "name": "20% Off Coffee Drinks",
        "discount_type": "FIXED_PERCENTAGE",
        "percentage": "20.0",
        "application_method": "MANUALLY_APPLIED"
      }
    },
    {
      "type": "PRODUCT_SET",
      "id": "P35KAELYZUVV7YHR3DLCWRJN",
      "updated_at": "2020-12-08T21:01:37.385Z",
      "version": 1607461297385,
      "is_deleted": false,
      "present_at_all_locations": true,
      "product_set_data": {
        "product_ids_any": [
          "QBIAVB2BWSPT2PYLPNAFJZ36",
          "MHXTR5VRKLXN767RCBTNB7YJ"
        ],
        "quantity_exact": 1
      }
    },
    {
      "type": "ITEM_VARIATION",
      "id": "QBIAVB2BWSPT2PYLPNAFJZ36",
      ...
    },
    {
      "type": "ITEM_VARIATION",
      "id": "MHXTR5VRKLXN767RCBTNB7YJ",
      ...
    }
  ]
} 

The following considerations apply to reward tier discount integrations:

  • Developers cannot use Square APIs to manage loyalty programs or reward tiers. To ensure that the discount details for a reward tier cannot be changed using the Catalog API, Square pins the corresponding pricing rule to a PRICING_RULE catalog object at a specific catalog version.

    When a seller updates the reward tier in the Seller Dashboard or Point of Sale application, a new pricing rule is created.

  • The Loyalty API does not use all fields in the PRICING_RULE, DISCOUNT, and PRODUCT_SET catalog objects. You can ignore fields that are not listed in the Catalog API mapping table, such as application_mode, max_applications_per_attachment, and application_method.

  • You should use the RetrieveCatalogObject endpoint to get discount details for a reward tier. Although the BatchRetrieveCatalogObject endpoint can also return catalog objects at a specific version, a single call might not return all pricing rules for a loyalty program because the corresponding PRICING_RULE catalog objects might belong to different versions.

Step 3: Get current product information Permalink Get a link to this section

To get the current product information for the catalog objects that are eligible for the discount, call the BatchRetrieveCatalogObjects or RetrieveCatalogObject endpoint in the Catalog API. This step ensures that you retrieve all updates made since the catalog version specified in the previous request, such as changes to an item's price.

Note

This step applies to CATEGORY and ITEM_VARIATION scopes; it does not apply to ORDER scopes. Depending on your application flow, you might have already retrieved the most current category or item information and can skip this step.

For this call, the request should:

  • Include the IDs from the product_set_data.product_ids_any field of the PRODUCT_SET catalog object from the previous request.

  • Not include the catalog_version field.

Batch Retrieve Catalog Objects
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
curl https://connect.squareupsandbox.com/v2/catalog/batch-retrieve \
  -X POST \
  -H 'Square-Version: 2021-03-17' \
  -H 'Authorization: Bearer {ACCESS_TOKEN}' \
  -H 'Content-Type: application/json' \
  -d '{
    "object_ids": [
      "QBIAVB2BWSPT2PYLPexample",
      "MHXTR5VRKLXN767RCexample"
    ]
  }'

Webhooks Permalink Get a link to this section

The Loyalty API support webhooks for the following events. All events require the LOYALTY_READ permission. For more information, Features of Square Webhooks.

Event Description
loyalty.account.created Published when a loyalty account is created for a buyer. A loyalty account can be created using any of the following methods and they all publish this event:
  • Using the Loyalty API CreateLoyaltyAccount endpoint.
  • The buyer might enroll in the program at the Square point of sale.
  • Manually on the Seller Dashboard.
  • The seller might use the Customer Directory merge feature to merge two customer accounts into one account. In this process, sellers might merge the two corresponding Square loyalty accounts by creating a new account and deleting existing accounts.
loyalty.account.updated Published for any updates to a buyer's existing loyalty account. For example:
  • If the seller updates the phone number associated with a loyalty account using the Seller Dashboard. For more information, see Square Loyalty FAQ.
  • Any change in the loyalty point balance, such as points added for visits, points expiration, or a manual adjustment to the point balance that a seller might perform.
  • The customer ID of the loyalty account changes. Perhaps the loyalty account moves to another customer.
loyalty.account.deleted Published when a loyalty account is deleted. The published event does not contain the customer_id that was associated with the (deleted) account. The following actions to delete the account can publish this event:
  • The seller uses the Seller Dashboard to delete an account.
  • The seller might use the Customer Directory merge feature to merge two customer accounts into one account. In this process, sellers might merge the two corresponding Square loyalty accounts by creating a new account and deleting existing accounts.
loyalty.program.updated Published when the loyalty program is updated from the Seller Dashboard.
loyalty.event.created Square loyalty maintains a ledger of events occurring in the lifetime of a loyalty account of a buyer. Square publishes this webhook for each loyalty event Square logs to the ledger. Loyalty events are immutable, which means they are never updated or deleted. For example, when a buyer redeems a reward and then returns it, a CREATE_REWARD event and a DELETE_REWARD event are published separately. Similarly, when a purchase that accrued points is refunded, the deduction of points publishes the ADJUST_POINTS event.

Requirements and limitations Permalink Get a link to this section

The following requirements and limitations apply when integrating the Square loyalty program in applications using the Loyalty API. For more information about the loyalty program, including pricing, see Square Loyalty.

Requirements Permalink Get a link to this section

  • In addition to setting up the loyalty program using the Square Seller Dashboard, the seller must be an active subscriber to a Square loyalty program. If a seller is not an active subscriber, the CreateLoyaltyAccount, AdjustLoyaltyPoints, and AccumulateLoyaltyPoints endpoints return BAD_REQUEST errors (other loyalty endpoints fail). Seller accounts must be in a market where Square Loyalty is available. For more information, see Square Loyalty International Availability.

    A seller can verify the subscription status in the Seller Dashboard by choosing Business under Accounts & Settings. The ListLoyaltyPrograms also returns the subscription status.

    Loyalty Subscription Status

    If a seller is not an active subscriber, the Seller Dashboard shows a banner with an appropriate message.

Limitations Permalink Get a link to this section

  • Currently, there can be only one loyalty program in a seller account.

  • Loyalty programs can only be created and updated on the Loyalty tab of the Seller Dashboard.

    If a reward tier references an invalid catalog item, or another condition prevents the Loyalty API from converting a reward tier, the ListLoyaltyPrograms endpoint returns an UNSUPPORTED_LOYALTY_REWARD_TIER error. If this occurs, contact Developer Support, join our Slack, or reach out to your Square Account Manager.

  • While redeeming multiple rewards of different tiers on the same order is supported, redeeming multiple rewards of the same tier on the same order is not supported.

  • When redeeming multiple rewards on the same order, only one reward can apply to each line item. Consequently, redeeming multiple whole purchase rewards leads to the highest discount being picked and others being ignored.

Advanced usage Permalink Get a link to this section

This section explains the following Loyalty API concepts:

Deferred reward creation Permalink Get a link to this section

As explained in the Redeem loyalty rewards section, after a reward is created, until it reaches its terminal state, Square reserves the loyalty points (the buyer cannot use the points to get a discount on any other purchases). The following actions set the reward to its terminal state:

  • A buyer can pay for the order and get the discount. In this case, the API removes the points permanently from the buyer's loyalty account and sets the reward to its terminal state of REDEEMED.

  • Your application might call DeleteLoyaltyReward to delete the reward. In this case, the API sets the reward to its terminal state of DELETED. The buyer can use the points to get discounts on other purchases.

Sometimes your application might want to show the buyer the effect of applying a reward on a purchase, for preview, without actually creating a reward (and avoid locking points). For example, consider the following eCommerce checkout flow:

  • Buyers can add items to the cart and apply available discounts to see the effect on purchase price. The buyers can keep the cart open for a long time without paying for it. Buyers might prefer to defer reward creations until they are truly ready to place an order.

  • Buyers can also walk away from the cart. Now you have permanently dangling reward objects.

In such scenarios, the application can use the CalculateOrder endpoint (the Orders API) to create a copy of an order with the specific rewards applied. The application can then show the buyer the effect of applying the discounts for preview. Later, when the buyer is ready to make a purchase, your application can call CreateLoyaltyReward to create a reward.

The following is an example CalculateOrder request:

Calculate Order
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
curl https://connect.squareupsandbox.com/v2/orders/calculate \
  -X POST \
  -H 'Square-Version: 2021-03-17' \
  -H 'Authorization: Bearer {ACCESS_TOKEN}' \
  -H 'Content-Type: application/json' \
  -d '{
    "order": {
      "line_items": [
        {
          "name": "Unisex Poncho",
          "quantity": "1",
          "base_price_money": {
            "amount": 4200,
            "currency": "USD"
          }
        }
      ],
      "location_id": "S8GWD5example"
    },
    "proposed_rewards": [
      {
        "id": "some-random-id",
        "reward_tier_id": "b525f003-47ea-43aa-bb18-6d12Cexample"
      }
    ]
  }'

For the order object, you can provide an existing order or a draft order that is not yet created:

  • Existing order. If you know the order ID, you can call RetrieveOrder to retrieve the order and pass the entire order object in the request body.

  • Draft order. You can build a draft order object and pass it in the request body, as shown in the preceding example (an order without an order ID). Use the same object that you would send to the CreateOrder endpoint.

For the proposed_rewards field, provide the following:

  • reward_tier_id. The ID of the reward tier from the loyalty program.

  • id. A random string that serves as the ID for the simulated reward.

The following is an example response that provides a preview of the order with the applied discount.

{
  "order": {
    "location_id": "S8GWD5example",
    "line_items": [
      {
        "uid": "HFROFMx2MckcvMexample",
        "quantity": "1",
        "name": "Unisex Poncho",
        "base_price_money": {
          "amount": 4200,
          "currency": "USD"
        },
        "gross_sales_money": {
          "amount": 4200,
          "currency": "USD"
        },
        "total_tax_money": {
          "amount": 0,
          "currency": "USD"
        },
        "total_discount_money": {
          "amount": 420,
          "currency": "USD"
        },
        "total_money": {
          "amount": 3780,
          "currency": "USD"
        },
        "variation_total_price_money": {
          "amount": 4200,
          "currency": "USD"
        },
        "applied_discounts": [
          {
            "uid": "QcKsxjGUDfNWZTexample",
            "discount_uid": "XOzfxZ6LaeTjL66example",
            "applied_money": {
              "amount": 420,
              "currency": "USD"
            }
          }
        ]
      }
    ],
    "discounts": [
      {
        "uid": "XOzfxZ6LaeTjL66example",
        "catalog_object_id": "RU4L3FVPCAZ5WR2SKexample",
        "name": "10% off entire sale (up to $50.00 off)",
        "percentage": "10.0",
        "applied_money": {
          "amount": 420,
          "currency": "USD"
        },
        "type": "FIXED_PERCENTAGE",
        "scope": "LINE_ITEM",
        "reward_ids": [
          "random-reward-id"
        ],
        "pricing_rule_id": "JNKW5IZGLA6JI2EFWexample"
      }
    ],
    "created_at": "2021-02-27T00:12:52.857Z",
    "updated_at": "2021-02-27T00:12:52.857Z",
    "state": "OPEN",
    "version": 1,
    "total_tax_money": {
      "amount": 0,
      "currency": "USD"
    },
    "total_discount_money": {
      "amount": 420,
      "currency": "USD"
    },
    "total_tip_money": {
      "amount": 0,
      "currency": "USD"
    },
    "total_money": {
      "amount": 3780,
      "currency": "USD"
    },
    "total_service_charge_money": {
      "amount": 0,
      "currency": "USD"
    },
    "net_amounts": {
      "total_money": {
        "amount": 3780,
        "currency": "USD"
      },
      "tax_money": {
        "amount": 0,
        "currency": "USD"
      },
      "discount_money": {
        "amount": 420,
        "currency": "USD"
      },
      "tip_money": {
        "amount": 0,
        "currency": "USD"
      },
      "service_charge_money": {
        "amount": 0,
        "currency": "USD"
      }
    },
    "rewards": [
      {
        "id": "some-random-id",
        "reward_tier_id": "b525f003-47ea-43aa-bb18-6d12Cexample"
      }
    ]
  }
}

Note the following fields in the response:

  • The total_discount_money, total_money, and discount_money amounts for the order and line item are updated with the applied discount.

  • The discounts field is added to the order. The discount contains information about the applied discount.

  • The applied_discounts field is added to the line item. The discount maps to the order-level discount.

  • The rewards field is added to the order. This simulated reward contains the reward and reward tier IDs you provided in the proposed_rewards field of the request.

Multiple redemption Permalink Get a link to this section

In a simple scenario, your application might choose to limit a buyer to redeem only one reward per order. The application flow is simple: show the rewards the buyer qualifies for (for example, a free coffee for 10 points and a free sandwich for 15 points) and have the buyer make the choice. After the buyer chooses the reward, adjust the point balance accordingly.

Your application can also allow buyers to redeem multiple rewards for a given order (for example, Square Point of Sale allows it). To offer a consistent experience, you might choose to implement multiple redemptions. As buyers explore redeeming one or more rewards (adding and removing rewards), your application needs to track the changes in the point balance. You have the following options:

  • If your application calls CreateLoyaltyReward (or DeleteLoyaltyReward) immediately each time a buyer adds (or removes) a reward, the loyalty account reflects the latest point balance. You can call RetrieveLoyaltyAccount to fetch the updated balance.

  • If your application uses the deferred reward creation approach as buyers add and remove rewards, the buyer's loyalty account does not accurately reflect the available point balance. Your application must keep track of the available point balance on the client side. You can use the following logic to track the available point balance on the client side:

    var availableBalance = getLoyaltyAccount().balance
    var allTiers = getLoyaltyProgram().reward_tiers
    var selectedTiers = []
    
    // Use this logic to determine which tiers are available to the buyer
    function getAvailableTiers() {
      var availableTiers = []
      for (tier : allTiers) {
        if (availableBalance >= tier.points) {
          availableTiers.push(tier)
        }
      }
      return availableTiers
    }
    
    // Use this logic when the buyer selects a tier
    function selectTier(rewardTier) {
      selectedTiers.push(rewardTier)
      availableBalance -= rewardTier.points
    }
    

When you apply multiple rewards, the Square pricing engine places rewards on the order in ways that maximize the reward value in the buyer's favor. As discounts are added to the order that the buyer placed, they can get rearranged as you add more rewards. For example, consider the following:

  • Suppose your cart has one tea and one coffee and the tea is more expensive than the coffee.

  • Suppose you have two rewards: one reward gives a free drink and another reward gives a free tea.

If you first apply the free drink reward, it discounts the tea because it is more expensive. Later, if you add the free tea reward, the discount previously added for the free drink reward is moved to discount the coffee, so that the free tea reward discount can be applied to the tea.

Loyalty events Permalink Get a link to this section

Square loyalty maintains a ledger of events that occur during the lifetime of a buyer's loyalty account. Each point balance change is recorded in the ledger. For example, each of the following activities causes an event to be added to the ledger:

  • A buyer earns points.

  • A buyer redeems a reward.

  • Loyalty points expire.

Square loyalty provides the SearchLoyaltyEvents endpoint to search the ledger for events. The Seller Dashboard uses the ledger to display information. You can also use this endpoint to query the ledger to show status information about your buyer-facing loyalty status page.

In the SearchLoyaltyEvents request, you specify query filters. The endpoint processes the filters as follows:

  • Uses a logical AND to evaluate multiple filters (for example, loyalty_account_filter and date_time_filter).

  • Uses a logical OR to evaluate repeated fields (for example, location_ids).

The following is an example SearchLoyaltyEvents request. The endpoint processes the specified filter as follows:

  • Performs a logical AND of these query filters: loyalty_account_filter, date_time_filter, type_filter, and location_filter.

  • Performs a logical OR for repeated field values: types and location_ids.

The endpoint returns all loyalty events for the loyalty account of the buyer ordered by created_at the timestamp in a descending order.

Search Loyalty Events
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
curl https://connect.squareupsandbox.com/v2/loyalty/events/search \
  -X POST \
  -H 'Square-Version: 2021-03-17' \
  -H 'Authorization: Bearer {ACCESS_TOKEN}' \
  -H 'Content-Type: application/json' \
  -d '{
    "query": {
      "filter": {
        "loyalty_account_filter": {
          "loyalty_account_id": "716cefbc-3d71-4d7c-bdc8-9c7f8example"
        },
        "date_time_filter": {
          "created_at": {
            "start_at": "2020-01-01T00:00:00Z",
            "end_at": "2020-12-31T00:00:00Z"
          }
        },
        "type_filter": {
          "types": [
            "ACCUMULATE_POINTS",
            "CREATE_REWARD"
          ]
        },
        "location_filter": {
          "location_ids": [
            "M3AK81example",
            "LB43CCexample"
          ]
        }
      }
    },
    "limit": 30
  }'