> ## Documentation Index
> Fetch the complete documentation index at: https://developers.appstle.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Appstle Loyalty API changelog

> Dated changes to the Appstle Loyalty Admin API, Storefront API, Shopify Flow and Partner Framework: new endpoints, fields and values, and what to do about each.

This page lists every developer-facing change to the Loyalty Admin API, the Storefront API, Shopify Flow and the Partner Framework, newest first. Each entry is dated the day the change reached production.

* **Additive changes** (new endpoints, new response fields, new optional parameters, new values of an existing field) don't break a client that ignores what it doesn't know. Parse JSON leniently and don't treat enum fields as closed lists.
* Entries tagged **Action required** may need a code change in your integration. Each one says who is affected and what to do.
* The API reference is generated from the live API, so it always shows the current contract. The Admin API base URL is `https://loyalty-admin.appstle.com`, and Storefront API paths start with `/loyalty/cp/api/`.

**November 1, 2026**

Points, approvals, birth dates

## Stricter validation on points, approvals and birth dates

These calls now return `400` for requests they used to accept. The problem `title` in the response says what's wrong.

* **`POST /api/external/add-points`** and **`POST /api/external/remove-points`** reject a `points` value of `0` or less with the title `Points must be greater than 0`. Until now, `0` and negative values were accepted. To take points away, call `remove-points` with a positive number.
* **`add-points`** rejects an `earnRuleId` whose earn rule is turned off, with the title `Point Earn Rule is not active`. It already rejected an unknown rule and a rule outside its scheduled dates. The same check applies to the **Rule Id** field of the Shopify Flow **Add Points** action.
* **`PUT /api/external/approve-pending-transactions`** approves every pending transaction only when you set `approveAllPendingTransactions=true`. A call that sends no approval list (or an empty one) and leaves out `approveAllPendingTransactions` now returns `400` with the title `Please provide an approval list or set approveAllPendingTransactions to true`. Until now, that call returned `204` and approved every pending transaction in the store.
* **`PUT /api/external/update-customer-birth-date`** validates `dob` the way the Storefront API already did. A missing `dob`, a year before 1900 or a date in the future returns `400` with the title `UserGeneratedError:Please enter a valid birthdate.` When you send `dobYearProvided: false`, only a missing `dob` is rejected. An unknown `customerId` now returns `400` with the title `Customer not found using this customerID` instead of an empty `500`.

**Who is affected:** integrations that send `0` or negative `points`, pass the ID of a turned-off earn rule, call `approve-pending-transactions` without a list to approve everything, or send birth dates without validating them.

**Action required:** send a positive `points` value, and send an `earnRuleId` only for an active earn rule. To approve every pending transaction, set `approveAllPendingTransactions=true`. Validate birth dates before you send them. Handle `400` on these calls and log the `title`.

### Reference corrections

The API reference now matches what these endpoints have always done. Their behavior didn't change.

* **`GET /api/external/customer-loyalty`** returns `400`, not `404`, when the customer isn't found. The title is `Customer not found using this customerID`, `Customer not found using this email`, or `Customer info not found` when you send neither `customerId` nor `customerEmail`. Without `status`, only `UNUSED` rewards are returned. `status` takes one or more of `UNUSED`, `USED`, `EXPIRED`, `SUBSCRIPTION_ACTIVE`, `REFUNDED` and `CANCELED`, separated by commas, and any other value returns `400`.
* **`PUT /api/external/check-discount`** returns the reward whatever its status, so check `status` before you apply the code. An unknown code returns `400` with the title `Discount is not found`, not `404`.
* When the earn rule you pass to `add-points` requires manual approval, the points stay pending until you approve them with `approve-pending-transactions`.

