#Loyalty
These endpoints are called from a shopper's browser, so they take your publishable key rather than a secret one. Anything specific to one customer also takes a signed email and timestamp: see Identifying the customer for how to produce the signature.
GET /widgets/loyalty
Get the store's loyalty program: its tiers, ways to earn, offers, stamp cards and rewards, plus the signed-in shopper's own state when the request is signed
Query Parameters
apiPublishableKeystringrequiredYour organization's publishable key, from Settings, API keys.
customerEmailstring (email)The signed-in shopper's address. Leave it out for the guest catalog.
timestampstringThe Unix seconds the signature was made at. Required with customerEmail.
signaturestringThe HMAC of the email and timestamp. Required with customerEmail.
siteSlugstringWhich of the store's sites the widget is embedded on. Leave it out and the oldest site is used.
previewTokenstringA preview token from POST /loyalty-programs/{id}/preview. It adds the one draft program it names to the response, so a merchant can check a program before publishing it. The publishable key on its own never reaches a draft.
curl \
"https://api.cascade.dev/widgets/loyalty?apiPublishableKey=pk_YOUR_PUBLISHABLE_KEY"import { client } from "@cascade-commerce/api/widget/client"
import { getWidgetsLoyalty } from "@cascade-commerce/api/widget"
client.setConfig({
baseUrl: "https://api.cascade.dev",
})
const { data, error } = await getWidgetsLoyalty({
query: { apiPublishableKey: "pk_YOUR_PUBLISHABLE_KEY" },
})require "net/http"
uri = URI("https://api.cascade.dev/widgets/loyalty?apiPublishableKey=pk_YOUR_PUBLISHABLE_KEY")
req = Net::HTTP::Get.new(uri)
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
puts res.bodyimport json
from urllib.request import Request, urlopen
req = Request(
"https://api.cascade.dev/widgets/loyalty?apiPublishableKey=pk_YOUR_PUBLISHABLE_KEY",
)
with urlopen(req) as res:
print(json.load(res)){
"organizationName": "string",
"siteSlug": "string",
"authenticated": true,
"programs": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "string",
"pointsNames": {
"singular": "...",
"plural": "..."
},
"paused": true,
"draft": true,
"sectionsEnabled": {
"tiers": "...",
"rules": "...",
"offers": "...",
"stampCards": "...",
"rewards": "..."
},
"sectionOrder": ["..."],
"tiers": ["..."],
"rules": ["..."],
"offers": ["..."],
"rewards": ["..."],
"stampCardTypes": ["..."],
"member": {
"loyaltyMembershipId": "...",
"joinedAt": "...",
"balance": "...",
"tier": "...",
"nextTier": "...",
"tierEarned": "...",
"tierRemaining": "...",
"tierTarget": "...",
"stampCards": "...",
"rewards": "...",
"referralCode": "...",
"referralShareUrl": "...",
"referralsPending": "...",
"referralsCompleted": "...",
"birthday": "..."
}
}
]
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"organizationName": {
"type": "string"
},
"siteSlug": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The site the programs were resolved through."
},
"authenticated": {
"type": "boolean",
"description": "Whether the request carried a valid customer signature."
},
"programs": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"name": {
"type": "string"
},
"pointsNames": {
"anyOf": [
{
"type": "object",
"properties": {
"singular": {
"type": "string",
"minLength": 1,
"description": "The name of one point, such as `point`."
},
"plural": {
"type": "string",
"minLength": 1,
"description": "The name of several points, such as `points`."
}
},
"required": ["singular", "plural"],
"additionalProperties": false,
"description": "Unit names a balance is composed from, for example `12 points`."
},
{
"type": "null"
}
],
"description": "What the program calls its points. Null means it has none, so there is no balance and nothing to spend."
},
"paused": {
"type": "boolean",
"description": "Whether the program is paused. Nothing earns and nothing can be claimed while it is, and everything already earned stays here to look at."
},
"draft": {
"type": "boolean",
"description": "Whether the program is still a draft. Only a preview token ever returns one, and it is read only: nobody can join it or claim from it until it is published."
},
"sectionsEnabled": {
"type": "object",
"properties": {
"tiers": {
"type": "boolean"
},
"rules": {
"type": "boolean"
},
"offers": {
"type": "boolean"
},
"stampCards": {
"type": "boolean"
},
"rewards": {
"type": "boolean"
}
},
"required": ["tiers", "rules", "offers", "stampCards", "rewards"],
"additionalProperties": false,
"description": "Which sections the store shows members. A section that is off comes back with an empty list, so the widget never has to filter."
},
"sectionOrder": {
"type": "array",
"items": {
"type": "string",
"enum": ["tiers", "rules", "offers", "stampCards", "rewards"],
"description": "A section of the member-facing loyalty widget, in the order members see them."
},
"description": "The order to present the sections in."
},
"tiers": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"name": {
"type": "string"
},
"color": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"pointsMultiplier": {
"type": "number",
"description": "What the tier multiplies rule-issued points by."
},
"thresholdAmount": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "Points needed to reach the tier. Null for a tier nobody earns into."
},
"thresholdWindow": {
"anyOf": [
{
"type": "string",
"enum": ["lifetime", "rolling12Months"],
"description": "The period earning is summed over. `lifetime` counts everything the member ever earned; `rolling12Months` counts the last 12 months."
},
{
"type": "null"
}
]
}
},
"required": [
"id",
"name",
"color",
"pointsMultiplier",
"thresholdAmount",
"thresholdWindow"
],
"additionalProperties": false
}
},
"rules": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"name": {
"type": "string",
"description": "The rule's display name, written for members."
},
"section": {
"type": "string",
"enum": ["rules", "rewards", "offers", "stampCards"],
"description": "A section of the loyalty widget."
},
"triggerType": {
"type": "string",
"description": "What makes the rule fire, such as `purchase` or `pointsSpent`."
},
"reward": {
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "points",
"description": "Credits the customer with loyalty points."
},
"amount": {
"type": "number",
"description": "How many points to credit."
}
},
"required": ["type", "amount"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "amountCoupon",
"description": "Issues a fixed-value discount coupon."
},
"amountCents": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991,
"description": "Face value of the coupon, in cents."
},
"productFilter": {
"anyOf": [
{
"type": "object",
"properties": {
"slugs": {
"type": "array",
"items": {
"type": "string"
},
"description": "Product slugs the reward applies to."
},
"tags": {
"type": "array",
"items": {
"type": "string"
},
"description": "Product tag names the reward applies to."
}
},
"required": ["slugs", "tags"],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "Restricts the coupon to matching products. Null means it applies to anything."
},
"minimumOrderTotalCents": {
"anyOf": [
{
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "Minimum order total, in cents, before the coupon can be used. Null for none."
},
"expiry": {
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "never",
"description": "The reward stays redeemable until it is used or withdrawn."
}
},
"required": ["type"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "daysAfterIssuance",
"description": "The reward lapses a set number of days after each member gets it."
},
"days": {
"type": "integer",
"minimum": 1,
"maximum": 3650,
"description": "How many days after issuance the reward stops being redeemable."
}
},
"required": ["type", "days"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "fixedDate",
"description": "Every reward issued from this rule lapses at the same moment."
},
"expiresAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "The moment the reward stops being redeemable, as an ISO 8601 timestamp."
}
},
"required": ["type", "expiresAt"],
"additionalProperties": false
}
],
"description": "When a reward issued from this rule stops being redeemable. Omitted means the program's default applies."
}
},
"required": [
"type",
"amountCents",
"productFilter",
"minimumOrderTotalCents"
],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "percentageCoupon",
"description": "Issues a percentage-off discount coupon."
},
"percentageDiscount": {
"type": "integer",
"minimum": 1,
"maximum": 100,
"description": "Percentage taken off the qualifying items, from 1 to 100."
},
"productFilter": {
"anyOf": [
{
"type": "object",
"properties": {
"slugs": {
"type": "array",
"items": {
"type": "string"
},
"description": "Product slugs the reward applies to."
},
"tags": {
"type": "array",
"items": {
"type": "string"
},
"description": "Product tag names the reward applies to."
}
},
"required": ["slugs", "tags"],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "Restricts the coupon to matching products. Null means it applies to anything."
},
"minimumOrderTotalCents": {
"anyOf": [
{
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "Minimum order total, in cents, before the coupon can be used. Null for none."
},
"expiry": {
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "never",
"description": "The reward stays redeemable until it is used or withdrawn."
}
},
"required": ["type"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "daysAfterIssuance",
"description": "The reward lapses a set number of days after each member gets it."
},
"days": {
"type": "integer",
"minimum": 1,
"maximum": 3650,
"description": "How many days after issuance the reward stops being redeemable."
}
},
"required": ["type", "days"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "fixedDate",
"description": "Every reward issued from this rule lapses at the same moment."
},
"expiresAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "The moment the reward stops being redeemable, as an ISO 8601 timestamp."
}
},
"required": ["type", "expiresAt"],
"additionalProperties": false
}
],
"description": "When a reward issued from this rule stops being redeemable. Omitted means the program's default applies."
}
},
"required": [
"type",
"percentageDiscount",
"productFilter",
"minimumOrderTotalCents"
],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "freeShipping",
"description": "Waives shipping on the customer's next order."
},
"maximumValueCents": {
"anyOf": [
{
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "Cap on the shipping cost covered, in cents. Null means uncapped."
},
"expiry": {
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "never",
"description": "The reward stays redeemable until it is used or withdrawn."
}
},
"required": ["type"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "daysAfterIssuance",
"description": "The reward lapses a set number of days after each member gets it."
},
"days": {
"type": "integer",
"minimum": 1,
"maximum": 3650,
"description": "How many days after issuance the reward stops being redeemable."
}
},
"required": ["type", "days"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "fixedDate",
"description": "Every reward issued from this rule lapses at the same moment."
},
"expiresAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "The moment the reward stops being redeemable, as an ISO 8601 timestamp."
}
},
"required": ["type", "expiresAt"],
"additionalProperties": false
}
],
"description": "When a reward issued from this rule stops being redeemable. Omitted means the program's default applies."
}
},
"required": ["type", "maximumValueCents"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "giftCard",
"description": "Issues a gift card."
},
"amountCents": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991,
"description": "Value loaded onto the gift card, in cents."
},
"expirySeconds": {
"anyOf": [
{
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "How long the gift card's balance stays spendable, in seconds. Null means the balance never expires."
},
"expiry": {
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "never",
"description": "The reward stays redeemable until it is used or withdrawn."
}
},
"required": ["type"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "daysAfterIssuance",
"description": "The reward lapses a set number of days after each member gets it."
},
"days": {
"type": "integer",
"minimum": 1,
"maximum": 3650,
"description": "How many days after issuance the reward stops being redeemable."
}
},
"required": ["type", "days"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "fixedDate",
"description": "Every reward issued from this rule lapses at the same moment."
},
"expiresAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "The moment the reward stops being redeemable, as an ISO 8601 timestamp."
}
},
"required": ["type", "expiresAt"],
"additionalProperties": false
}
],
"description": "When a reward issued from this rule stops being redeemable. Omitted means the program's default applies."
}
},
"required": ["type", "amountCents", "expirySeconds"],
"additionalProperties": false
}
],
"description": "What the member gets."
},
"pointsPrice": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "What one unit of the reward costs in points. Null when it cannot be bought."
},
"variableAmount": {
"type": "boolean",
"description": "Whether the member chooses how many points to spend on the reward."
},
"stampCardTypeId": {
"anyOf": [
{
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
{
"type": "null"
}
],
"description": "The stamp card the rule pays out on, when it pays out on one."
},
"stampsRequired": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "How many stamps fill the card the rule pays out on."
},
"remainingCount": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "How many are left across everyone. Null means there is no cap."
},
"availableTo": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
],
"description": "When the rule stops. Null means it has no end date."
},
"eligible": {
"type": "boolean",
"description": "Whether the shopper's tier qualifies for the rule. Always true for a guest."
}
},
"required": [
"id",
"name",
"section",
"triggerType",
"reward",
"pointsPrice",
"variableAmount",
"stampCardTypeId",
"stampsRequired",
"remainingCount",
"availableTo",
"eligible"
],
"additionalProperties": false
},
"description": "Ways to earn."
},
"offers": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"name": {
"type": "string",
"description": "The rule's display name, written for members."
},
"section": {
"type": "string",
"enum": ["rules", "rewards", "offers", "stampCards"],
"description": "A section of the loyalty widget."
},
"triggerType": {
"type": "string",
"description": "What makes the rule fire, such as `purchase` or `pointsSpent`."
},
"reward": {
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "points",
"description": "Credits the customer with loyalty points."
},
"amount": {
"type": "number",
"description": "How many points to credit."
}
},
"required": ["type", "amount"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "amountCoupon",
"description": "Issues a fixed-value discount coupon."
},
"amountCents": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991,
"description": "Face value of the coupon, in cents."
},
"productFilter": {
"anyOf": [
{
"type": "object",
"properties": {
"slugs": {
"type": "array",
"items": {
"type": "string"
},
"description": "Product slugs the reward applies to."
},
"tags": {
"type": "array",
"items": {
"type": "string"
},
"description": "Product tag names the reward applies to."
}
},
"required": ["slugs", "tags"],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "Restricts the coupon to matching products. Null means it applies to anything."
},
"minimumOrderTotalCents": {
"anyOf": [
{
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "Minimum order total, in cents, before the coupon can be used. Null for none."
},
"expiry": {
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "never",
"description": "The reward stays redeemable until it is used or withdrawn."
}
},
"required": ["type"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "daysAfterIssuance",
"description": "The reward lapses a set number of days after each member gets it."
},
"days": {
"type": "integer",
"minimum": 1,
"maximum": 3650,
"description": "How many days after issuance the reward stops being redeemable."
}
},
"required": ["type", "days"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "fixedDate",
"description": "Every reward issued from this rule lapses at the same moment."
},
"expiresAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "The moment the reward stops being redeemable, as an ISO 8601 timestamp."
}
},
"required": ["type", "expiresAt"],
"additionalProperties": false
}
],
"description": "When a reward issued from this rule stops being redeemable. Omitted means the program's default applies."
}
},
"required": [
"type",
"amountCents",
"productFilter",
"minimumOrderTotalCents"
],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "percentageCoupon",
"description": "Issues a percentage-off discount coupon."
},
"percentageDiscount": {
"type": "integer",
"minimum": 1,
"maximum": 100,
"description": "Percentage taken off the qualifying items, from 1 to 100."
},
"productFilter": {
"anyOf": [
{
"type": "object",
"properties": {
"slugs": {
"type": "array",
"items": {
"type": "string"
},
"description": "Product slugs the reward applies to."
},
"tags": {
"type": "array",
"items": {
"type": "string"
},
"description": "Product tag names the reward applies to."
}
},
"required": ["slugs", "tags"],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "Restricts the coupon to matching products. Null means it applies to anything."
},
"minimumOrderTotalCents": {
"anyOf": [
{
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "Minimum order total, in cents, before the coupon can be used. Null for none."
},
"expiry": {
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "never",
"description": "The reward stays redeemable until it is used or withdrawn."
}
},
"required": ["type"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "daysAfterIssuance",
"description": "The reward lapses a set number of days after each member gets it."
},
"days": {
"type": "integer",
"minimum": 1,
"maximum": 3650,
"description": "How many days after issuance the reward stops being redeemable."
}
},
"required": ["type", "days"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "fixedDate",
"description": "Every reward issued from this rule lapses at the same moment."
},
"expiresAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "The moment the reward stops being redeemable, as an ISO 8601 timestamp."
}
},
"required": ["type", "expiresAt"],
"additionalProperties": false
}
],
"description": "When a reward issued from this rule stops being redeemable. Omitted means the program's default applies."
}
},
"required": [
"type",
"percentageDiscount",
"productFilter",
"minimumOrderTotalCents"
],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "freeShipping",
"description": "Waives shipping on the customer's next order."
},
"maximumValueCents": {
"anyOf": [
{
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "Cap on the shipping cost covered, in cents. Null means uncapped."
},
"expiry": {
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "never",
"description": "The reward stays redeemable until it is used or withdrawn."
}
},
"required": ["type"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "daysAfterIssuance",
"description": "The reward lapses a set number of days after each member gets it."
},
"days": {
"type": "integer",
"minimum": 1,
"maximum": 3650,
"description": "How many days after issuance the reward stops being redeemable."
}
},
"required": ["type", "days"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "fixedDate",
"description": "Every reward issued from this rule lapses at the same moment."
},
"expiresAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "The moment the reward stops being redeemable, as an ISO 8601 timestamp."
}
},
"required": ["type", "expiresAt"],
"additionalProperties": false
}
],
"description": "When a reward issued from this rule stops being redeemable. Omitted means the program's default applies."
}
},
"required": ["type", "maximumValueCents"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "giftCard",
"description": "Issues a gift card."
},
"amountCents": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991,
"description": "Value loaded onto the gift card, in cents."
},
"expirySeconds": {
"anyOf": [
{
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "How long the gift card's balance stays spendable, in seconds. Null means the balance never expires."
},
"expiry": {
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "never",
"description": "The reward stays redeemable until it is used or withdrawn."
}
},
"required": ["type"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "daysAfterIssuance",
"description": "The reward lapses a set number of days after each member gets it."
},
"days": {
"type": "integer",
"minimum": 1,
"maximum": 3650,
"description": "How many days after issuance the reward stops being redeemable."
}
},
"required": ["type", "days"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "fixedDate",
"description": "Every reward issued from this rule lapses at the same moment."
},
"expiresAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "The moment the reward stops being redeemable, as an ISO 8601 timestamp."
}
},
"required": ["type", "expiresAt"],
"additionalProperties": false
}
],
"description": "When a reward issued from this rule stops being redeemable. Omitted means the program's default applies."
}
},
"required": ["type", "amountCents", "expirySeconds"],
"additionalProperties": false
}
],
"description": "What the member gets."
},
"pointsPrice": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "What one unit of the reward costs in points. Null when it cannot be bought."
},
"variableAmount": {
"type": "boolean",
"description": "Whether the member chooses how many points to spend on the reward."
},
"stampCardTypeId": {
"anyOf": [
{
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
{
"type": "null"
}
],
"description": "The stamp card the rule pays out on, when it pays out on one."
},
"stampsRequired": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "How many stamps fill the card the rule pays out on."
},
"remainingCount": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "How many are left across everyone. Null means there is no cap."
},
"availableTo": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
],
"description": "When the rule stops. Null means it has no end date."
},
"eligible": {
"type": "boolean",
"description": "Whether the shopper's tier qualifies for the rule. Always true for a guest."
}
},
"required": [
"id",
"name",
"section",
"triggerType",
"reward",
"pointsPrice",
"variableAmount",
"stampCardTypeId",
"stampsRequired",
"remainingCount",
"availableTo",
"eligible"
],
"additionalProperties": false
},
"description": "Limited-time offers."
},
"rewards": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"name": {
"type": "string",
"description": "The rule's display name, written for members."
},
"section": {
"type": "string",
"enum": ["rules", "rewards", "offers", "stampCards"],
"description": "A section of the loyalty widget."
},
"triggerType": {
"type": "string",
"description": "What makes the rule fire, such as `purchase` or `pointsSpent`."
},
"reward": {
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "points",
"description": "Credits the customer with loyalty points."
},
"amount": {
"type": "number",
"description": "How many points to credit."
}
},
"required": ["type", "amount"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "amountCoupon",
"description": "Issues a fixed-value discount coupon."
},
"amountCents": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991,
"description": "Face value of the coupon, in cents."
},
"productFilter": {
"anyOf": [
{
"type": "object",
"properties": {
"slugs": {
"type": "array",
"items": {
"type": "string"
},
"description": "Product slugs the reward applies to."
},
"tags": {
"type": "array",
"items": {
"type": "string"
},
"description": "Product tag names the reward applies to."
}
},
"required": ["slugs", "tags"],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "Restricts the coupon to matching products. Null means it applies to anything."
},
"minimumOrderTotalCents": {
"anyOf": [
{
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "Minimum order total, in cents, before the coupon can be used. Null for none."
},
"expiry": {
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "never",
"description": "The reward stays redeemable until it is used or withdrawn."
}
},
"required": ["type"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "daysAfterIssuance",
"description": "The reward lapses a set number of days after each member gets it."
},
"days": {
"type": "integer",
"minimum": 1,
"maximum": 3650,
"description": "How many days after issuance the reward stops being redeemable."
}
},
"required": ["type", "days"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "fixedDate",
"description": "Every reward issued from this rule lapses at the same moment."
},
"expiresAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "The moment the reward stops being redeemable, as an ISO 8601 timestamp."
}
},
"required": ["type", "expiresAt"],
"additionalProperties": false
}
],
"description": "When a reward issued from this rule stops being redeemable. Omitted means the program's default applies."
}
},
"required": [
"type",
"amountCents",
"productFilter",
"minimumOrderTotalCents"
],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "percentageCoupon",
"description": "Issues a percentage-off discount coupon."
},
"percentageDiscount": {
"type": "integer",
"minimum": 1,
"maximum": 100,
"description": "Percentage taken off the qualifying items, from 1 to 100."
},
"productFilter": {
"anyOf": [
{
"type": "object",
"properties": {
"slugs": {
"type": "array",
"items": {
"type": "string"
},
"description": "Product slugs the reward applies to."
},
"tags": {
"type": "array",
"items": {
"type": "string"
},
"description": "Product tag names the reward applies to."
}
},
"required": ["slugs", "tags"],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "Restricts the coupon to matching products. Null means it applies to anything."
},
"minimumOrderTotalCents": {
"anyOf": [
{
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "Minimum order total, in cents, before the coupon can be used. Null for none."
},
"expiry": {
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "never",
"description": "The reward stays redeemable until it is used or withdrawn."
}
},
"required": ["type"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "daysAfterIssuance",
"description": "The reward lapses a set number of days after each member gets it."
},
"days": {
"type": "integer",
"minimum": 1,
"maximum": 3650,
"description": "How many days after issuance the reward stops being redeemable."
}
},
"required": ["type", "days"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "fixedDate",
"description": "Every reward issued from this rule lapses at the same moment."
},
"expiresAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "The moment the reward stops being redeemable, as an ISO 8601 timestamp."
}
},
"required": ["type", "expiresAt"],
"additionalProperties": false
}
],
"description": "When a reward issued from this rule stops being redeemable. Omitted means the program's default applies."
}
},
"required": [
"type",
"percentageDiscount",
"productFilter",
"minimumOrderTotalCents"
],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "freeShipping",
"description": "Waives shipping on the customer's next order."
},
"maximumValueCents": {
"anyOf": [
{
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "Cap on the shipping cost covered, in cents. Null means uncapped."
},
"expiry": {
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "never",
"description": "The reward stays redeemable until it is used or withdrawn."
}
},
"required": ["type"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "daysAfterIssuance",
"description": "The reward lapses a set number of days after each member gets it."
},
"days": {
"type": "integer",
"minimum": 1,
"maximum": 3650,
"description": "How many days after issuance the reward stops being redeemable."
}
},
"required": ["type", "days"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "fixedDate",
"description": "Every reward issued from this rule lapses at the same moment."
},
"expiresAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "The moment the reward stops being redeemable, as an ISO 8601 timestamp."
}
},
"required": ["type", "expiresAt"],
"additionalProperties": false
}
],
"description": "When a reward issued from this rule stops being redeemable. Omitted means the program's default applies."
}
},
"required": ["type", "maximumValueCents"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "giftCard",
"description": "Issues a gift card."
},
"amountCents": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991,
"description": "Value loaded onto the gift card, in cents."
},
"expirySeconds": {
"anyOf": [
{
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "How long the gift card's balance stays spendable, in seconds. Null means the balance never expires."
},
"expiry": {
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "never",
"description": "The reward stays redeemable until it is used or withdrawn."
}
},
"required": ["type"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "daysAfterIssuance",
"description": "The reward lapses a set number of days after each member gets it."
},
"days": {
"type": "integer",
"minimum": 1,
"maximum": 3650,
"description": "How many days after issuance the reward stops being redeemable."
}
},
"required": ["type", "days"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "fixedDate",
"description": "Every reward issued from this rule lapses at the same moment."
},
"expiresAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "The moment the reward stops being redeemable, as an ISO 8601 timestamp."
}
},
"required": ["type", "expiresAt"],
"additionalProperties": false
}
],
"description": "When a reward issued from this rule stops being redeemable. Omitted means the program's default applies."
}
},
"required": ["type", "amountCents", "expirySeconds"],
"additionalProperties": false
}
],
"description": "What the member gets."
},
"pointsPrice": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "What one unit of the reward costs in points. Null when it cannot be bought."
},
"variableAmount": {
"type": "boolean",
"description": "Whether the member chooses how many points to spend on the reward."
},
"stampCardTypeId": {
"anyOf": [
{
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
{
"type": "null"
}
],
"description": "The stamp card the rule pays out on, when it pays out on one."
},
"stampsRequired": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "How many stamps fill the card the rule pays out on."
},
"remainingCount": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "How many are left across everyone. Null means there is no cap."
},
"availableTo": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
],
"description": "When the rule stops. Null means it has no end date."
},
"eligible": {
"type": "boolean",
"description": "Whether the shopper's tier qualifies for the rule. Always true for a guest."
}
},
"required": [
"id",
"name",
"section",
"triggerType",
"reward",
"pointsPrice",
"variableAmount",
"stampCardTypeId",
"stampsRequired",
"remainingCount",
"availableTo",
"eligible"
],
"additionalProperties": false
},
"description": "What points can be spent on."
},
"stampCardTypes": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"name": {
"type": "string"
},
"target": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "Stamps needed to fill a card. Null when no live rule pays out on it."
}
},
"required": ["id", "name", "target"],
"additionalProperties": false
}
},
"member": {
"anyOf": [
{
"type": "object",
"properties": {
"loyaltyMembershipId": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"joinedAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
"balance": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "The member's points balance. Null when the program has no points."
},
"tier": {
"anyOf": [
{
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"name": {
"type": "string"
},
"color": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"pointsMultiplier": {
"type": "number"
}
},
"required": ["id", "name", "color", "pointsMultiplier"],
"additionalProperties": false
},
{
"type": "null"
}
]
},
"nextTier": {
"anyOf": [
{
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"name": {
"type": "string"
},
"color": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"required": ["id", "name", "color"],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "The tier the member is working toward. Null at the top of the ladder."
},
"tierEarned": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "Points earned toward the next tier, inside its window."
},
"tierRemaining": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "Points still needed for the next tier."
},
"tierTarget": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "What the next tier costs to reach."
},
"stampCards": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"stampCardTypeId": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"name": {
"type": "string"
},
"stampCount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"target": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
]
},
"completedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
]
}
},
"required": [
"id",
"stampCardTypeId",
"name",
"stampCount",
"target",
"completedAt"
],
"additionalProperties": false
},
"description": "The member's open stamp cards."
},
"rewards": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"name": {
"type": "string",
"description": "What the reward was called when it was issued."
},
"reward": {
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "points",
"description": "Credits the customer with loyalty points."
},
"amount": {
"type": "number",
"description": "How many points to credit."
}
},
"required": ["type", "amount"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "amountCoupon",
"description": "Issues a fixed-value discount coupon."
},
"amountCents": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991,
"description": "Face value of the coupon, in cents."
},
"productFilter": {
"anyOf": [
{
"type": "object",
"properties": {
"slugs": {
"type": "array",
"items": {
"type": "string"
},
"description": "Product slugs the reward applies to."
},
"tags": {
"type": "array",
"items": {
"type": "string"
},
"description": "Product tag names the reward applies to."
}
},
"required": ["slugs", "tags"],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "Restricts the coupon to matching products. Null means it applies to anything."
},
"minimumOrderTotalCents": {
"anyOf": [
{
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "Minimum order total, in cents, before the coupon can be used. Null for none."
},
"expiry": {
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "never",
"description": "The reward stays redeemable until it is used or withdrawn."
}
},
"required": ["type"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "daysAfterIssuance",
"description": "The reward lapses a set number of days after each member gets it."
},
"days": {
"type": "integer",
"minimum": 1,
"maximum": 3650,
"description": "How many days after issuance the reward stops being redeemable."
}
},
"required": ["type", "days"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "fixedDate",
"description": "Every reward issued from this rule lapses at the same moment."
},
"expiresAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "The moment the reward stops being redeemable, as an ISO 8601 timestamp."
}
},
"required": ["type", "expiresAt"],
"additionalProperties": false
}
],
"description": "When a reward issued from this rule stops being redeemable. Omitted means the program's default applies."
}
},
"required": [
"type",
"amountCents",
"productFilter",
"minimumOrderTotalCents"
],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "percentageCoupon",
"description": "Issues a percentage-off discount coupon."
},
"percentageDiscount": {
"type": "integer",
"minimum": 1,
"maximum": 100,
"description": "Percentage taken off the qualifying items, from 1 to 100."
},
"productFilter": {
"anyOf": [
{
"type": "object",
"properties": {
"slugs": {
"type": "array",
"items": {
"type": "string"
},
"description": "Product slugs the reward applies to."
},
"tags": {
"type": "array",
"items": {
"type": "string"
},
"description": "Product tag names the reward applies to."
}
},
"required": ["slugs", "tags"],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "Restricts the coupon to matching products. Null means it applies to anything."
},
"minimumOrderTotalCents": {
"anyOf": [
{
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "Minimum order total, in cents, before the coupon can be used. Null for none."
},
"expiry": {
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "never",
"description": "The reward stays redeemable until it is used or withdrawn."
}
},
"required": ["type"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "daysAfterIssuance",
"description": "The reward lapses a set number of days after each member gets it."
},
"days": {
"type": "integer",
"minimum": 1,
"maximum": 3650,
"description": "How many days after issuance the reward stops being redeemable."
}
},
"required": ["type", "days"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "fixedDate",
"description": "Every reward issued from this rule lapses at the same moment."
},
"expiresAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "The moment the reward stops being redeemable, as an ISO 8601 timestamp."
}
},
"required": ["type", "expiresAt"],
"additionalProperties": false
}
],
"description": "When a reward issued from this rule stops being redeemable. Omitted means the program's default applies."
}
},
"required": [
"type",
"percentageDiscount",
"productFilter",
"minimumOrderTotalCents"
],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "freeShipping",
"description": "Waives shipping on the customer's next order."
},
"maximumValueCents": {
"anyOf": [
{
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "Cap on the shipping cost covered, in cents. Null means uncapped."
},
"expiry": {
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "never",
"description": "The reward stays redeemable until it is used or withdrawn."
}
},
"required": ["type"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "daysAfterIssuance",
"description": "The reward lapses a set number of days after each member gets it."
},
"days": {
"type": "integer",
"minimum": 1,
"maximum": 3650,
"description": "How many days after issuance the reward stops being redeemable."
}
},
"required": ["type", "days"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "fixedDate",
"description": "Every reward issued from this rule lapses at the same moment."
},
"expiresAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "The moment the reward stops being redeemable, as an ISO 8601 timestamp."
}
},
"required": ["type", "expiresAt"],
"additionalProperties": false
}
],
"description": "When a reward issued from this rule stops being redeemable. Omitted means the program's default applies."
}
},
"required": ["type", "maximumValueCents"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "giftCard",
"description": "Issues a gift card."
},
"amountCents": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991,
"description": "Value loaded onto the gift card, in cents."
},
"expirySeconds": {
"anyOf": [
{
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "How long the gift card's balance stays spendable, in seconds. Null means the balance never expires."
},
"expiry": {
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "never",
"description": "The reward stays redeemable until it is used or withdrawn."
}
},
"required": ["type"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "daysAfterIssuance",
"description": "The reward lapses a set number of days after each member gets it."
},
"days": {
"type": "integer",
"minimum": 1,
"maximum": 3650,
"description": "How many days after issuance the reward stops being redeemable."
}
},
"required": ["type", "days"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "fixedDate",
"description": "Every reward issued from this rule lapses at the same moment."
},
"expiresAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "The moment the reward stops being redeemable, as an ISO 8601 timestamp."
}
},
"required": ["type", "expiresAt"],
"additionalProperties": false
}
],
"description": "When a reward issued from this rule stops being redeemable. Omitted means the program's default applies."
}
},
"required": ["type", "amountCents", "expirySeconds"],
"additionalProperties": false
}
],
"description": "What the customer gets when the rule fires."
},
"status": {
"type": "string",
"enum": ["pending", "redeemed", "expired", "revoked"],
"description": "Where a reward has got to: `pending` means it is waiting to be used, `redeemed` means the customer used it, `expired` means it ran out of time, and `revoked` means it was withdrawn."
},
"code": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The code to present at checkout."
},
"pointsSpent": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "What the member paid for it. Null when it cost nothing."
},
"expiresAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
]
},
"redeemedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
]
},
"createdAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
"cancelable": {
"type": "boolean",
"description": "Whether the member can still cancel it and take their points back."
}
},
"required": [
"id",
"name",
"reward",
"status",
"code",
"pointsSpent",
"expiresAt",
"redeemedAt",
"createdAt",
"cancelable"
],
"additionalProperties": false
},
"description": "Rewards the member holds and has not used yet."
},
"referralCode": {
"type": "string",
"description": "The member's refer-a-friend code."
},
"referralShareUrl": {
"anyOf": [
{
"type": "string",
"format": "uri"
},
{
"type": "null"
}
],
"description": "The link to share with a friend: the store's home page carrying the code. Null when the store has no site to point at."
},
"referralsPending": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"referralsCompleted": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"birthday": {
"anyOf": [
{
"type": "string",
"pattern": "^(?:(?:0[13578]|1[02])-(?:0[1-9]|[12][0-9]|3[01])|(?:0[469]|11)-(?:0[1-9]|[12][0-9]|30)|02-(?:0[1-9]|1[0-9]|2[0-9]))$",
"description": "The customer's birthday as `MM-DD`. Month and day only: no year is collected or stored. February 29 is allowed, and in a year that has no February 29 it is treated as February 28."
},
{
"type": "null"
}
],
"description": "The shopper's birthday as `MM-DD`, or null when they have not given one. Month and day only: no year is asked for."
}
},
"required": [
"loyaltyMembershipId",
"joinedAt",
"balance",
"tier",
"nextTier",
"tierEarned",
"tierRemaining",
"tierTarget",
"stampCards",
"rewards",
"referralCode",
"referralShareUrl",
"referralsPending",
"referralsCompleted",
"birthday"
],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "The shopper's own state. Null for a guest, and null for a signed-in shopper who has not joined."
}
},
"required": [
"id",
"name",
"pointsNames",
"paused",
"draft",
"sectionsEnabled",
"sectionOrder",
"tiers",
"rules",
"offers",
"rewards",
"stampCardTypes",
"member"
],
"additionalProperties": false
}
}
},
"required": ["organizationName", "siteSlug", "authenticated", "programs"],
"additionalProperties": false
}GET /widgets/loyalty/earnable
Get the points a purchase would earn, for a badge on a product page. Send the price and it comes back with the points every live purchase rule adds up to, with the signed-in shopper's tier multiplier applied when the request is signed. A program that would earn nothing is left out, so a store with no purchase rule gets an empty list.
Query Parameters
apiPublishableKeystringrequiredYour organization's publishable key, from Settings, API keys.
customerEmailstring (email)The signed-in shopper's address. Leave it out for the guest catalog.
timestampstringThe Unix seconds the signature was made at. Required with customerEmail.
signaturestringThe HMAC of the email and timestamp. Required with customerEmail.
siteSlugstringWhich of the store's sites the widget is embedded on. Leave it out and the oldest site is used.
previewTokenstringA preview token from POST /loyalty-programs/{id}/preview. It adds the one draft program it names to the response, so a merchant can check a program before publishing it. The publishable key on its own never reaches a draft.
pricenumberrequiredWhat the shopper would spend, in whole currency units rather than cents. For example 12.50.
currencystringThe currency the price is in, such as USD. Points are earned per whole currency unit, so this is only carried back to the widget.
curl \
"https://api.cascade.dev/widgets/loyalty/earnable?apiPublishableKey=pk_YOUR_PUBLISHABLE_KEY&price=0"import { client } from "@cascade-commerce/api/widget/client"
import { getWidgetsLoyaltyEarnable } from "@cascade-commerce/api/widget"
client.setConfig({
baseUrl: "https://api.cascade.dev",
})
const { data, error } = await getWidgetsLoyaltyEarnable({
query: { apiPublishableKey: "pk_YOUR_PUBLISHABLE_KEY", price: 0 },
})require "net/http"
uri = URI("https://api.cascade.dev/widgets/loyalty/earnable?apiPublishableKey=pk_YOUR_PUBLISHABLE_KEY&price=0")
req = Net::HTTP::Get.new(uri)
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
puts res.bodyimport json
from urllib.request import Request, urlopen
req = Request(
"https://api.cascade.dev/widgets/loyalty/earnable?apiPublishableKey=pk_YOUR_PUBLISHABLE_KEY&price=0",
)
with urlopen(req) as res:
print(json.load(res)){
"siteSlug": "string",
"authenticated": true,
"currency": "string",
"programs": [
{
"loyaltyProgramId": "550e8400-e29b-41d4-a716-446655440000",
"programName": "string",
"pointsNames": {
"singular": "...",
"plural": "..."
},
"points": 0,
"pointsMultiplier": 0,
"member": true
}
]
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"siteSlug": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The site the programs were resolved through."
},
"authenticated": {
"type": "boolean",
"description": "Whether the request carried a valid customer signature."
},
"currency": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The currency the price was sent in, carried back."
},
"programs": {
"type": "array",
"items": {
"type": "object",
"properties": {
"loyaltyProgramId": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"programName": {
"type": "string"
},
"pointsNames": {
"anyOf": [
{
"type": "object",
"properties": {
"singular": {
"type": "string",
"minLength": 1,
"description": "The name of one point, such as `point`."
},
"plural": {
"type": "string",
"minLength": 1,
"description": "The name of several points, such as `points`."
}
},
"required": ["singular", "plural"],
"additionalProperties": false,
"description": "Unit names a balance is composed from, for example `12 points`."
},
{
"type": "null"
}
],
"description": "What the program calls its points."
},
"points": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "Points this spend would earn, added up across every live purchase rule. Only programs that would earn something are listed."
},
"pointsMultiplier": {
"type": "number",
"description": "The member's tier multiplier, already applied to `points`. 1 for a guest."
},
"member": {
"type": "boolean",
"description": "Whether the shopper is already in the program."
}
},
"required": [
"loyaltyProgramId",
"programName",
"pointsNames",
"points",
"pointsMultiplier",
"member"
],
"additionalProperties": false
}
}
},
"required": ["siteSlug", "authenticated", "currency", "programs"],
"additionalProperties": false
}POST /widgets/loyalty/join
Join a loyalty program as the signed-in shopper
Query Parameters
apiPublishableKeystringrequiredYour organization's publishable key, from Settings, API keys.
customerEmailstring (email)requiredEmail address of the customer the storefront signed in.
timestampstringrequiredUnix time in seconds when the signature was generated. It is accepted for 24 hours.
signaturestringrequiredHex HMAC-SHA256 of customerEmail followed by timestamp, keyed with your widget signing secret. See the widgets guide for the recipe.
siteSlugstringWhich of the store's sites the widget is embedded on. Leave it out and the oldest site is used.
previewTokenstringA preview token from POST /loyalty-programs/{id}/preview. It adds the one draft program it names to the response, so a merchant can check a program before publishing it. The publishable key on its own never reaches a draft.
Body Parameters
loyaltyProgramIdstring (uuid)requiredThe program to join.
referralCodestringThe code the shopper arrived with, so the friend who sent them gets credit.
curl \
-X POST \
"https://api.cascade.dev/widgets/loyalty/join?apiPublishableKey=pk_YOUR_PUBLISHABLE_KEY&customerEmail=shopper%40example.com×tamp=1735689600&signature=9f86d081..." \
-H "Content-Type: application/json" \
-d '{
"loyaltyProgramId": "550e8400-e29b-41d4-a716-446655440000",
"referralCode": "string"
}'import { client } from "@cascade-commerce/api/widget/client"
import { postWidgetsLoyaltyJoin } from "@cascade-commerce/api/widget"
client.setConfig({
baseUrl: "https://api.cascade.dev",
})
const { data, error } = await postWidgetsLoyaltyJoin({
query: {
apiPublishableKey: "pk_YOUR_PUBLISHABLE_KEY",
customerEmail: "[email protected]",
timestamp: "1735689600",
signature: "9f86d081...",
},
body: {
loyaltyProgramId: "550e8400-e29b-41d4-a716-446655440000",
referralCode: "string",
},
})require "net/http"
uri = URI("https://api.cascade.dev/widgets/loyalty/join?apiPublishableKey=pk_YOUR_PUBLISHABLE_KEY&customerEmail=shopper%40example.com×tamp=1735689600&signature=9f86d081...")
req = Net::HTTP::Post.new(uri)
req["Content-Type"] = "application/json"
req.body = <<~JSON
{
"loyaltyProgramId": "550e8400-e29b-41d4-a716-446655440000",
"referralCode": "string"
}
JSON
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
puts res.bodyimport json
from urllib.request import Request, urlopen
body = """{
"loyaltyProgramId": "550e8400-e29b-41d4-a716-446655440000",
"referralCode": "string"
}"""
req = Request(
"https://api.cascade.dev/widgets/loyalty/join?apiPublishableKey=pk_YOUR_PUBLISHABLE_KEY&customerEmail=shopper%40example.com×tamp=1735689600&signature=9f86d081...",
data=body.encode(),
headers={"Content-Type": "application/json"},
)
with urlopen(req) as res:
print(json.load(res)){
"program": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "string",
"pointsNames": {
"singular": "string",
"plural": "string"
},
"paused": true,
"draft": true,
"sectionsEnabled": {
"tiers": true,
"rules": true,
"offers": true,
"stampCards": true,
"rewards": true
},
"sectionOrder": ["tiers"],
"tiers": [
{
"id": "...",
"name": "...",
"color": "...",
"pointsMultiplier": "...",
"thresholdAmount": "...",
"thresholdWindow": "..."
}
],
"rules": [
{
"id": "...",
"name": "...",
"section": "...",
"triggerType": "...",
"reward": "...",
"pointsPrice": "...",
"variableAmount": "...",
"stampCardTypeId": "...",
"stampsRequired": "...",
"remainingCount": "...",
"availableTo": "...",
"eligible": "..."
}
],
"offers": [
{
"id": "...",
"name": "...",
"section": "...",
"triggerType": "...",
"reward": "...",
"pointsPrice": "...",
"variableAmount": "...",
"stampCardTypeId": "...",
"stampsRequired": "...",
"remainingCount": "...",
"availableTo": "...",
"eligible": "..."
}
],
"rewards": [
{
"id": "...",
"name": "...",
"section": "...",
"triggerType": "...",
"reward": "...",
"pointsPrice": "...",
"variableAmount": "...",
"stampCardTypeId": "...",
"stampsRequired": "...",
"remainingCount": "...",
"availableTo": "...",
"eligible": "..."
}
],
"stampCardTypes": [
{
"id": "...",
"name": "...",
"target": "..."
}
],
"member": {
"loyaltyMembershipId": "550e8400-e29b-41d4-a716-446655440000",
"joinedAt": "2025-01-15T09:30:00Z",
"balance": 0,
"tier": {
"id": "...",
"name": "...",
"color": "...",
"pointsMultiplier": "..."
},
"nextTier": {
"id": "...",
"name": "...",
"color": "..."
},
"tierEarned": 0,
"tierRemaining": 0,
"tierTarget": 0,
"stampCards": ["..."],
"rewards": ["..."],
"referralCode": "string",
"referralShareUrl": "https://example.com",
"referralsPending": 0,
"referralsCompleted": 0,
"birthday": "string"
}
},
"alreadyMember": true,
"referral": "attributed"
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"program": {
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"name": {
"type": "string"
},
"pointsNames": {
"anyOf": [
{
"type": "object",
"properties": {
"singular": {
"type": "string",
"minLength": 1,
"description": "The name of one point, such as `point`."
},
"plural": {
"type": "string",
"minLength": 1,
"description": "The name of several points, such as `points`."
}
},
"required": ["singular", "plural"],
"additionalProperties": false,
"description": "Unit names a balance is composed from, for example `12 points`."
},
{
"type": "null"
}
],
"description": "What the program calls its points. Null means it has none, so there is no balance and nothing to spend."
},
"paused": {
"type": "boolean",
"description": "Whether the program is paused. Nothing earns and nothing can be claimed while it is, and everything already earned stays here to look at."
},
"draft": {
"type": "boolean",
"description": "Whether the program is still a draft. Only a preview token ever returns one, and it is read only: nobody can join it or claim from it until it is published."
},
"sectionsEnabled": {
"type": "object",
"properties": {
"tiers": {
"type": "boolean"
},
"rules": {
"type": "boolean"
},
"offers": {
"type": "boolean"
},
"stampCards": {
"type": "boolean"
},
"rewards": {
"type": "boolean"
}
},
"required": ["tiers", "rules", "offers", "stampCards", "rewards"],
"additionalProperties": false,
"description": "Which sections the store shows members. A section that is off comes back with an empty list, so the widget never has to filter."
},
"sectionOrder": {
"type": "array",
"items": {
"type": "string",
"enum": ["tiers", "rules", "offers", "stampCards", "rewards"],
"description": "A section of the member-facing loyalty widget, in the order members see them."
},
"description": "The order to present the sections in."
},
"tiers": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"name": {
"type": "string"
},
"color": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"pointsMultiplier": {
"type": "number",
"description": "What the tier multiplies rule-issued points by."
},
"thresholdAmount": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "Points needed to reach the tier. Null for a tier nobody earns into."
},
"thresholdWindow": {
"anyOf": [
{
"type": "string",
"enum": ["lifetime", "rolling12Months"],
"description": "The period earning is summed over. `lifetime` counts everything the member ever earned; `rolling12Months` counts the last 12 months."
},
{
"type": "null"
}
]
}
},
"required": [
"id",
"name",
"color",
"pointsMultiplier",
"thresholdAmount",
"thresholdWindow"
],
"additionalProperties": false
}
},
"rules": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"name": {
"type": "string",
"description": "The rule's display name, written for members."
},
"section": {
"type": "string",
"enum": ["rules", "rewards", "offers", "stampCards"],
"description": "A section of the loyalty widget."
},
"triggerType": {
"type": "string",
"description": "What makes the rule fire, such as `purchase` or `pointsSpent`."
},
"reward": {
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "points",
"description": "Credits the customer with loyalty points."
},
"amount": {
"type": "number",
"description": "How many points to credit."
}
},
"required": ["type", "amount"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "amountCoupon",
"description": "Issues a fixed-value discount coupon."
},
"amountCents": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991,
"description": "Face value of the coupon, in cents."
},
"productFilter": {
"anyOf": [
{
"type": "object",
"properties": {
"slugs": {
"type": "array",
"items": {
"type": "string"
},
"description": "Product slugs the reward applies to."
},
"tags": {
"type": "array",
"items": {
"type": "string"
},
"description": "Product tag names the reward applies to."
}
},
"required": ["slugs", "tags"],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "Restricts the coupon to matching products. Null means it applies to anything."
},
"minimumOrderTotalCents": {
"anyOf": [
{
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "Minimum order total, in cents, before the coupon can be used. Null for none."
},
"expiry": {
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "never",
"description": "The reward stays redeemable until it is used or withdrawn."
}
},
"required": ["type"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "daysAfterIssuance",
"description": "The reward lapses a set number of days after each member gets it."
},
"days": {
"type": "integer",
"minimum": 1,
"maximum": 3650,
"description": "How many days after issuance the reward stops being redeemable."
}
},
"required": ["type", "days"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "fixedDate",
"description": "Every reward issued from this rule lapses at the same moment."
},
"expiresAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "The moment the reward stops being redeemable, as an ISO 8601 timestamp."
}
},
"required": ["type", "expiresAt"],
"additionalProperties": false
}
],
"description": "When a reward issued from this rule stops being redeemable. Omitted means the program's default applies."
}
},
"required": ["type", "amountCents", "productFilter", "minimumOrderTotalCents"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "percentageCoupon",
"description": "Issues a percentage-off discount coupon."
},
"percentageDiscount": {
"type": "integer",
"minimum": 1,
"maximum": 100,
"description": "Percentage taken off the qualifying items, from 1 to 100."
},
"productFilter": {
"anyOf": [
{
"type": "object",
"properties": {
"slugs": {
"type": "array",
"items": {
"type": "string"
},
"description": "Product slugs the reward applies to."
},
"tags": {
"type": "array",
"items": {
"type": "string"
},
"description": "Product tag names the reward applies to."
}
},
"required": ["slugs", "tags"],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "Restricts the coupon to matching products. Null means it applies to anything."
},
"minimumOrderTotalCents": {
"anyOf": [
{
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "Minimum order total, in cents, before the coupon can be used. Null for none."
},
"expiry": {
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "never",
"description": "The reward stays redeemable until it is used or withdrawn."
}
},
"required": ["type"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "daysAfterIssuance",
"description": "The reward lapses a set number of days after each member gets it."
},
"days": {
"type": "integer",
"minimum": 1,
"maximum": 3650,
"description": "How many days after issuance the reward stops being redeemable."
}
},
"required": ["type", "days"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "fixedDate",
"description": "Every reward issued from this rule lapses at the same moment."
},
"expiresAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "The moment the reward stops being redeemable, as an ISO 8601 timestamp."
}
},
"required": ["type", "expiresAt"],
"additionalProperties": false
}
],
"description": "When a reward issued from this rule stops being redeemable. Omitted means the program's default applies."
}
},
"required": [
"type",
"percentageDiscount",
"productFilter",
"minimumOrderTotalCents"
],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "freeShipping",
"description": "Waives shipping on the customer's next order."
},
"maximumValueCents": {
"anyOf": [
{
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "Cap on the shipping cost covered, in cents. Null means uncapped."
},
"expiry": {
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "never",
"description": "The reward stays redeemable until it is used or withdrawn."
}
},
"required": ["type"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "daysAfterIssuance",
"description": "The reward lapses a set number of days after each member gets it."
},
"days": {
"type": "integer",
"minimum": 1,
"maximum": 3650,
"description": "How many days after issuance the reward stops being redeemable."
}
},
"required": ["type", "days"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "fixedDate",
"description": "Every reward issued from this rule lapses at the same moment."
},
"expiresAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "The moment the reward stops being redeemable, as an ISO 8601 timestamp."
}
},
"required": ["type", "expiresAt"],
"additionalProperties": false
}
],
"description": "When a reward issued from this rule stops being redeemable. Omitted means the program's default applies."
}
},
"required": ["type", "maximumValueCents"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "giftCard",
"description": "Issues a gift card."
},
"amountCents": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991,
"description": "Value loaded onto the gift card, in cents."
},
"expirySeconds": {
"anyOf": [
{
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "How long the gift card's balance stays spendable, in seconds. Null means the balance never expires."
},
"expiry": {
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "never",
"description": "The reward stays redeemable until it is used or withdrawn."
}
},
"required": ["type"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "daysAfterIssuance",
"description": "The reward lapses a set number of days after each member gets it."
},
"days": {
"type": "integer",
"minimum": 1,
"maximum": 3650,
"description": "How many days after issuance the reward stops being redeemable."
}
},
"required": ["type", "days"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "fixedDate",
"description": "Every reward issued from this rule lapses at the same moment."
},
"expiresAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "The moment the reward stops being redeemable, as an ISO 8601 timestamp."
}
},
"required": ["type", "expiresAt"],
"additionalProperties": false
}
],
"description": "When a reward issued from this rule stops being redeemable. Omitted means the program's default applies."
}
},
"required": ["type", "amountCents", "expirySeconds"],
"additionalProperties": false
}
],
"description": "What the member gets."
},
"pointsPrice": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "What one unit of the reward costs in points. Null when it cannot be bought."
},
"variableAmount": {
"type": "boolean",
"description": "Whether the member chooses how many points to spend on the reward."
},
"stampCardTypeId": {
"anyOf": [
{
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
{
"type": "null"
}
],
"description": "The stamp card the rule pays out on, when it pays out on one."
},
"stampsRequired": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "How many stamps fill the card the rule pays out on."
},
"remainingCount": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "How many are left across everyone. Null means there is no cap."
},
"availableTo": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
],
"description": "When the rule stops. Null means it has no end date."
},
"eligible": {
"type": "boolean",
"description": "Whether the shopper's tier qualifies for the rule. Always true for a guest."
}
},
"required": [
"id",
"name",
"section",
"triggerType",
"reward",
"pointsPrice",
"variableAmount",
"stampCardTypeId",
"stampsRequired",
"remainingCount",
"availableTo",
"eligible"
],
"additionalProperties": false
},
"description": "Ways to earn."
},
"offers": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"name": {
"type": "string",
"description": "The rule's display name, written for members."
},
"section": {
"type": "string",
"enum": ["rules", "rewards", "offers", "stampCards"],
"description": "A section of the loyalty widget."
},
"triggerType": {
"type": "string",
"description": "What makes the rule fire, such as `purchase` or `pointsSpent`."
},
"reward": {
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "points",
"description": "Credits the customer with loyalty points."
},
"amount": {
"type": "number",
"description": "How many points to credit."
}
},
"required": ["type", "amount"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "amountCoupon",
"description": "Issues a fixed-value discount coupon."
},
"amountCents": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991,
"description": "Face value of the coupon, in cents."
},
"productFilter": {
"anyOf": [
{
"type": "object",
"properties": {
"slugs": {
"type": "array",
"items": {
"type": "string"
},
"description": "Product slugs the reward applies to."
},
"tags": {
"type": "array",
"items": {
"type": "string"
},
"description": "Product tag names the reward applies to."
}
},
"required": ["slugs", "tags"],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "Restricts the coupon to matching products. Null means it applies to anything."
},
"minimumOrderTotalCents": {
"anyOf": [
{
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "Minimum order total, in cents, before the coupon can be used. Null for none."
},
"expiry": {
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "never",
"description": "The reward stays redeemable until it is used or withdrawn."
}
},
"required": ["type"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "daysAfterIssuance",
"description": "The reward lapses a set number of days after each member gets it."
},
"days": {
"type": "integer",
"minimum": 1,
"maximum": 3650,
"description": "How many days after issuance the reward stops being redeemable."
}
},
"required": ["type", "days"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "fixedDate",
"description": "Every reward issued from this rule lapses at the same moment."
},
"expiresAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "The moment the reward stops being redeemable, as an ISO 8601 timestamp."
}
},
"required": ["type", "expiresAt"],
"additionalProperties": false
}
],
"description": "When a reward issued from this rule stops being redeemable. Omitted means the program's default applies."
}
},
"required": ["type", "amountCents", "productFilter", "minimumOrderTotalCents"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "percentageCoupon",
"description": "Issues a percentage-off discount coupon."
},
"percentageDiscount": {
"type": "integer",
"minimum": 1,
"maximum": 100,
"description": "Percentage taken off the qualifying items, from 1 to 100."
},
"productFilter": {
"anyOf": [
{
"type": "object",
"properties": {
"slugs": {
"type": "array",
"items": {
"type": "string"
},
"description": "Product slugs the reward applies to."
},
"tags": {
"type": "array",
"items": {
"type": "string"
},
"description": "Product tag names the reward applies to."
}
},
"required": ["slugs", "tags"],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "Restricts the coupon to matching products. Null means it applies to anything."
},
"minimumOrderTotalCents": {
"anyOf": [
{
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "Minimum order total, in cents, before the coupon can be used. Null for none."
},
"expiry": {
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "never",
"description": "The reward stays redeemable until it is used or withdrawn."
}
},
"required": ["type"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "daysAfterIssuance",
"description": "The reward lapses a set number of days after each member gets it."
},
"days": {
"type": "integer",
"minimum": 1,
"maximum": 3650,
"description": "How many days after issuance the reward stops being redeemable."
}
},
"required": ["type", "days"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "fixedDate",
"description": "Every reward issued from this rule lapses at the same moment."
},
"expiresAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "The moment the reward stops being redeemable, as an ISO 8601 timestamp."
}
},
"required": ["type", "expiresAt"],
"additionalProperties": false
}
],
"description": "When a reward issued from this rule stops being redeemable. Omitted means the program's default applies."
}
},
"required": [
"type",
"percentageDiscount",
"productFilter",
"minimumOrderTotalCents"
],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "freeShipping",
"description": "Waives shipping on the customer's next order."
},
"maximumValueCents": {
"anyOf": [
{
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "Cap on the shipping cost covered, in cents. Null means uncapped."
},
"expiry": {
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "never",
"description": "The reward stays redeemable until it is used or withdrawn."
}
},
"required": ["type"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "daysAfterIssuance",
"description": "The reward lapses a set number of days after each member gets it."
},
"days": {
"type": "integer",
"minimum": 1,
"maximum": 3650,
"description": "How many days after issuance the reward stops being redeemable."
}
},
"required": ["type", "days"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "fixedDate",
"description": "Every reward issued from this rule lapses at the same moment."
},
"expiresAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "The moment the reward stops being redeemable, as an ISO 8601 timestamp."
}
},
"required": ["type", "expiresAt"],
"additionalProperties": false
}
],
"description": "When a reward issued from this rule stops being redeemable. Omitted means the program's default applies."
}
},
"required": ["type", "maximumValueCents"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "giftCard",
"description": "Issues a gift card."
},
"amountCents": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991,
"description": "Value loaded onto the gift card, in cents."
},
"expirySeconds": {
"anyOf": [
{
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "How long the gift card's balance stays spendable, in seconds. Null means the balance never expires."
},
"expiry": {
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "never",
"description": "The reward stays redeemable until it is used or withdrawn."
}
},
"required": ["type"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "daysAfterIssuance",
"description": "The reward lapses a set number of days after each member gets it."
},
"days": {
"type": "integer",
"minimum": 1,
"maximum": 3650,
"description": "How many days after issuance the reward stops being redeemable."
}
},
"required": ["type", "days"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "fixedDate",
"description": "Every reward issued from this rule lapses at the same moment."
},
"expiresAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "The moment the reward stops being redeemable, as an ISO 8601 timestamp."
}
},
"required": ["type", "expiresAt"],
"additionalProperties": false
}
],
"description": "When a reward issued from this rule stops being redeemable. Omitted means the program's default applies."
}
},
"required": ["type", "amountCents", "expirySeconds"],
"additionalProperties": false
}
],
"description": "What the member gets."
},
"pointsPrice": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "What one unit of the reward costs in points. Null when it cannot be bought."
},
"variableAmount": {
"type": "boolean",
"description": "Whether the member chooses how many points to spend on the reward."
},
"stampCardTypeId": {
"anyOf": [
{
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
{
"type": "null"
}
],
"description": "The stamp card the rule pays out on, when it pays out on one."
},
"stampsRequired": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "How many stamps fill the card the rule pays out on."
},
"remainingCount": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "How many are left across everyone. Null means there is no cap."
},
"availableTo": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
],
"description": "When the rule stops. Null means it has no end date."
},
"eligible": {
"type": "boolean",
"description": "Whether the shopper's tier qualifies for the rule. Always true for a guest."
}
},
"required": [
"id",
"name",
"section",
"triggerType",
"reward",
"pointsPrice",
"variableAmount",
"stampCardTypeId",
"stampsRequired",
"remainingCount",
"availableTo",
"eligible"
],
"additionalProperties": false
},
"description": "Limited-time offers."
},
"rewards": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"name": {
"type": "string",
"description": "The rule's display name, written for members."
},
"section": {
"type": "string",
"enum": ["rules", "rewards", "offers", "stampCards"],
"description": "A section of the loyalty widget."
},
"triggerType": {
"type": "string",
"description": "What makes the rule fire, such as `purchase` or `pointsSpent`."
},
"reward": {
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "points",
"description": "Credits the customer with loyalty points."
},
"amount": {
"type": "number",
"description": "How many points to credit."
}
},
"required": ["type", "amount"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "amountCoupon",
"description": "Issues a fixed-value discount coupon."
},
"amountCents": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991,
"description": "Face value of the coupon, in cents."
},
"productFilter": {
"anyOf": [
{
"type": "object",
"properties": {
"slugs": {
"type": "array",
"items": {
"type": "string"
},
"description": "Product slugs the reward applies to."
},
"tags": {
"type": "array",
"items": {
"type": "string"
},
"description": "Product tag names the reward applies to."
}
},
"required": ["slugs", "tags"],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "Restricts the coupon to matching products. Null means it applies to anything."
},
"minimumOrderTotalCents": {
"anyOf": [
{
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "Minimum order total, in cents, before the coupon can be used. Null for none."
},
"expiry": {
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "never",
"description": "The reward stays redeemable until it is used or withdrawn."
}
},
"required": ["type"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "daysAfterIssuance",
"description": "The reward lapses a set number of days after each member gets it."
},
"days": {
"type": "integer",
"minimum": 1,
"maximum": 3650,
"description": "How many days after issuance the reward stops being redeemable."
}
},
"required": ["type", "days"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "fixedDate",
"description": "Every reward issued from this rule lapses at the same moment."
},
"expiresAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "The moment the reward stops being redeemable, as an ISO 8601 timestamp."
}
},
"required": ["type", "expiresAt"],
"additionalProperties": false
}
],
"description": "When a reward issued from this rule stops being redeemable. Omitted means the program's default applies."
}
},
"required": ["type", "amountCents", "productFilter", "minimumOrderTotalCents"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "percentageCoupon",
"description": "Issues a percentage-off discount coupon."
},
"percentageDiscount": {
"type": "integer",
"minimum": 1,
"maximum": 100,
"description": "Percentage taken off the qualifying items, from 1 to 100."
},
"productFilter": {
"anyOf": [
{
"type": "object",
"properties": {
"slugs": {
"type": "array",
"items": {
"type": "string"
},
"description": "Product slugs the reward applies to."
},
"tags": {
"type": "array",
"items": {
"type": "string"
},
"description": "Product tag names the reward applies to."
}
},
"required": ["slugs", "tags"],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "Restricts the coupon to matching products. Null means it applies to anything."
},
"minimumOrderTotalCents": {
"anyOf": [
{
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "Minimum order total, in cents, before the coupon can be used. Null for none."
},
"expiry": {
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "never",
"description": "The reward stays redeemable until it is used or withdrawn."
}
},
"required": ["type"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "daysAfterIssuance",
"description": "The reward lapses a set number of days after each member gets it."
},
"days": {
"type": "integer",
"minimum": 1,
"maximum": 3650,
"description": "How many days after issuance the reward stops being redeemable."
}
},
"required": ["type", "days"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "fixedDate",
"description": "Every reward issued from this rule lapses at the same moment."
},
"expiresAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "The moment the reward stops being redeemable, as an ISO 8601 timestamp."
}
},
"required": ["type", "expiresAt"],
"additionalProperties": false
}
],
"description": "When a reward issued from this rule stops being redeemable. Omitted means the program's default applies."
}
},
"required": [
"type",
"percentageDiscount",
"productFilter",
"minimumOrderTotalCents"
],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "freeShipping",
"description": "Waives shipping on the customer's next order."
},
"maximumValueCents": {
"anyOf": [
{
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "Cap on the shipping cost covered, in cents. Null means uncapped."
},
"expiry": {
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "never",
"description": "The reward stays redeemable until it is used or withdrawn."
}
},
"required": ["type"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "daysAfterIssuance",
"description": "The reward lapses a set number of days after each member gets it."
},
"days": {
"type": "integer",
"minimum": 1,
"maximum": 3650,
"description": "How many days after issuance the reward stops being redeemable."
}
},
"required": ["type", "days"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "fixedDate",
"description": "Every reward issued from this rule lapses at the same moment."
},
"expiresAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "The moment the reward stops being redeemable, as an ISO 8601 timestamp."
}
},
"required": ["type", "expiresAt"],
"additionalProperties": false
}
],
"description": "When a reward issued from this rule stops being redeemable. Omitted means the program's default applies."
}
},
"required": ["type", "maximumValueCents"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "giftCard",
"description": "Issues a gift card."
},
"amountCents": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991,
"description": "Value loaded onto the gift card, in cents."
},
"expirySeconds": {
"anyOf": [
{
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "How long the gift card's balance stays spendable, in seconds. Null means the balance never expires."
},
"expiry": {
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "never",
"description": "The reward stays redeemable until it is used or withdrawn."
}
},
"required": ["type"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "daysAfterIssuance",
"description": "The reward lapses a set number of days after each member gets it."
},
"days": {
"type": "integer",
"minimum": 1,
"maximum": 3650,
"description": "How many days after issuance the reward stops being redeemable."
}
},
"required": ["type", "days"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "fixedDate",
"description": "Every reward issued from this rule lapses at the same moment."
},
"expiresAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "The moment the reward stops being redeemable, as an ISO 8601 timestamp."
}
},
"required": ["type", "expiresAt"],
"additionalProperties": false
}
],
"description": "When a reward issued from this rule stops being redeemable. Omitted means the program's default applies."
}
},
"required": ["type", "amountCents", "expirySeconds"],
"additionalProperties": false
}
],
"description": "What the member gets."
},
"pointsPrice": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "What one unit of the reward costs in points. Null when it cannot be bought."
},
"variableAmount": {
"type": "boolean",
"description": "Whether the member chooses how many points to spend on the reward."
},
"stampCardTypeId": {
"anyOf": [
{
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
{
"type": "null"
}
],
"description": "The stamp card the rule pays out on, when it pays out on one."
},
"stampsRequired": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "How many stamps fill the card the rule pays out on."
},
"remainingCount": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "How many are left across everyone. Null means there is no cap."
},
"availableTo": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
],
"description": "When the rule stops. Null means it has no end date."
},
"eligible": {
"type": "boolean",
"description": "Whether the shopper's tier qualifies for the rule. Always true for a guest."
}
},
"required": [
"id",
"name",
"section",
"triggerType",
"reward",
"pointsPrice",
"variableAmount",
"stampCardTypeId",
"stampsRequired",
"remainingCount",
"availableTo",
"eligible"
],
"additionalProperties": false
},
"description": "What points can be spent on."
},
"stampCardTypes": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"name": {
"type": "string"
},
"target": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "Stamps needed to fill a card. Null when no live rule pays out on it."
}
},
"required": ["id", "name", "target"],
"additionalProperties": false
}
},
"member": {
"anyOf": [
{
"type": "object",
"properties": {
"loyaltyMembershipId": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"joinedAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
"balance": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "The member's points balance. Null when the program has no points."
},
"tier": {
"anyOf": [
{
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"name": {
"type": "string"
},
"color": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"pointsMultiplier": {
"type": "number"
}
},
"required": ["id", "name", "color", "pointsMultiplier"],
"additionalProperties": false
},
{
"type": "null"
}
]
},
"nextTier": {
"anyOf": [
{
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"name": {
"type": "string"
},
"color": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"required": ["id", "name", "color"],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "The tier the member is working toward. Null at the top of the ladder."
},
"tierEarned": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "Points earned toward the next tier, inside its window."
},
"tierRemaining": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "Points still needed for the next tier."
},
"tierTarget": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "What the next tier costs to reach."
},
"stampCards": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"stampCardTypeId": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"name": {
"type": "string"
},
"stampCount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"target": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
]
},
"completedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
]
}
},
"required": [
"id",
"stampCardTypeId",
"name",
"stampCount",
"target",
"completedAt"
],
"additionalProperties": false
},
"description": "The member's open stamp cards."
},
"rewards": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"name": {
"type": "string",
"description": "What the reward was called when it was issued."
},
"reward": {
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "points",
"description": "Credits the customer with loyalty points."
},
"amount": {
"type": "number",
"description": "How many points to credit."
}
},
"required": ["type", "amount"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "amountCoupon",
"description": "Issues a fixed-value discount coupon."
},
"amountCents": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991,
"description": "Face value of the coupon, in cents."
},
"productFilter": {
"anyOf": [
{
"type": "object",
"properties": {
"slugs": {
"type": "array",
"items": {
"type": "string"
},
"description": "Product slugs the reward applies to."
},
"tags": {
"type": "array",
"items": {
"type": "string"
},
"description": "Product tag names the reward applies to."
}
},
"required": ["slugs", "tags"],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "Restricts the coupon to matching products. Null means it applies to anything."
},
"minimumOrderTotalCents": {
"anyOf": [
{
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "Minimum order total, in cents, before the coupon can be used. Null for none."
},
"expiry": {
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "never",
"description": "The reward stays redeemable until it is used or withdrawn."
}
},
"required": ["type"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "daysAfterIssuance",
"description": "The reward lapses a set number of days after each member gets it."
},
"days": {
"type": "integer",
"minimum": 1,
"maximum": 3650,
"description": "How many days after issuance the reward stops being redeemable."
}
},
"required": ["type", "days"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "fixedDate",
"description": "Every reward issued from this rule lapses at the same moment."
},
"expiresAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "The moment the reward stops being redeemable, as an ISO 8601 timestamp."
}
},
"required": ["type", "expiresAt"],
"additionalProperties": false
}
],
"description": "When a reward issued from this rule stops being redeemable. Omitted means the program's default applies."
}
},
"required": [
"type",
"amountCents",
"productFilter",
"minimumOrderTotalCents"
],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "percentageCoupon",
"description": "Issues a percentage-off discount coupon."
},
"percentageDiscount": {
"type": "integer",
"minimum": 1,
"maximum": 100,
"description": "Percentage taken off the qualifying items, from 1 to 100."
},
"productFilter": {
"anyOf": [
{
"type": "object",
"properties": {
"slugs": {
"type": "array",
"items": {
"type": "string"
},
"description": "Product slugs the reward applies to."
},
"tags": {
"type": "array",
"items": {
"type": "string"
},
"description": "Product tag names the reward applies to."
}
},
"required": ["slugs", "tags"],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "Restricts the coupon to matching products. Null means it applies to anything."
},
"minimumOrderTotalCents": {
"anyOf": [
{
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "Minimum order total, in cents, before the coupon can be used. Null for none."
},
"expiry": {
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "never",
"description": "The reward stays redeemable until it is used or withdrawn."
}
},
"required": ["type"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "daysAfterIssuance",
"description": "The reward lapses a set number of days after each member gets it."
},
"days": {
"type": "integer",
"minimum": 1,
"maximum": 3650,
"description": "How many days after issuance the reward stops being redeemable."
}
},
"required": ["type", "days"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "fixedDate",
"description": "Every reward issued from this rule lapses at the same moment."
},
"expiresAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "The moment the reward stops being redeemable, as an ISO 8601 timestamp."
}
},
"required": ["type", "expiresAt"],
"additionalProperties": false
}
],
"description": "When a reward issued from this rule stops being redeemable. Omitted means the program's default applies."
}
},
"required": [
"type",
"percentageDiscount",
"productFilter",
"minimumOrderTotalCents"
],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "freeShipping",
"description": "Waives shipping on the customer's next order."
},
"maximumValueCents": {
"anyOf": [
{
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "Cap on the shipping cost covered, in cents. Null means uncapped."
},
"expiry": {
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "never",
"description": "The reward stays redeemable until it is used or withdrawn."
}
},
"required": ["type"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "daysAfterIssuance",
"description": "The reward lapses a set number of days after each member gets it."
},
"days": {
"type": "integer",
"minimum": 1,
"maximum": 3650,
"description": "How many days after issuance the reward stops being redeemable."
}
},
"required": ["type", "days"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "fixedDate",
"description": "Every reward issued from this rule lapses at the same moment."
},
"expiresAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "The moment the reward stops being redeemable, as an ISO 8601 timestamp."
}
},
"required": ["type", "expiresAt"],
"additionalProperties": false
}
],
"description": "When a reward issued from this rule stops being redeemable. Omitted means the program's default applies."
}
},
"required": ["type", "maximumValueCents"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "giftCard",
"description": "Issues a gift card."
},
"amountCents": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991,
"description": "Value loaded onto the gift card, in cents."
},
"expirySeconds": {
"anyOf": [
{
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "How long the gift card's balance stays spendable, in seconds. Null means the balance never expires."
},
"expiry": {
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "never",
"description": "The reward stays redeemable until it is used or withdrawn."
}
},
"required": ["type"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "daysAfterIssuance",
"description": "The reward lapses a set number of days after each member gets it."
},
"days": {
"type": "integer",
"minimum": 1,
"maximum": 3650,
"description": "How many days after issuance the reward stops being redeemable."
}
},
"required": ["type", "days"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "fixedDate",
"description": "Every reward issued from this rule lapses at the same moment."
},
"expiresAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "The moment the reward stops being redeemable, as an ISO 8601 timestamp."
}
},
"required": ["type", "expiresAt"],
"additionalProperties": false
}
],
"description": "When a reward issued from this rule stops being redeemable. Omitted means the program's default applies."
}
},
"required": ["type", "amountCents", "expirySeconds"],
"additionalProperties": false
}
],
"description": "What the customer gets when the rule fires."
},
"status": {
"type": "string",
"enum": ["pending", "redeemed", "expired", "revoked"],
"description": "Where a reward has got to: `pending` means it is waiting to be used, `redeemed` means the customer used it, `expired` means it ran out of time, and `revoked` means it was withdrawn."
},
"code": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The code to present at checkout."
},
"pointsSpent": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "What the member paid for it. Null when it cost nothing."
},
"expiresAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
]
},
"redeemedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
]
},
"createdAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
"cancelable": {
"type": "boolean",
"description": "Whether the member can still cancel it and take their points back."
}
},
"required": [
"id",
"name",
"reward",
"status",
"code",
"pointsSpent",
"expiresAt",
"redeemedAt",
"createdAt",
"cancelable"
],
"additionalProperties": false
},
"description": "Rewards the member holds and has not used yet."
},
"referralCode": {
"type": "string",
"description": "The member's refer-a-friend code."
},
"referralShareUrl": {
"anyOf": [
{
"type": "string",
"format": "uri"
},
{
"type": "null"
}
],
"description": "The link to share with a friend: the store's home page carrying the code. Null when the store has no site to point at."
},
"referralsPending": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"referralsCompleted": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"birthday": {
"anyOf": [
{
"type": "string",
"pattern": "^(?:(?:0[13578]|1[02])-(?:0[1-9]|[12][0-9]|3[01])|(?:0[469]|11)-(?:0[1-9]|[12][0-9]|30)|02-(?:0[1-9]|1[0-9]|2[0-9]))$",
"description": "The customer's birthday as `MM-DD`. Month and day only: no year is collected or stored. February 29 is allowed, and in a year that has no February 29 it is treated as February 28."
},
{
"type": "null"
}
],
"description": "The shopper's birthday as `MM-DD`, or null when they have not given one. Month and day only: no year is asked for."
}
},
"required": [
"loyaltyMembershipId",
"joinedAt",
"balance",
"tier",
"nextTier",
"tierEarned",
"tierRemaining",
"tierTarget",
"stampCards",
"rewards",
"referralCode",
"referralShareUrl",
"referralsPending",
"referralsCompleted",
"birthday"
],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "The shopper's own state. Null for a guest, and null for a signed-in shopper who has not joined."
}
},
"required": [
"id",
"name",
"pointsNames",
"paused",
"draft",
"sectionsEnabled",
"sectionOrder",
"tiers",
"rules",
"offers",
"rewards",
"stampCardTypes",
"member"
],
"additionalProperties": false
},
"alreadyMember": {
"type": "boolean",
"description": "True when the shopper was already in the program."
},
"referral": {
"anyOf": [
{
"type": "string",
"enum": ["attributed", "alreadyAttributed", "selfReferral", "unknownCode"]
},
{
"type": "null"
}
],
"description": "What happened to the referral code, when one was sent."
}
},
"required": ["program", "alreadyMember", "referral"],
"additionalProperties": false
}POST /widgets/loyalty/claims
Spend points on a reward. The reward comes back with the code to use at checkout.
Query Parameters
apiPublishableKeystringrequiredYour organization's publishable key, from Settings, API keys.
customerEmailstring (email)requiredEmail address of the customer the storefront signed in.
timestampstringrequiredUnix time in seconds when the signature was generated. It is accepted for 24 hours.
signaturestringrequiredHex HMAC-SHA256 of customerEmail followed by timestamp, keyed with your widget signing secret. See the widgets guide for the recipe.
siteSlugstringWhich of the store's sites the widget is embedded on. Leave it out and the oldest site is used.
previewTokenstringA preview token from POST /loyalty-programs/{id}/preview. It adds the one draft program it names to the response, so a merchant can check a program before publishing it. The publishable key on its own never reaches a draft.
Body Parameters
loyaltyRuleIdstring (uuid)requiredThe reward to spend points on.
pointsToSpendintegerHow many points to spend. Only rewards that let the member choose accept more than one unit.
curl \
-X POST \
"https://api.cascade.dev/widgets/loyalty/claims?apiPublishableKey=pk_YOUR_PUBLISHABLE_KEY&customerEmail=shopper%40example.com×tamp=1735689600&signature=9f86d081..." \
-H "Content-Type: application/json" \
-d '{
"loyaltyRuleId": "550e8400-e29b-41d4-a716-446655440000",
"pointsToSpend": 0
}'import { client } from "@cascade-commerce/api/widget/client"
import { postWidgetsLoyaltyClaims } from "@cascade-commerce/api/widget"
client.setConfig({
baseUrl: "https://api.cascade.dev",
})
const { data, error } = await postWidgetsLoyaltyClaims({
query: {
apiPublishableKey: "pk_YOUR_PUBLISHABLE_KEY",
customerEmail: "[email protected]",
timestamp: "1735689600",
signature: "9f86d081...",
},
body: {
loyaltyRuleId: "550e8400-e29b-41d4-a716-446655440000",
pointsToSpend: 0,
},
})require "net/http"
uri = URI("https://api.cascade.dev/widgets/loyalty/claims?apiPublishableKey=pk_YOUR_PUBLISHABLE_KEY&customerEmail=shopper%40example.com×tamp=1735689600&signature=9f86d081...")
req = Net::HTTP::Post.new(uri)
req["Content-Type"] = "application/json"
req.body = <<~JSON
{
"loyaltyRuleId": "550e8400-e29b-41d4-a716-446655440000",
"pointsToSpend": 0
}
JSON
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
puts res.bodyimport json
from urllib.request import Request, urlopen
body = """{
"loyaltyRuleId": "550e8400-e29b-41d4-a716-446655440000",
"pointsToSpend": 0
}"""
req = Request(
"https://api.cascade.dev/widgets/loyalty/claims?apiPublishableKey=pk_YOUR_PUBLISHABLE_KEY&customerEmail=shopper%40example.com×tamp=1735689600&signature=9f86d081...",
data=body.encode(),
headers={"Content-Type": "application/json"},
)
with urlopen(req) as res:
print(json.load(res)){
"reward": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "string",
"reward": {
"type": "points",
"amount": 0
},
"status": "pending",
"code": "string",
"pointsSpent": 0,
"expiresAt": "2025-01-15T09:30:00Z",
"redeemedAt": "2025-01-15T09:30:00Z",
"createdAt": "2025-01-15T09:30:00Z",
"cancelable": true
},
"balance": 0
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"reward": {
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"name": {
"type": "string",
"description": "What the reward was called when it was issued."
},
"reward": {
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "points",
"description": "Credits the customer with loyalty points."
},
"amount": {
"type": "number",
"description": "How many points to credit."
}
},
"required": ["type", "amount"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "amountCoupon",
"description": "Issues a fixed-value discount coupon."
},
"amountCents": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991,
"description": "Face value of the coupon, in cents."
},
"productFilter": {
"anyOf": [
{
"type": "object",
"properties": {
"slugs": {
"type": "array",
"items": {
"type": "string"
},
"description": "Product slugs the reward applies to."
},
"tags": {
"type": "array",
"items": {
"type": "string"
},
"description": "Product tag names the reward applies to."
}
},
"required": ["slugs", "tags"],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "Restricts the coupon to matching products. Null means it applies to anything."
},
"minimumOrderTotalCents": {
"anyOf": [
{
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "Minimum order total, in cents, before the coupon can be used. Null for none."
},
"expiry": {
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "never",
"description": "The reward stays redeemable until it is used or withdrawn."
}
},
"required": ["type"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "daysAfterIssuance",
"description": "The reward lapses a set number of days after each member gets it."
},
"days": {
"type": "integer",
"minimum": 1,
"maximum": 3650,
"description": "How many days after issuance the reward stops being redeemable."
}
},
"required": ["type", "days"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "fixedDate",
"description": "Every reward issued from this rule lapses at the same moment."
},
"expiresAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "The moment the reward stops being redeemable, as an ISO 8601 timestamp."
}
},
"required": ["type", "expiresAt"],
"additionalProperties": false
}
],
"description": "When a reward issued from this rule stops being redeemable. Omitted means the program's default applies."
}
},
"required": ["type", "amountCents", "productFilter", "minimumOrderTotalCents"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "percentageCoupon",
"description": "Issues a percentage-off discount coupon."
},
"percentageDiscount": {
"type": "integer",
"minimum": 1,
"maximum": 100,
"description": "Percentage taken off the qualifying items, from 1 to 100."
},
"productFilter": {
"anyOf": [
{
"type": "object",
"properties": {
"slugs": {
"type": "array",
"items": {
"type": "string"
},
"description": "Product slugs the reward applies to."
},
"tags": {
"type": "array",
"items": {
"type": "string"
},
"description": "Product tag names the reward applies to."
}
},
"required": ["slugs", "tags"],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "Restricts the coupon to matching products. Null means it applies to anything."
},
"minimumOrderTotalCents": {
"anyOf": [
{
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "Minimum order total, in cents, before the coupon can be used. Null for none."
},
"expiry": {
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "never",
"description": "The reward stays redeemable until it is used or withdrawn."
}
},
"required": ["type"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "daysAfterIssuance",
"description": "The reward lapses a set number of days after each member gets it."
},
"days": {
"type": "integer",
"minimum": 1,
"maximum": 3650,
"description": "How many days after issuance the reward stops being redeemable."
}
},
"required": ["type", "days"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "fixedDate",
"description": "Every reward issued from this rule lapses at the same moment."
},
"expiresAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "The moment the reward stops being redeemable, as an ISO 8601 timestamp."
}
},
"required": ["type", "expiresAt"],
"additionalProperties": false
}
],
"description": "When a reward issued from this rule stops being redeemable. Omitted means the program's default applies."
}
},
"required": ["type", "percentageDiscount", "productFilter", "minimumOrderTotalCents"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "freeShipping",
"description": "Waives shipping on the customer's next order."
},
"maximumValueCents": {
"anyOf": [
{
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "Cap on the shipping cost covered, in cents. Null means uncapped."
},
"expiry": {
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "never",
"description": "The reward stays redeemable until it is used or withdrawn."
}
},
"required": ["type"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "daysAfterIssuance",
"description": "The reward lapses a set number of days after each member gets it."
},
"days": {
"type": "integer",
"minimum": 1,
"maximum": 3650,
"description": "How many days after issuance the reward stops being redeemable."
}
},
"required": ["type", "days"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "fixedDate",
"description": "Every reward issued from this rule lapses at the same moment."
},
"expiresAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "The moment the reward stops being redeemable, as an ISO 8601 timestamp."
}
},
"required": ["type", "expiresAt"],
"additionalProperties": false
}
],
"description": "When a reward issued from this rule stops being redeemable. Omitted means the program's default applies."
}
},
"required": ["type", "maximumValueCents"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "giftCard",
"description": "Issues a gift card."
},
"amountCents": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991,
"description": "Value loaded onto the gift card, in cents."
},
"expirySeconds": {
"anyOf": [
{
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "How long the gift card's balance stays spendable, in seconds. Null means the balance never expires."
},
"expiry": {
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "never",
"description": "The reward stays redeemable until it is used or withdrawn."
}
},
"required": ["type"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "daysAfterIssuance",
"description": "The reward lapses a set number of days after each member gets it."
},
"days": {
"type": "integer",
"minimum": 1,
"maximum": 3650,
"description": "How many days after issuance the reward stops being redeemable."
}
},
"required": ["type", "days"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "fixedDate",
"description": "Every reward issued from this rule lapses at the same moment."
},
"expiresAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "The moment the reward stops being redeemable, as an ISO 8601 timestamp."
}
},
"required": ["type", "expiresAt"],
"additionalProperties": false
}
],
"description": "When a reward issued from this rule stops being redeemable. Omitted means the program's default applies."
}
},
"required": ["type", "amountCents", "expirySeconds"],
"additionalProperties": false
}
],
"description": "What the customer gets when the rule fires."
},
"status": {
"type": "string",
"enum": ["pending", "redeemed", "expired", "revoked"],
"description": "Where a reward has got to: `pending` means it is waiting to be used, `redeemed` means the customer used it, `expired` means it ran out of time, and `revoked` means it was withdrawn."
},
"code": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The code to present at checkout."
},
"pointsSpent": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "What the member paid for it. Null when it cost nothing."
},
"expiresAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
]
},
"redeemedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
]
},
"createdAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
"cancelable": {
"type": "boolean",
"description": "Whether the member can still cancel it and take their points back."
}
},
"required": [
"id",
"name",
"reward",
"status",
"code",
"pointsSpent",
"expiresAt",
"redeemedAt",
"createdAt",
"cancelable"
],
"additionalProperties": false
},
"balance": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "The member's points balance after the claim."
}
},
"required": ["reward", "balance"],
"additionalProperties": false
}GET /widgets/loyalty/rewards
List the rewards the shopper holds, with their codes
Query Parameters
apiPublishableKeystringrequiredYour organization's publishable key, from Settings, API keys.
customerEmailstring (email)requiredEmail address of the customer the storefront signed in.
timestampstringrequiredUnix time in seconds when the signature was generated. It is accepted for 24 hours.
signaturestringrequiredHex HMAC-SHA256 of customerEmail followed by timestamp, keyed with your widget signing secret. See the widgets guide for the recipe.
siteSlugstringWhich of the store's sites the widget is embedded on. Leave it out and the oldest site is used.
previewTokenstringA preview token from POST /loyalty-programs/{id}/preview. It adds the one draft program it names to the response, so a merchant can check a program before publishing it. The publishable key on its own never reaches a draft.
loyaltyProgramIdstring (uuid)requiredThe program whose rewards to list.
curl \
"https://api.cascade.dev/widgets/loyalty/rewards?apiPublishableKey=pk_YOUR_PUBLISHABLE_KEY&customerEmail=shopper%40example.com×tamp=1735689600&signature=9f86d081...&loyaltyProgramId=550e8400-e29b-41d4-a716-446655440000"import { client } from "@cascade-commerce/api/widget/client"
import { getWidgetsLoyaltyRewards } from "@cascade-commerce/api/widget"
client.setConfig({
baseUrl: "https://api.cascade.dev",
})
const { data, error } = await getWidgetsLoyaltyRewards({
query: {
apiPublishableKey: "pk_YOUR_PUBLISHABLE_KEY",
customerEmail: "[email protected]",
timestamp: "1735689600",
signature: "9f86d081...",
loyaltyProgramId: "550e8400-e29b-41d4-a716-446655440000",
},
})require "net/http"
uri = URI("https://api.cascade.dev/widgets/loyalty/rewards?apiPublishableKey=pk_YOUR_PUBLISHABLE_KEY&customerEmail=shopper%40example.com×tamp=1735689600&signature=9f86d081...&loyaltyProgramId=550e8400-e29b-41d4-a716-446655440000")
req = Net::HTTP::Get.new(uri)
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
puts res.bodyimport json
from urllib.request import Request, urlopen
req = Request(
"https://api.cascade.dev/widgets/loyalty/rewards?apiPublishableKey=pk_YOUR_PUBLISHABLE_KEY&customerEmail=shopper%40example.com×tamp=1735689600&signature=9f86d081...&loyaltyProgramId=550e8400-e29b-41d4-a716-446655440000",
)
with urlopen(req) as res:
print(json.load(res)){
"rewards": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "string",
"reward": {
"type": "...",
"amount": "..."
},
"status": "pending",
"code": "string",
"pointsSpent": 0,
"expiresAt": "2025-01-15T09:30:00Z",
"redeemedAt": "2025-01-15T09:30:00Z",
"createdAt": "2025-01-15T09:30:00Z",
"cancelable": true
}
]
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"rewards": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"name": {
"type": "string",
"description": "What the reward was called when it was issued."
},
"reward": {
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "points",
"description": "Credits the customer with loyalty points."
},
"amount": {
"type": "number",
"description": "How many points to credit."
}
},
"required": ["type", "amount"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "amountCoupon",
"description": "Issues a fixed-value discount coupon."
},
"amountCents": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991,
"description": "Face value of the coupon, in cents."
},
"productFilter": {
"anyOf": [
{
"type": "object",
"properties": {
"slugs": {
"type": "array",
"items": {
"type": "string"
},
"description": "Product slugs the reward applies to."
},
"tags": {
"type": "array",
"items": {
"type": "string"
},
"description": "Product tag names the reward applies to."
}
},
"required": ["slugs", "tags"],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "Restricts the coupon to matching products. Null means it applies to anything."
},
"minimumOrderTotalCents": {
"anyOf": [
{
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "Minimum order total, in cents, before the coupon can be used. Null for none."
},
"expiry": {
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "never",
"description": "The reward stays redeemable until it is used or withdrawn."
}
},
"required": ["type"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "daysAfterIssuance",
"description": "The reward lapses a set number of days after each member gets it."
},
"days": {
"type": "integer",
"minimum": 1,
"maximum": 3650,
"description": "How many days after issuance the reward stops being redeemable."
}
},
"required": ["type", "days"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "fixedDate",
"description": "Every reward issued from this rule lapses at the same moment."
},
"expiresAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "The moment the reward stops being redeemable, as an ISO 8601 timestamp."
}
},
"required": ["type", "expiresAt"],
"additionalProperties": false
}
],
"description": "When a reward issued from this rule stops being redeemable. Omitted means the program's default applies."
}
},
"required": ["type", "amountCents", "productFilter", "minimumOrderTotalCents"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "percentageCoupon",
"description": "Issues a percentage-off discount coupon."
},
"percentageDiscount": {
"type": "integer",
"minimum": 1,
"maximum": 100,
"description": "Percentage taken off the qualifying items, from 1 to 100."
},
"productFilter": {
"anyOf": [
{
"type": "object",
"properties": {
"slugs": {
"type": "array",
"items": {
"type": "string"
},
"description": "Product slugs the reward applies to."
},
"tags": {
"type": "array",
"items": {
"type": "string"
},
"description": "Product tag names the reward applies to."
}
},
"required": ["slugs", "tags"],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "Restricts the coupon to matching products. Null means it applies to anything."
},
"minimumOrderTotalCents": {
"anyOf": [
{
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "Minimum order total, in cents, before the coupon can be used. Null for none."
},
"expiry": {
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "never",
"description": "The reward stays redeemable until it is used or withdrawn."
}
},
"required": ["type"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "daysAfterIssuance",
"description": "The reward lapses a set number of days after each member gets it."
},
"days": {
"type": "integer",
"minimum": 1,
"maximum": 3650,
"description": "How many days after issuance the reward stops being redeemable."
}
},
"required": ["type", "days"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "fixedDate",
"description": "Every reward issued from this rule lapses at the same moment."
},
"expiresAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "The moment the reward stops being redeemable, as an ISO 8601 timestamp."
}
},
"required": ["type", "expiresAt"],
"additionalProperties": false
}
],
"description": "When a reward issued from this rule stops being redeemable. Omitted means the program's default applies."
}
},
"required": [
"type",
"percentageDiscount",
"productFilter",
"minimumOrderTotalCents"
],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "freeShipping",
"description": "Waives shipping on the customer's next order."
},
"maximumValueCents": {
"anyOf": [
{
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "Cap on the shipping cost covered, in cents. Null means uncapped."
},
"expiry": {
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "never",
"description": "The reward stays redeemable until it is used or withdrawn."
}
},
"required": ["type"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "daysAfterIssuance",
"description": "The reward lapses a set number of days after each member gets it."
},
"days": {
"type": "integer",
"minimum": 1,
"maximum": 3650,
"description": "How many days after issuance the reward stops being redeemable."
}
},
"required": ["type", "days"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "fixedDate",
"description": "Every reward issued from this rule lapses at the same moment."
},
"expiresAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "The moment the reward stops being redeemable, as an ISO 8601 timestamp."
}
},
"required": ["type", "expiresAt"],
"additionalProperties": false
}
],
"description": "When a reward issued from this rule stops being redeemable. Omitted means the program's default applies."
}
},
"required": ["type", "maximumValueCents"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "giftCard",
"description": "Issues a gift card."
},
"amountCents": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991,
"description": "Value loaded onto the gift card, in cents."
},
"expirySeconds": {
"anyOf": [
{
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "How long the gift card's balance stays spendable, in seconds. Null means the balance never expires."
},
"expiry": {
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "never",
"description": "The reward stays redeemable until it is used or withdrawn."
}
},
"required": ["type"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "daysAfterIssuance",
"description": "The reward lapses a set number of days after each member gets it."
},
"days": {
"type": "integer",
"minimum": 1,
"maximum": 3650,
"description": "How many days after issuance the reward stops being redeemable."
}
},
"required": ["type", "days"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "fixedDate",
"description": "Every reward issued from this rule lapses at the same moment."
},
"expiresAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "The moment the reward stops being redeemable, as an ISO 8601 timestamp."
}
},
"required": ["type", "expiresAt"],
"additionalProperties": false
}
],
"description": "When a reward issued from this rule stops being redeemable. Omitted means the program's default applies."
}
},
"required": ["type", "amountCents", "expirySeconds"],
"additionalProperties": false
}
],
"description": "What the customer gets when the rule fires."
},
"status": {
"type": "string",
"enum": ["pending", "redeemed", "expired", "revoked"],
"description": "Where a reward has got to: `pending` means it is waiting to be used, `redeemed` means the customer used it, `expired` means it ran out of time, and `revoked` means it was withdrawn."
},
"code": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The code to present at checkout."
},
"pointsSpent": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "What the member paid for it. Null when it cost nothing."
},
"expiresAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
]
},
"redeemedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
]
},
"createdAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
"cancelable": {
"type": "boolean",
"description": "Whether the member can still cancel it and take their points back."
}
},
"required": [
"id",
"name",
"reward",
"status",
"code",
"pointsSpent",
"expiresAt",
"redeemedAt",
"createdAt",
"cancelable"
],
"additionalProperties": false
}
}
},
"required": ["rewards"],
"additionalProperties": false
}POST /widgets/loyalty/rewards/{id}/cancel
Cancel an unused reward and take the points it cost back
Path Parameters
idstring (uuid)requiredThe ID of the reward.
Query Parameters
apiPublishableKeystringrequiredYour organization's publishable key, from Settings, API keys.
customerEmailstring (email)requiredEmail address of the customer the storefront signed in.
timestampstringrequiredUnix time in seconds when the signature was generated. It is accepted for 24 hours.
signaturestringrequiredHex HMAC-SHA256 of customerEmail followed by timestamp, keyed with your widget signing secret. See the widgets guide for the recipe.
siteSlugstringWhich of the store's sites the widget is embedded on. Leave it out and the oldest site is used.
previewTokenstringA preview token from POST /loyalty-programs/{id}/preview. It adds the one draft program it names to the response, so a merchant can check a program before publishing it. The publishable key on its own never reaches a draft.
curl \
-X POST \
"https://api.cascade.dev/widgets/loyalty/rewards/550e8400-e29b-41d4-a716-446655440000/cancel?apiPublishableKey=pk_YOUR_PUBLISHABLE_KEY&customerEmail=shopper%40example.com×tamp=1735689600&signature=9f86d081..."import { client } from "@cascade-commerce/api/widget/client"
import { postWidgetsLoyaltyRewardsByIdCancel } from "@cascade-commerce/api/widget"
client.setConfig({
baseUrl: "https://api.cascade.dev",
})
const { data, error } = await postWidgetsLoyaltyRewardsByIdCancel({
path: { id: "550e8400-e29b-41d4-a716-446655440000" },
query: {
apiPublishableKey: "pk_YOUR_PUBLISHABLE_KEY",
customerEmail: "[email protected]",
timestamp: "1735689600",
signature: "9f86d081...",
},
})require "net/http"
uri = URI("https://api.cascade.dev/widgets/loyalty/rewards/550e8400-e29b-41d4-a716-446655440000/cancel?apiPublishableKey=pk_YOUR_PUBLISHABLE_KEY&customerEmail=shopper%40example.com×tamp=1735689600&signature=9f86d081...")
req = Net::HTTP::Post.new(uri)
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
puts res.bodyimport json
from urllib.request import Request, urlopen
req = Request(
"https://api.cascade.dev/widgets/loyalty/rewards/550e8400-e29b-41d4-a716-446655440000/cancel?apiPublishableKey=pk_YOUR_PUBLISHABLE_KEY&customerEmail=shopper%40example.com×tamp=1735689600&signature=9f86d081...",
method="POST",
)
with urlopen(req) as res:
print(json.load(res)){
"reward": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "string",
"reward": {
"type": "points",
"amount": 0
},
"status": "pending",
"code": "string",
"pointsSpent": 0,
"expiresAt": "2025-01-15T09:30:00Z",
"redeemedAt": "2025-01-15T09:30:00Z",
"createdAt": "2025-01-15T09:30:00Z",
"cancelable": true
},
"balance": 0
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"reward": {
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"name": {
"type": "string",
"description": "What the reward was called when it was issued."
},
"reward": {
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "points",
"description": "Credits the customer with loyalty points."
},
"amount": {
"type": "number",
"description": "How many points to credit."
}
},
"required": ["type", "amount"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "amountCoupon",
"description": "Issues a fixed-value discount coupon."
},
"amountCents": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991,
"description": "Face value of the coupon, in cents."
},
"productFilter": {
"anyOf": [
{
"type": "object",
"properties": {
"slugs": {
"type": "array",
"items": {
"type": "string"
},
"description": "Product slugs the reward applies to."
},
"tags": {
"type": "array",
"items": {
"type": "string"
},
"description": "Product tag names the reward applies to."
}
},
"required": ["slugs", "tags"],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "Restricts the coupon to matching products. Null means it applies to anything."
},
"minimumOrderTotalCents": {
"anyOf": [
{
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "Minimum order total, in cents, before the coupon can be used. Null for none."
},
"expiry": {
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "never",
"description": "The reward stays redeemable until it is used or withdrawn."
}
},
"required": ["type"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "daysAfterIssuance",
"description": "The reward lapses a set number of days after each member gets it."
},
"days": {
"type": "integer",
"minimum": 1,
"maximum": 3650,
"description": "How many days after issuance the reward stops being redeemable."
}
},
"required": ["type", "days"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "fixedDate",
"description": "Every reward issued from this rule lapses at the same moment."
},
"expiresAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "The moment the reward stops being redeemable, as an ISO 8601 timestamp."
}
},
"required": ["type", "expiresAt"],
"additionalProperties": false
}
],
"description": "When a reward issued from this rule stops being redeemable. Omitted means the program's default applies."
}
},
"required": ["type", "amountCents", "productFilter", "minimumOrderTotalCents"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "percentageCoupon",
"description": "Issues a percentage-off discount coupon."
},
"percentageDiscount": {
"type": "integer",
"minimum": 1,
"maximum": 100,
"description": "Percentage taken off the qualifying items, from 1 to 100."
},
"productFilter": {
"anyOf": [
{
"type": "object",
"properties": {
"slugs": {
"type": "array",
"items": {
"type": "string"
},
"description": "Product slugs the reward applies to."
},
"tags": {
"type": "array",
"items": {
"type": "string"
},
"description": "Product tag names the reward applies to."
}
},
"required": ["slugs", "tags"],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "Restricts the coupon to matching products. Null means it applies to anything."
},
"minimumOrderTotalCents": {
"anyOf": [
{
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "Minimum order total, in cents, before the coupon can be used. Null for none."
},
"expiry": {
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "never",
"description": "The reward stays redeemable until it is used or withdrawn."
}
},
"required": ["type"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "daysAfterIssuance",
"description": "The reward lapses a set number of days after each member gets it."
},
"days": {
"type": "integer",
"minimum": 1,
"maximum": 3650,
"description": "How many days after issuance the reward stops being redeemable."
}
},
"required": ["type", "days"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "fixedDate",
"description": "Every reward issued from this rule lapses at the same moment."
},
"expiresAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "The moment the reward stops being redeemable, as an ISO 8601 timestamp."
}
},
"required": ["type", "expiresAt"],
"additionalProperties": false
}
],
"description": "When a reward issued from this rule stops being redeemable. Omitted means the program's default applies."
}
},
"required": ["type", "percentageDiscount", "productFilter", "minimumOrderTotalCents"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "freeShipping",
"description": "Waives shipping on the customer's next order."
},
"maximumValueCents": {
"anyOf": [
{
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "Cap on the shipping cost covered, in cents. Null means uncapped."
},
"expiry": {
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "never",
"description": "The reward stays redeemable until it is used or withdrawn."
}
},
"required": ["type"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "daysAfterIssuance",
"description": "The reward lapses a set number of days after each member gets it."
},
"days": {
"type": "integer",
"minimum": 1,
"maximum": 3650,
"description": "How many days after issuance the reward stops being redeemable."
}
},
"required": ["type", "days"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "fixedDate",
"description": "Every reward issued from this rule lapses at the same moment."
},
"expiresAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "The moment the reward stops being redeemable, as an ISO 8601 timestamp."
}
},
"required": ["type", "expiresAt"],
"additionalProperties": false
}
],
"description": "When a reward issued from this rule stops being redeemable. Omitted means the program's default applies."
}
},
"required": ["type", "maximumValueCents"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "giftCard",
"description": "Issues a gift card."
},
"amountCents": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991,
"description": "Value loaded onto the gift card, in cents."
},
"expirySeconds": {
"anyOf": [
{
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "How long the gift card's balance stays spendable, in seconds. Null means the balance never expires."
},
"expiry": {
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "never",
"description": "The reward stays redeemable until it is used or withdrawn."
}
},
"required": ["type"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "daysAfterIssuance",
"description": "The reward lapses a set number of days after each member gets it."
},
"days": {
"type": "integer",
"minimum": 1,
"maximum": 3650,
"description": "How many days after issuance the reward stops being redeemable."
}
},
"required": ["type", "days"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "fixedDate",
"description": "Every reward issued from this rule lapses at the same moment."
},
"expiresAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "The moment the reward stops being redeemable, as an ISO 8601 timestamp."
}
},
"required": ["type", "expiresAt"],
"additionalProperties": false
}
],
"description": "When a reward issued from this rule stops being redeemable. Omitted means the program's default applies."
}
},
"required": ["type", "amountCents", "expirySeconds"],
"additionalProperties": false
}
],
"description": "What the customer gets when the rule fires."
},
"status": {
"type": "string",
"enum": ["pending", "redeemed", "expired", "revoked"],
"description": "Where a reward has got to: `pending` means it is waiting to be used, `redeemed` means the customer used it, `expired` means it ran out of time, and `revoked` means it was withdrawn."
},
"code": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The code to present at checkout."
},
"pointsSpent": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "What the member paid for it. Null when it cost nothing."
},
"expiresAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
]
},
"redeemedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
]
},
"createdAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
"cancelable": {
"type": "boolean",
"description": "Whether the member can still cancel it and take their points back."
}
},
"required": [
"id",
"name",
"reward",
"status",
"code",
"pointsSpent",
"expiresAt",
"redeemedAt",
"createdAt",
"cancelable"
],
"additionalProperties": false
},
"balance": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "The member's points balance after the refund."
}
},
"required": ["reward", "balance"],
"additionalProperties": false
}PATCH /widgets/loyalty/profile
Update the signed-in shopper's own loyalty details. The birthday is month and day only, so a birthday reward can find them without the store holding a date of birth.
Query Parameters
apiPublishableKeystringrequiredYour organization's publishable key, from Settings, API keys.
customerEmailstring (email)requiredEmail address of the customer the storefront signed in.
timestampstringrequiredUnix time in seconds when the signature was generated. It is accepted for 24 hours.
signaturestringrequiredHex HMAC-SHA256 of customerEmail followed by timestamp, keyed with your widget signing secret. See the widgets guide for the recipe.
siteSlugstringWhich of the store's sites the widget is embedded on. Leave it out and the oldest site is used.
previewTokenstringA preview token from POST /loyalty-programs/{id}/preview. It adds the one draft program it names to the response, so a merchant can check a program before publishing it. The publishable key on its own never reaches a draft.
Body Parameters
profileobjectrequiredcurl \
-X PATCH \
"https://api.cascade.dev/widgets/loyalty/profile?apiPublishableKey=pk_YOUR_PUBLISHABLE_KEY&customerEmail=shopper%40example.com×tamp=1735689600&signature=9f86d081..." \
-H "Content-Type: application/json" \
-d '{
"profile": {
"birthday": "string"
}
}'import { client } from "@cascade-commerce/api/widget/client"
import { patchWidgetsLoyaltyProfile } from "@cascade-commerce/api/widget"
client.setConfig({
baseUrl: "https://api.cascade.dev",
})
const { data, error } = await patchWidgetsLoyaltyProfile({
query: {
apiPublishableKey: "pk_YOUR_PUBLISHABLE_KEY",
customerEmail: "[email protected]",
timestamp: "1735689600",
signature: "9f86d081...",
},
body: {
profile: {
birthday: "string",
},
},
})require "net/http"
uri = URI("https://api.cascade.dev/widgets/loyalty/profile?apiPublishableKey=pk_YOUR_PUBLISHABLE_KEY&customerEmail=shopper%40example.com×tamp=1735689600&signature=9f86d081...")
req = Net::HTTP::Patch.new(uri)
req["Content-Type"] = "application/json"
req.body = <<~JSON
{
"profile": {
"birthday": "string"
}
}
JSON
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
puts res.bodyimport json
from urllib.request import Request, urlopen
body = """{
"profile": {
"birthday": "string"
}
}"""
req = Request(
"https://api.cascade.dev/widgets/loyalty/profile?apiPublishableKey=pk_YOUR_PUBLISHABLE_KEY&customerEmail=shopper%40example.com×tamp=1735689600&signature=9f86d081...",
data=body.encode(),
method="PATCH",
headers={"Content-Type": "application/json"},
)
with urlopen(req) as res:
print(json.load(res)){
"profile": {
"birthday": "string"
}
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"profile": {
"type": "object",
"properties": {
"birthday": {
"anyOf": [
{
"type": "string",
"pattern": "^(?:(?:0[13578]|1[02])-(?:0[1-9]|[12][0-9]|3[01])|(?:0[469]|11)-(?:0[1-9]|[12][0-9]|30)|02-(?:0[1-9]|1[0-9]|2[0-9]))$",
"description": "The customer's birthday as `MM-DD`. Month and day only: no year is collected or stored. February 29 is allowed, and in a year that has no February 29 it is treated as February 28."
},
{
"type": "null"
}
],
"description": "The shopper's birthday as `MM-DD`, such as `03-14`. Send null to remove it."
}
},
"required": ["birthday"],
"additionalProperties": false
}
},
"required": ["profile"],
"additionalProperties": false
}POST /widgets/loyalty/events
Record a storefront event for the shopper. The loyalty widget sends a visit on load; send it yourself if you built your own storefront.
Query Parameters
apiPublishableKeystringrequiredYour organization's publishable key, from Settings, API keys.
customerEmailstring (email)requiredEmail address of the customer the storefront signed in.
timestampstringrequiredUnix time in seconds when the signature was generated. It is accepted for 24 hours.
signaturestringrequiredHex HMAC-SHA256 of customerEmail followed by timestamp, keyed with your widget signing secret. See the widgets guide for the recipe.
siteSlugstringWhich of the store's sites the widget is embedded on. Leave it out and the oldest site is used.
previewTokenstringA preview token from POST /loyalty-programs/{id}/preview. It adds the one draft program it names to the response, so a merchant can check a program before publishing it. The publishable key on its own never reaches a draft.
Body Parameters
eventobjectrequiredcurl \
-X POST \
"https://api.cascade.dev/widgets/loyalty/events?apiPublishableKey=pk_YOUR_PUBLISHABLE_KEY&customerEmail=shopper%40example.com×tamp=1735689600&signature=9f86d081..." \
-H "Content-Type: application/json" \
-d '{
"event": {
"type": "visit",
"dedupeKey": "string",
"loyaltyProgramId": "550e8400-e29b-41d4-a716-446655440000"
}
}'import { client } from "@cascade-commerce/api/widget/client"
import { postWidgetsLoyaltyEvents } from "@cascade-commerce/api/widget"
client.setConfig({
baseUrl: "https://api.cascade.dev",
})
const { data, error } = await postWidgetsLoyaltyEvents({
query: {
apiPublishableKey: "pk_YOUR_PUBLISHABLE_KEY",
customerEmail: "[email protected]",
timestamp: "1735689600",
signature: "9f86d081...",
},
body: {
event: {
type: "visit",
dedupeKey: "string",
loyaltyProgramId: "550e8400-e29b-41d4-a716-446655440000",
},
},
})require "net/http"
uri = URI("https://api.cascade.dev/widgets/loyalty/events?apiPublishableKey=pk_YOUR_PUBLISHABLE_KEY&customerEmail=shopper%40example.com×tamp=1735689600&signature=9f86d081...")
req = Net::HTTP::Post.new(uri)
req["Content-Type"] = "application/json"
req.body = <<~JSON
{
"event": {
"type": "visit",
"dedupeKey": "string",
"loyaltyProgramId": "550e8400-e29b-41d4-a716-446655440000"
}
}
JSON
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
puts res.bodyimport json
from urllib.request import Request, urlopen
body = """{
"event": {
"type": "visit",
"dedupeKey": "string",
"loyaltyProgramId": "550e8400-e29b-41d4-a716-446655440000"
}
}"""
req = Request(
"https://api.cascade.dev/widgets/loyalty/events?apiPublishableKey=pk_YOUR_PUBLISHABLE_KEY&customerEmail=shopper%40example.com×tamp=1735689600&signature=9f86d081...",
data=body.encode(),
headers={"Content-Type": "application/json"},
)
with urlopen(req) as res:
print(json.load(res)){
"accepted": true
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"accepted": {
"type": "boolean",
"description": "The event was queued. Whether it earns anything is up to the program's rules."
}
},
"required": ["accepted"],
"additionalProperties": false
}