Reference: [Add points to customer account](https://developers.appstle.com/loyalty-admin-api/points-&-earn-rules/add-points-to-customer-account.md), [Remove points from customer account](https://developers.appstle.com/loyalty-admin-api/points-&-earn-rules/remove-points-from-customer-account.md), [Approve pending point transactions](https://developers.appstle.com/loyalty-admin-api/points-&-earn-rules/approve-pending-point-transactions.md), [Update customer birth date](https://developers.appstle.com/loyalty-admin-api/customer-enrollment-&-profile/update-customer-birth-date.md), [Get customer loyalty details](https://developers.appstle.com/loyalty-admin-api/customer-enrollment-&-profile/get-customer-loyalty-details.md), [Check discount code validity](https://developers.appstle.com/loyalty-admin-api/redemptions-&-discount-codes/check-discount-code-validity.md).

**October 30, 2026**

Social media points

## Social media claims return the link to open

When `POST /loyalty/cp/api/claim-social-media-points` awards points, the response includes `redirectUrl`, the social media link set on the earn rule. Open it for the customer after the claim. A refused claim returns `redirectUrl` as `null`.

Reference: [Claim social media reward points](https://developers.appstle.com/loyalty-storefront-api/points-&-earn-rules/claim-social-media-reward-points.md).

**October 29, 2026**

Flow points actions

## Add Points and Remove Points apply once per action run

Shopify Flow resends an action with the same action run ID when it doesn't get a response in time. The **Add Points** and **Remove Points** actions now apply the points once per action run, so a resent action no longer adds or removes them a second time.

**Who is affected:** workflows that guard against doubled points, for example with a tag check before the action. You can remove that guard.

Reference: [Shopify Flow](https://developers.appstle.com/loyalty/shopify-flow.md).

**October 27, 2026**

Rewards

## Return an unused reward for its points

New `POST /loyalty/cp/api/return-reward/{rewardId}` lets a signed-in customer hand back a reward they haven't used and get its points back. The discount code is removed from Shopify and the points are credited as a `REDEEM_REVERSAL` transaction. The response is the customer's updated loyalty details, in the same shape as `redeem-points` returns.

It works only when the merchant has turned on **Let customers return unused rewards** in **Advanced Settings**. Only a discount code the customer bought with points can be returned, and only while it's unused and hasn't expired. Store credit and gifted rewards (VIP tier, birthday, referral) can't be returned.

Otherwise the call returns `400`. The `title` explains why, and `message` is `error.` followed by one of `rewardReturnDisabled`, `idnotfound`, `rewardNotUnused`, `rewardNotReturnable`, `rewardExpired`, `rewardUsageUnverified` or `rewardActionInProgress`. The last two are temporary, so the customer can try again.

**Who is affected:** storefronts that list a customer's rewards. Nothing changes until you call the endpoint.

Reference: [Return an unused reward for its points](https://developers.appstle.com/loyalty-storefront-api/redemptions-&-discount-codes/return-an-unused-reward-for-its-points.md).

**October 24, 2026**

API keys

## Merchants can revoke the legacy API key

In the Appstle admin, under **More → API Integrations**, the **Legacy API Key** card has a **Revoke** button. After a merchant revokes it, every request that sends that key returns `401` with `{"status":401,"detail":"Invalid authentication"}`, the same response as any other wrong key (see [Error responses](https://developers.appstle.com/loyalty/authentication.md#error-responses)). Appstle doesn't issue a replacement. Keys that start with `apst_` aren't affected.

**Who is affected:** integrations that still send the legacy key, the one the app showed before `apst_` keys existed.

**Action required:** create an `apst_` key under **More → API Integrations** and move your integration to it before the merchant revokes the legacy key.

**October 22, 2026**

Widget labels

## Hover labels for reward activation and expiry

`GET /api/external/widget-labels` returns two new label fields: `rewardActivationDelayHoverLabel` and `rewardExpirationHoverLabel`.

Reference: [List storefront widget label translations](https://developers.appstle.com/loyalty-admin-api/storefront-widgets-&-labels/list-storefront-widget-label-translations.md).

**October 21, 2026**

Redemptions, earn rules

## Redeeming a reward can fail when its Shopify discount has expired

`POST /api/external/redeem-points` and `POST /loyalty/cp/api/redeem-points` now return `400` with the title `UserGeneratedError:This reward is temporarily unavailable. Please try again later or contact the store.` when the Shopify discount behind the redeem rule has expired. Before, Appstle issued a code that Shopify then rejected at checkout, and the customer lost the points. Since October 22, Appstle first tries to renew the expired discount and returns this error only when it can't. The customer's points aren't deducted when the call fails.

Show the title to the customer or log it, and retry later. Tell the merchant to check the redeem rule's discount in Shopify.

## Subscription and sign-up settings on earn rules

`GET /api/external/point-earn-rules` returns three new booleans on each rule:

* `earnOnInitialSubscriptionOrder` and `earnOnRecurringSubscriptionOrder` say whether a purchase rule awards points for subscription products on a subscription's first order and on its renewal orders. When one is missing, treat it as `true`.
* `awardExistingCustomers` applies to the sign-up rule. When it's `true`, customers who joined before the rule was created can still earn its points. When it's missing, treat it as `false`.

Reference: [Redeem customer points for rewards](https://developers.appstle.com/loyalty-admin-api/redemptions-&-discount-codes/redeem-customer-points-for-rewards.md), [Get all point earning rules](https://developers.appstle.com/loyalty-admin-api/points-&-earn-rules/get-all-point-earning-rules.md).

**October 18, 2026**

Widget labels

## Greeting icon in widget settings

`GET /api/external/widget-labels` returns a new field, `customerGreetingsIcon`.

Reference: [List storefront widget label translations](https://developers.appstle.com/loyalty-admin-api/storefront-widgets-&-labels/list-storefront-widget-label-translations.md).

**October 16, 2026**

Earn rules

## Recurring schedule on earn rules

`GET /api/external/point-earn-rules` returns `ruleRecurrence`, a JSON string that limits a rule to certain days on top of its start and end dates. It holds two lists: `weeks`, the weeks of the month (week 1 is days 1 to 7), and `days`, the days of the week (`MONDAY` to `SUNDAY`). Both are read in the store's time zone, and an empty list means any week or any day. `ruleRecurrence` is empty when a rule has no recurrence.

Reference: [Get all point earning rules](https://developers.appstle.com/loyalty-admin-api/points-&-earn-rules/get-all-point-earning-rules.md).

**October 15, 2026**

Referrals

## Error key for self-referrals

* `GET` and `POST /loyalty/cp/api/referral-rules/accept-offer` return `errorKey` `SELF_REFERRAL` when a customer tries to accept their own referral offer. The response was already `400` with `status` `FAILED`. Until now, `errorKey` was empty for this case.
* `GET /api/external/widget-labels` returns `selfReferralErrorLabel`, the text to show for that error.

**Who is affected:** clients that map `errorKey` to a fixed list of values.

**Action required:** accept values you don't recognize.

Reference: [Accept referral offer (POST)](https://developers.appstle.com/loyalty-storefront-api/referrals/accept-referral-offer-post.md).

**October 6, 2026**

Widget labels

## Label for unused rewards

`GET /api/external/widget-labels` returns a new field, `unusedRewardsLabel`.

Reference: [List storefront widget label translations](https://developers.appstle.com/loyalty-admin-api/storefront-widgets-&-labels/list-storefront-widget-label-translations.md).

**October 5, 2026**

Referrals

## `existingCustomer` on referral offers

`GET` and `POST /loyalty/cp/api/referral-rules/accept-offer` return `existingCustomer` (boolean). On success, it's `true` when the referred email already belongs to a customer of the store. `GET /api/external/widget-labels` returns `referralExistingCustomerAcceptedText`.

Reference: [Accept referral offer (POST)](https://developers.appstle.com/loyalty-storefront-api/referrals/accept-referral-offer-post.md).

**October 3, 2026**

Redemptions

## Turned-off redeem rules can't be redeemed

`POST /api/external/redeem-points` and `POST /loyalty/cp/api/redeem-points` return `400` with the title `Redeem rule not available` when the redeem rule is turned off, the same title as for an unknown rule. Until now, a turned-off rule could still be redeemed through the API.

**Who is affected:** integrations that store redeem rule IDs and keep using them after the merchant turns a rule off.

**Action required:** take redeem rule IDs from `GET /api/external/point-redeem-rules`, which lists only active rules, and handle `400` on `redeem-points`.

Reference: [Redeem customer points for rewards](https://developers.appstle.com/loyalty-admin-api/redemptions-&-discount-codes/redeem-customer-points-for-rewards.md), [Get all point redemption rules](https://developers.appstle.com/loyalty-admin-api/redemptions-&-discount-codes/get-all-point-redemption-rules.md).

**October 2, 2026**

Rewards

## Delayed reward activation

* Redeem rules (`GET /api/external/point-redeem-rules`) return `activationDelayDays`, the number of days before a redeemed reward becomes usable.
* Rewards in `customer-loyalty`, `add-points`, `add-credits`, `redeem-points`, `remove-points` and `update-discount-code-status` responses carry `activeFrom`, the date the reward becomes usable. `check-discount` and `update-discount` return `activeFrom` too.
* The Storefront API returns `rewards[].activeFrom` from `GET /loyalty/cp/api/customer-loyalty` and `POST /loyalty/cp/api/redeem-points`.

**Who is affected:** integrations that show reward codes to customers.

**Action required:** check `activeFrom` first so you don't present a code that doesn't work yet.

Reference: [Get all point redemption rules](https://developers.appstle.com/loyalty-admin-api/redemptions-&-discount-codes/get-all-point-redemption-rules.md), [Get customer loyalty details](https://developers.appstle.com/loyalty-admin-api/customer-enrollment-&-profile/get-customer-loyalty-details.md).

**October 1, 2026**

Storefront API writes, social media points

## Storefront API writes need `X-Requested-With`

Every Storefront API request that isn't a `GET`, `HEAD` or `OPTIONS` must send the header `X-Requested-With: XMLHttpRequest`. Without it, the call returns `401` with `{"status":401,"detail":"Invalid authentication"}`, even for a signed-in customer.

**Who is affected:** storefront code that sends a Storefront API `POST`, `PUT` or `DELETE` without that header.

**Action required:** add `X-Requested-With: XMLHttpRequest` to every Storefront API write call.

## New refusal reason for social media claims

`POST /loyalty/cp/api/claim-social-media-points` can return `awarded` `false` with the `reason` `CREDIT_PENDING_RECONCILIATION`. This happens only for earn rules that give store credit: the claim is recorded, but Appstle couldn't confirm that the store credit was added. The `message` asks the customer to contact support before trying again.

**Who is affected:** clients that map `reason` to a fixed list of values.

**Action required:** accept `reason` values you don't recognize, and show the `message` for them.

Reference: [Enroll customer in loyalty program](https://developers.appstle.com/loyalty-storefront-api/customer-enrollment-&-profile/enroll-customer-in-loyalty-program.md), [Claim social media reward points](https://developers.appstle.com/loyalty-storefront-api/points-&-earn-rules/claim-social-media-reward-points.md).

**September 30, 2026**

Redeem rules

## `excludeProductData` on redeem rules

`GET /api/external/point-redeem-rules` returns `excludeProductData` on each rule: a JSON string that lists the products and variants the reward's discount doesn't apply to. It's empty when nothing is excluded.

Reference: [Get all point redemption rules](https://developers.appstle.com/loyalty-admin-api/redemptions-&-discount-codes/get-all-point-redemption-rules.md).

**September 23, 2026**

Widget labels

## Earn rule description labels

`GET /api/external/widget-labels` returns three new label fields: `buyProductRuleDescriptionLabel`, `buyProductFromCollectionRuleDescriptionLabel` and `earnRuleShowMoreLabel`.

Reference: [List storefront widget label translations](https://developers.appstle.com/loyalty-admin-api/storefront-widgets-&-labels/list-storefront-widget-label-translations.md).

**September 21, 2026**

Birthdays

## Birth year is optional on birthday updates

* `PUT /api/external/update-customer-birth-date` and `POST /loyalty/cp/api/update-customer-birth-date` accept `dobYearProvided` (boolean). When `false`, the year in `dob` is a placeholder and only the month and day are used to match birthday rewards. It defaults to `true` when omitted, so existing clients behave as before.
* `customer-loyalty`, `add-points`, `add-credits`, `redeem-points`, `remove-points`, `update-discount-code-status` and `update-customer-birth-date` responses return `dobYearProvided`, and so does `POST /loyalty/cp/api/update-customer-status`.
* The birthday earn rule returns `collectBirthYear`. When it's `false`, the merchant has turned off **Ask for birth year**, so collect only the month and day and send `dobYearProvided: false`. When it's missing, treat it as `true`.

**Who is affected:** integrations that collect birthdays without a year. Send `dobYearProvided: false` with a placeholder year in `dob`.

Reference: [Update customer birth date](https://developers.appstle.com/loyalty-admin-api/customer-enrollment-&-profile/update-customer-birth-date.md).

**September 17, 2026**

Point transactions

## New transaction rule `REDEEM_REVERSAL`

The `transactionRule` field of a point transaction can now be `REDEEM_REVERSAL`, recorded when the points from a redeemed reward are returned to the customer. It isn't counted as newly earned points. The other values are `ADJUSTMENT`, `EARN_RULE`, `REDEEM_RULE` and `REFERRAL`. It appears in `GET /api/external/point-transaction-history/{customer_id}` and `GET /loyalty/cp/api/transaction-by-shop`.

**Who is affected:** clients that map `transactionRule` to a fixed list of values.

**Action required:** accept values you don't recognize.

Reference: [Get customer point transaction history](https://developers.appstle.com/loyalty-admin-api/points-&-earn-rules/get-customer-point-transaction-history.md).

**September 16, 2026**

Birthdays

## Customers can't change a birth date that's already set

`POST /loyalty/cp/api/update-customer-birth-date` no longer changes a birth date the customer already has. It returns `400` with the title `UserGeneratedError:Birthdate has already been set. Please contact the store to update it.` and leaves the birth date as it is. A customer who has no birth date can still set one.

**Who is affected:** storefronts that let signed-in customers change their birthday.

**Action required:** don't offer customers a way to change a birth date once it's set. To change one, call `PUT /api/external/update-customer-birth-date` from your server, which doesn't have this restriction.

Reference: [Update customer birth date](https://developers.appstle.com/loyalty-storefront-api/customer-enrollment-&-profile/update-customer-birth-date.md), [Update customer birth date (Admin API)](https://developers.appstle.com/loyalty-admin-api/customer-enrollment-&-profile/update-customer-birth-date.md).

**September 14, 2026**

Redeem rules, customer updates

## Discount expiry on redeem rules

`GET /api/external/point-redeem-rules` returns `discountExpirationType` (`DAY`, `MONTH` or `YEAR`), `discountExpirationValue` and `enableDiscountExpirationOverride` on each rule.

## Birthday and status updates return only the profile

`POST /loyalty/cp/api/update-customer-birth-date` and `POST /loyalty/cp/api/update-customer-status` return a smaller customer object. Only the profile fields are filled in: `shop`, `customerId`, `firstName`, `lastName`, `displayName`, `email`, `dob`, `status` and `loyaltyProgramEnabled`. The counters `completedActivities`, `rewardClaimed`, `referrals` and `totalRevenue` are always `0`, and every other field is `null`, including `id`, `availablePoints`, `pendingPoints`, `availableStoreCredits` and `vipTier`. Until now, these calls returned the customer's full record. The API reference lists more fields, such as `totalEarnedPoints`, but they're always `null` here.

**Who is affected:** storefront code that reads points, store credit or the VIP tier from these responses.

**Action required:** after the call, read `availablePoints`, `storeCreditBalance` and `currentVipTier` from `GET /loyalty/cp/api/customer-loyalty`.

Reference: [Get all point redemption rules](https://developers.appstle.com/loyalty-admin-api/redemptions-&-discount-codes/get-all-point-redemption-rules.md), [Update customer birth date](https://developers.appstle.com/loyalty-storefront-api/customer-enrollment-&-profile/update-customer-birth-date.md), [Update customer loyalty status](https://developers.appstle.com/loyalty-storefront-api/customer-enrollment-&-profile/update-customer-loyalty-status.md).

**September 13, 2026**

Reviews, session checks

## Submit a review from an invitation

New `POST /loyalty/cp/api/submit-review/{reviewToken}` submits a product review from a review invitation. `reviewToken` comes from the invitation, and the body takes `rating` (1 to 5), `title` and `body`. An invalid token returns `400` with the title `Invalid review token`, and a rating outside 1 to 5 returns `400` with the title `Rating must be between 1 and 5`.

## Storefront requests must match the session

The Storefront API now checks the `customerId` and `shop` you send against the signed-in customer's session:

* A `customerId` in the request body or query string that isn't the signed-in customer's returns `403` with the `detail` `Customer does not match the authenticated session`. The bodies of `redeem-points`, `submit-review`, `update-customer-birth-date` and `update-customer-status` have a `customerId` field.
* A `shop` in the request body that names another store returns `403` with the `detail` `Shop does not match the authenticated session`. `submit-review` is the only endpoint whose body has a `shop` field.

Until now, these endpoints ignored both values and used the session's customer and store.

**Who is affected:** storefront code that sends `customerId` or `shop`, for example in a body copied from the API reference, whose examples still show `customerId`.

**Action required:** leave `customerId` and `shop` out of Storefront API requests. The session already identifies the customer and the store.

Reference: [Submit an invited review](https://developers.appstle.com/loyalty-storefront-api/store-front-resource/post-loyaltycpapisubmit-review.md), [Submit product review](https://developers.appstle.com/loyalty-storefront-api/reviews/submit-product-review.md), [Redeem customer loyalty points](https://developers.appstle.com/loyalty-storefront-api/redemptions-&-discount-codes/redeem-customer-loyalty-points.md), [Update customer birth date](https://developers.appstle.com/loyalty-storefront-api/customer-enrollment-&-profile/update-customer-birth-date.md), [Update customer loyalty status](https://developers.appstle.com/loyalty-storefront-api/customer-enrollment-&-profile/update-customer-loyalty-status.md).

**September 11, 2026**

Partner authentication

## One error for every failed partner authentication

`connect`, `initiate-connect`, `verify`, `disconnect` and `status` under `/api/partner/{partnerId}/` now return the same error for every authentication failure: `400` with the `errorKey` `TOKEN_INVALID` and the title `UserGeneratedError:Invalid partner credentials.` That covers a Partner ID that is wrong or belongs to a deactivated partner, a missing or wrong `X-Partner-Secret`, and an invalid HMAC signature or a timestamp more than 5 minutes off. Until now, an unknown or deactivated Partner ID returned `PARTNER_NOT_FOUND`, and each authentication failure had its own title, such as `UserGeneratedError:Invalid partner secret.`

**Who is affected:** partners whose code branches on `PARTNER_NOT_FOUND`, or on the title of a `TOKEN_INVALID` error.

**Action required:** stop handling `PARTNER_NOT_FOUND` and the old titles. On `TOKEN_INVALID` with the title `UserGeneratedError:Invalid partner credentials.`, check the Partner ID first, then the secret or HMAC key.

Reference: [Partner integration](https://developers.appstle.com/loyalty/partner-integration.md#error-codes).

**September 10, 2026**

Daily call allowance

## Optional daily call allowance per store

The Admin API (`/api/external/`) can now enforce a daily call allowance for a store. Appstle support sets it per store, and a store without one has no daily cap. Calls made with a partner token don't count toward it.

When a store uses up its allowance, every Admin API call made with its API key returns `429` until 00:00 UTC. The response has a `Retry-After` header with the number of seconds left until then, and this body (key order can vary):

```json theme={null}
{
  "status": 429,
  "detail": "Daily API call limit reached for this store. The allowance resets at 00:00 UTC."
}
```

The Admin API's other `429` responses, for the per-minute rate limit and for too many `PUT` calls at once, have no `Retry-After` header, and their body has an `error` field instead of `detail`.

**Who is affected:** integrations that make many Admin API calls a day with one store's API key.

**Action required:** when a `429` has a `Retry-After` header, stop calling until it elapses, because retrying sooner can't succeed. Keep exponential backoff for a `429` without one.

Reference: [Rate limits](https://developers.appstle.com/loyalty/integration-guide.md#rate-limits).

**September 8, 2026**

Widget labels

## Label for already claimed social actions

`GET /api/external/widget-labels` returns a new field, `socialAlreadyClaimedLabel`.

Reference: [List storefront widget label translations](https://developers.appstle.com/loyalty-admin-api/storefront-widgets-&-labels/list-storefront-widget-label-translations.md).

**September 7, 2026**

Credit expiry, widget labels

## Credit expiry date on point transactions

* Point transactions return `creditExpireAt`, the date the store credit from a transaction expires. It's set only when the store has credit expiry turned on. It appears in `GET /api/external/point-transaction-history/{customer_id}` and `GET /loyalty/cp/api/transaction-by-shop`.
* `GET /api/external/widget-labels` returns `dedicatedPageUnavailableLabel`.

Reference: [Get customer point transaction history](https://developers.appstle.com/loyalty-admin-api/points-&-earn-rules/get-customer-point-transaction-history.md).

**September 5, 2026**

Referrals, authentication errors

## Referral errors and a smaller referral history

* `GET` and `POST /loyalty/cp/api/referral-rules/accept-offer` return `errorKey`. When the offer is refused because the friend was already referred, the response is `400` with `status` `FAILED` and `errorKey` `ALREADY_REFERRED`.
* `GET /api/external/widget-labels` returns `alreadyReferredErrorLabel`, the text to show for that error.
* `GET /loyalty/cp/api/customer-referrals` returns less about each referral. `referredEmailId` is always masked, so `jane@example.com` comes back as `j**e@e******.com`. `description`, `referralRuleId`, `referralNote`, `referredNote` and `pixelData` are always `null`. Until now, these fields had values, and the email was masked only in some stores. `orderCount` is listed in the API reference but is always `null` on this endpoint.

**Who is affected:** storefronts that show a friend's full email, or read the fields that are now `null`, from the referral history.

**Action required:** show `referredEmailId` as it comes, and build the history from `status`, `createAt`, `claimAt`, `referralRewarded` and `referredRewarded`.

## New body for authentication errors, and `shop` must match the key

* When the API key or the customer's storefront session is missing or invalid, the Admin API and the Storefront API return `401` with `{"status":401,"detail":"Invalid authentication"}`. Until now, the body was an `application/problem+json` document with the title `Unauthorized`.
* An Admin API call with a `shop` parameter that names a store other than the API key's store returns `403` with the `detail` `Shop does not match the authenticated session`. Until now, `shop` was ignored.

**Action required:** treat any `401` as a credential problem without parsing its body, and leave `shop` out of Admin API calls, since your API key already identifies the store.

Reference: [Accept referral offer (POST)](https://developers.appstle.com/loyalty-storefront-api/referrals/accept-referral-offer-post.md), [Get customer referral history](https://developers.appstle.com/loyalty-storefront-api/referrals/get-customer-referral-history.md), [Error responses](https://developers.appstle.com/loyalty/authentication.md#error-responses).

**September 4, 2026**

Enrollment, reviews, redemptions

## Storefront enrollment no longer accepts a VIP tier

`POST /loyalty/cp/api/enroll-customer` no longer moves a customer into the VIP tier named in `vipTier`. A signed-in customer's request that includes `vipTier` now returns `400` with the title `VIP tier changes require merchant access.` Enrolling a customer without `vipTier` works as before.

## Stricter checks on reviews and redemptions

* `POST /loyalty/cp/api/submit-review` returns `400` with the title `Rating must be between 1 and 5` when `rating` is missing or outside 1 to 5. Until now, the rating wasn't checked.
* `POST /api/external/redeem-points` and `POST /loyalty/cp/api/redeem-points` return `400` with the title `Points to redeem must be greater than zero.` when the redeem rule's `type` is `DYNAMIC_DISCOUNT` and `points` is missing, `0` or negative. Until now, a missing `points` returned the title `UserGeneratedError:Please enter the number of points to redeem`.
* When a redeem rule is limited to customers with certain tags and Appstle can't read the customer's tags from Shopify, `redeem-points` returns `400` with the title `Unable to verify reward eligibility. Please try again.` Until now, the redemption went ahead without the tag check. The error is temporary.

**Who is affected:** storefronts that send `vipTier` when enrolling a customer or submit reviews without a rating, and integrations that redeem dynamic-discount rules.

**Action required:** remove `vipTier` from the enrollment body. Send a `rating` from 1 to 5 with every review. Send a positive `points` value for a dynamic-discount rule, and retry `redeem-points` after `Unable to verify reward eligibility. Please try again.`

Reference: [Enroll customer in loyalty program](https://developers.appstle.com/loyalty-storefront-api/customer-enrollment-&-profile/enroll-customer-in-loyalty-program.md), [Submit product review](https://developers.appstle.com/loyalty-storefront-api/reviews/submit-product-review.md), [Redeem customer points for rewards](https://developers.appstle.com/loyalty-admin-api/redemptions-&-discount-codes/redeem-customer-points-for-rewards.md).

**September 2, 2026**

Flow review points

## Reward Points for Reviews accepts any Product ID

The **Reward Points for Reviews** action now awards the points when **Product ID** is empty or isn't a Shopify product ID, for example when a review app passes a SKU or a handle. Until now, the action failed in those cases and awarded nothing.

**Who is affected:** workflows whose review app passes something other than a Shopify product ID as **Product ID**. Those actions now succeed.

Reference: [Reward Points for Reviews](https://developers.appstle.com/loyalty/shopify-flow.md#reward-points-for-reviews).

**August 20, 2026**

Social media points

## Social media claims say whether points were awarded

`POST /loyalty/cp/api/claim-social-media-points` now returns a body that says what happened. Until now, it returned `200` with an empty body whether or not the customer earned points. A refused claim still returns `200`, so read the body:

* `awarded` (boolean): `true` when the points were added.
* `reason` (string): `AWARDED`, or why the claim was refused: `CUSTOMER_EXCLUDED`, `RULE_NOT_FOUND`, `RULE_OUTSIDE_SCHEDULE`, `CUSTOMER_TAGS_UNAVAILABLE`, `RESTRICTED_CUSTOMER_TAG`, `MISSING_ALLOWED_CUSTOMER_TAG`, `IMPORTED_CUSTOMER`, `ALREADY_CLAIMED` or `NOT_ELIGIBLE`.
* `message` (string): a short description of the outcome.
* `points` (number): the points added. It's `null` when the claim is refused.

The API reference doesn't show this body yet.

Reference: [Claim social media reward points](https://developers.appstle.com/loyalty-storefront-api/points-&-earn-rules/claim-social-media-reward-points.md).

**August 10, 2026**

Earn rules

## Extra points for review photos and videos

`GET /api/external/point-earn-rules` returns `addAdditionalPhotoPoints`, `additionalPhotoPoints`, `addAdditionalVideoPoints` and `additionalVideoPoints` on review earn rules.

Reference: [Get all point earning rules](https://developers.appstle.com/loyalty-admin-api/points-&-earn-rules/get-all-point-earning-rules.md).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.