#Loyalty rewards
GET /loyalty-rewards
List the rewards members hold, newest first by default. Every reward a rule issued, a stamp card produced, a member claimed with points, or you issued by hand lands here. Supports search, filtering and cursor pagination.
Requires the loyalty:read permission.
Query Parameters
limitnumberThe number of records to return.
cursorstringA pagination cursor. Fetch the next page by passing the nextCursor value from the previous response.
sortcreatedAt | -createdAt | redeemedAt | -redeemedAt | expiresAt | -expiresAtThe sort order. Prefix a field with - to sort descending. Defaults to -createdAt.
searchstringA free-text search term, matched case-insensitively against the record's most identifying fields. Combined with filter using AND.
filterstringA JSON-encoded filter document, for example {"createdAt":{"$gte":"2025-01-01T00:00:00Z"}}. Filterable fields: id, loyaltyMembershipId, loyaltyRuleId, stampCardId, status, code, redeemedAt, expiresAt, revokedAt, createdAt, customer.id, customer.email, customer.firstName, customer.lastName, customer.metadata, customer.emailOptOut, customer.emailOptOutSource, customer.createdAt, rule.id, rule.name. See Filtering for the syntax.
includestringA comma-separated list of extra fields to compute and return. Each one costs an additional query, so ask only for what you need. Available: customer, membership, rule.
curl \
"https://api.cascade.dev/loyalty-rewards" \
-H "Authorization: Bearer sk_YOUR_SECRET_KEY"import { client } from "@cascade-commerce/api/admin/client"
import { getLoyaltyRewards } from "@cascade-commerce/api/admin"
client.setConfig({
baseUrl: "https://api.cascade.dev",
headers: { Authorization: "Bearer sk_YOUR_SECRET_KEY" },
})
const { data, error } = await getLoyaltyRewards()require "net/http"
uri = URI("https://api.cascade.dev/loyalty-rewards")
req = Net::HTTP::Get.new(uri)
req["Authorization"] = "Bearer sk_YOUR_SECRET_KEY"
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/loyalty-rewards",
headers={"Authorization": "Bearer sk_YOUR_SECRET_KEY"},
)
with urlopen(req) as res:
print(json.load(res)){
"data": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"loyaltyMembershipId": "550e8400-e29b-41d4-a716-446655440000",
"loyaltyRuleId": "550e8400-e29b-41d4-a716-446655440000",
"stampCardId": "550e8400-e29b-41d4-a716-446655440000",
"loyaltyTransactionId": "550e8400-e29b-41d4-a716-446655440000",
"reward": {
"type": "...",
"amount": "..."
},
"status": "pending",
"code": "string",
"redeemedAt": "2025-01-15T09:30:00Z",
"expiresAt": "2025-01-15T09:30:00Z",
"revokedAt": "2025-01-15T09:30:00Z",
"metadata": "...",
"fulfillment": {
"mode": "...",
"status": "...",
"shopifyDiscountId": "...",
"shopifyDiscountGid": "...",
"shop": "...",
"note": "..."
},
"ruleSnapshot": "...",
"createdAt": "2025-01-15T09:30:00Z",
"updatedAt": "2025-01-15T09:30:00Z",
"customer": {
"id": "...",
"email": "...",
"phone": "...",
"firstName": "...",
"lastName": "...",
"country": "...",
"birthday": "...",
"metadata": "...",
"notes": "...",
"emailOptOut": "...",
"emailOptOutAt": "...",
"emailOptOutSource": "...",
"createdAt": "...",
"updatedAt": "..."
},
"membership": {
"id": "...",
"customerId": "...",
"loyaltyProgramId": "...",
"loyaltyTierId": "...",
"tierAssignedManually": "...",
"tierAttainedAt": "...",
"tierReviewAt": "...",
"leftAt": "...",
"referralCode": "...",
"createdAt": "...",
"updatedAt": "..."
},
"rule": {
"id": "...",
"name": "...",
"section": "..."
}
}
],
"nextCursor": "string"
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"data": {
"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)$",
"description": "Unique identifier for the reward."
},
"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)$",
"description": "The loyalty membership the reward belongs to."
},
"loyaltyRuleId": {
"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 rule that issued the reward, or null when it was issued by hand."
},
"stampCardId": {
"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 that produced the reward, when one did."
},
"loyaltyTransactionId": {
"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 points entry the member paid with, when they claimed the reward. Null for a reward that cost nothing."
},
"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."
},
"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 the member presents at checkout. Unique across the organization."
},
"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"
}
],
"description": "When the code was used at checkout. Null while the reward is unused."
},
"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"
}
],
"description": "When the reward stops being redeemable. Null means it never expires."
},
"revokedAt": {
"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 reward was withdrawn, if it was."
},
"metadata": {
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {
"type": "string"
},
"description": "Free-form string key/value pairs for your own data. Individual keys are filterable with a dot path, for example `metadata.category`."
},
"fulfillment": {
"anyOf": [
{
"type": "object",
"properties": {
"mode": {
"type": "string",
"enum": ["shopify", "manual"],
"description": "How the code is honored. `shopify` means the connector created a real discount on the connected store; `manual` means the merchant applies the code themselves."
},
"status": {
"type": "string",
"enum": ["pending", "active", "deactivated", "failed"],
"description": "Where the Shopify discount got to. `pending` is queued, `active` is live at checkout, `deactivated` is switched off, and `failed` means Shopify refused it."
},
"shopifyDiscountId": {
"description": "The numeric id of the Shopify discount or gift card, when there is one.",
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"shopifyDiscountGid": {
"description": "The `gid://` form of the same id, which is what the Admin API takes.",
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"shop": {
"description": "The myshopify.com domain the discount lives on.",
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"note": {
"description": "Why the reward is honored by hand, when something stopped Shopify fulfilling it.",
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"required": ["mode", "status"],
"additionalProperties": false,
"description": "How a reward's code is honored at checkout."
},
{
"type": "null"
}
],
"description": "How the code is honored at checkout. Null until the reward has been through fulfillment."
},
"ruleSnapshot": {
"anyOf": [
{
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {}
},
{
"type": "null"
}
],
"description": "The rule's configuration when the reward was issued, so the reward still reads correctly after the rule is edited."
},
"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))$",
"description": "When the reward was issued."
},
"updatedAt": {
"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": "When the reward was last changed."
},
"customer": {
"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)$",
"description": "Unique identifier for the customer."
},
"email": {
"type": "string",
"format": "email",
"pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$",
"description": "The customer's email address, unique within the organization."
},
"phone": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The customer's phone number in E.164 format."
},
"firstName": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The customer's first name."
},
"lastName": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The customer's last name."
},
"country": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The customer's country, as an ISO 3166-1 code."
},
"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 customer's birthday as `MM-DD`, or null when they have not given one. Month and day only: no year is collected or stored."
},
"metadata": {
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {
"type": "string"
},
"description": "Free-form string key/value pairs for your own data. Individual keys are filterable with a dot path, for example `metadata.category`."
},
"notes": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Internal notes about the customer. Never shown to them."
},
"emailOptOut": {
"type": "boolean",
"description": "Whether the customer has opted out of marketing email. They still receive mail they asked for, such as a wishlist they shared themselves."
},
"emailOptOutAt": {
"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 opt-out was recorded. Null while the customer is still subscribed."
},
"emailOptOutSource": {
"anyOf": [
{
"type": "string",
"enum": ["customer", "merchant", "import", "shopify"],
"description": "Where the opt-out came from."
},
{
"type": "null"
}
],
"description": "Where the opt-out came from: `customer` (they opted out themselves, through an unsubscribe link or your integration), `merchant`, `import` or `shopify`. Null while the customer is still subscribed."
},
"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))$",
"description": "When the customer was first created in Cascade."
},
"updatedAt": {
"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": "When the customer was last changed."
}
},
"required": [
"id",
"email",
"phone",
"firstName",
"lastName",
"country",
"birthday",
"metadata",
"notes",
"emailOptOut",
"emailOptOutAt",
"emailOptOutSource",
"createdAt",
"updatedAt"
],
"additionalProperties": false,
"description": "The customer holding the reward, through the membership."
},
"membership": {
"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)$",
"description": "Unique identifier for the membership."
},
"customerId": {
"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)$",
"description": "The customer the membership belongs to."
},
"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)$",
"description": "The loyalty program the membership is in."
},
"loyaltyTierId": {
"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 tier the member currently sits in, if the program uses tiers."
},
"tierAssignedManually": {
"type": "boolean",
"description": "True when an admin pinned the tier. Pinned tiers are left alone by recalculation until the pin is removed."
},
"tierAttainedAt": {
"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 member reached their current tier, or null without a tier."
},
"tierReviewAt": {
"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 a rolling-window tier is next reviewed for a downgrade. Null for lifetime tiers, pinned tiers, and members without a tier."
},
"leftAt": {
"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 customer left the program, or null while they are an active member. A member who left keeps their row so rejoining can restore their balances."
},
"referralCode": {
"type": "string",
"description": "The member's shareable refer-a-friend code, stable for the life of the membership."
},
"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))$",
"description": "When the customer joined the program."
},
"updatedAt": {
"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": "When the membership was last changed."
}
},
"required": [
"id",
"customerId",
"loyaltyProgramId",
"loyaltyTierId",
"tierAssignedManually",
"tierAttainedAt",
"tierReviewAt",
"leftAt",
"referralCode",
"createdAt",
"updatedAt"
],
"additionalProperties": false,
"description": "The membership the reward belongs to."
},
"rule": {
"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)$",
"description": "Unique identifier for the rule."
},
"name": {
"type": "string",
"description": "The rule's display name."
},
"section": {
"type": "string",
"enum": ["rules", "rewards", "offers", "stampCards"],
"description": "The widget section the rule sits in."
}
},
"required": ["id", "name", "section"],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "The rule that issued the reward, or null when it was issued by hand."
}
},
"required": [
"id",
"loyaltyMembershipId",
"loyaltyRuleId",
"stampCardId",
"loyaltyTransactionId",
"reward",
"status",
"code",
"redeemedAt",
"expiresAt",
"revokedAt",
"metadata",
"fulfillment",
"ruleSnapshot",
"createdAt",
"updatedAt"
],
"additionalProperties": false
},
"description": "The page of records, in the requested sort order."
},
"nextCursor": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Pass this back as `cursor` to fetch the next page. Null once the last page has been returned."
}
},
"required": ["data", "nextCursor"],
"additionalProperties": false
}POST /loyalty-rewards
Issue a reward to one member outside any rule, for example as an apology or a prize. The reward comes out pending with a fresh code and shows up on the member's activity feed. Points are added through a balance adjustment instead. Triggers the loyaltyReward.issued webhook.
Requires the loyalty:write permission.
Body Parameters
dataobjectrequiredThe reward to issue.
curl \
-X POST \
"https://api.cascade.dev/loyalty-rewards" \
-H "Authorization: Bearer sk_YOUR_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"data": {
"loyaltyMembershipId": "550e8400-e29b-41d4-a716-446655440000",
"reward": {
"type": "points",
"amount": 0
},
"expiresAt": "2025-01-15T09:30:00Z",
"reason": "string"
}
}'import { client } from "@cascade-commerce/api/admin/client"
import { postLoyaltyRewards } from "@cascade-commerce/api/admin"
client.setConfig({
baseUrl: "https://api.cascade.dev",
headers: { Authorization: "Bearer sk_YOUR_SECRET_KEY" },
})
const { data, error } = await postLoyaltyRewards({
body: {
data: {
loyaltyMembershipId: "550e8400-e29b-41d4-a716-446655440000",
reward: {
type: "points",
amount: 0,
},
expiresAt: "2025-01-15T09:30:00Z",
reason: "string",
},
},
})require "net/http"
uri = URI("https://api.cascade.dev/loyalty-rewards")
req = Net::HTTP::Post.new(uri)
req["Authorization"] = "Bearer sk_YOUR_SECRET_KEY"
req["Content-Type"] = "application/json"
req.body = <<~JSON
{
"data": {
"loyaltyMembershipId": "550e8400-e29b-41d4-a716-446655440000",
"reward": {
"type": "points",
"amount": 0
},
"expiresAt": "2025-01-15T09:30:00Z",
"reason": "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 = """{
"data": {
"loyaltyMembershipId": "550e8400-e29b-41d4-a716-446655440000",
"reward": {
"type": "points",
"amount": 0
},
"expiresAt": "2025-01-15T09:30:00Z",
"reason": "string"
}
}"""
req = Request(
"https://api.cascade.dev/loyalty-rewards",
data=body.encode(),
headers={
"Authorization": "Bearer sk_YOUR_SECRET_KEY",
"Content-Type": "application/json",
},
)
with urlopen(req) as res:
print(json.load(res)){
"id": "550e8400-e29b-41d4-a716-446655440000",
"loyaltyMembershipId": "550e8400-e29b-41d4-a716-446655440000",
"loyaltyRuleId": "550e8400-e29b-41d4-a716-446655440000",
"stampCardId": "550e8400-e29b-41d4-a716-446655440000",
"loyaltyTransactionId": "550e8400-e29b-41d4-a716-446655440000",
"reward": {
"type": "points",
"amount": 0
},
"status": "pending",
"code": "string",
"redeemedAt": "2025-01-15T09:30:00Z",
"expiresAt": "2025-01-15T09:30:00Z",
"revokedAt": "2025-01-15T09:30:00Z",
"metadata": "...",
"fulfillment": {
"mode": "shopify",
"status": "pending",
"shopifyDiscountId": "string",
"shopifyDiscountGid": "string",
"shop": "string",
"note": "string"
},
"ruleSnapshot": "...",
"createdAt": "2025-01-15T09:30:00Z",
"updatedAt": "2025-01-15T09:30:00Z",
"customer": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"email": "[email protected]",
"phone": "string",
"firstName": "string",
"lastName": "string",
"country": "string",
"birthday": "string",
"metadata": "...",
"notes": "string",
"emailOptOut": true,
"emailOptOutAt": "2025-01-15T09:30:00Z",
"emailOptOutSource": "customer",
"createdAt": "2025-01-15T09:30:00Z",
"updatedAt": "2025-01-15T09:30:00Z"
},
"membership": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"customerId": "550e8400-e29b-41d4-a716-446655440000",
"loyaltyProgramId": "550e8400-e29b-41d4-a716-446655440000",
"loyaltyTierId": "550e8400-e29b-41d4-a716-446655440000",
"tierAssignedManually": true,
"tierAttainedAt": "2025-01-15T09:30:00Z",
"tierReviewAt": "2025-01-15T09:30:00Z",
"leftAt": "2025-01-15T09:30:00Z",
"referralCode": "string",
"createdAt": "2025-01-15T09:30:00Z",
"updatedAt": "2025-01-15T09:30:00Z"
},
"rule": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "string",
"section": "rules"
}
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"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)$",
"description": "Unique identifier for the reward."
},
"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)$",
"description": "The loyalty membership the reward belongs to."
},
"loyaltyRuleId": {
"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 rule that issued the reward, or null when it was issued by hand."
},
"stampCardId": {
"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 that produced the reward, when one did."
},
"loyaltyTransactionId": {
"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 points entry the member paid with, when they claimed the reward. Null for a reward that cost nothing."
},
"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."
},
"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 the member presents at checkout. Unique across the organization."
},
"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"
}
],
"description": "When the code was used at checkout. Null while the reward is unused."
},
"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"
}
],
"description": "When the reward stops being redeemable. Null means it never expires."
},
"revokedAt": {
"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 reward was withdrawn, if it was."
},
"metadata": {
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {
"type": "string"
},
"description": "Free-form string key/value pairs for your own data. Individual keys are filterable with a dot path, for example `metadata.category`."
},
"fulfillment": {
"anyOf": [
{
"type": "object",
"properties": {
"mode": {
"type": "string",
"enum": ["shopify", "manual"],
"description": "How the code is honored. `shopify` means the connector created a real discount on the connected store; `manual` means the merchant applies the code themselves."
},
"status": {
"type": "string",
"enum": ["pending", "active", "deactivated", "failed"],
"description": "Where the Shopify discount got to. `pending` is queued, `active` is live at checkout, `deactivated` is switched off, and `failed` means Shopify refused it."
},
"shopifyDiscountId": {
"description": "The numeric id of the Shopify discount or gift card, when there is one.",
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"shopifyDiscountGid": {
"description": "The `gid://` form of the same id, which is what the Admin API takes.",
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"shop": {
"description": "The myshopify.com domain the discount lives on.",
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"note": {
"description": "Why the reward is honored by hand, when something stopped Shopify fulfilling it.",
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"required": ["mode", "status"],
"additionalProperties": false,
"description": "How a reward's code is honored at checkout."
},
{
"type": "null"
}
],
"description": "How the code is honored at checkout. Null until the reward has been through fulfillment."
},
"ruleSnapshot": {
"anyOf": [
{
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {}
},
{
"type": "null"
}
],
"description": "The rule's configuration when the reward was issued, so the reward still reads correctly after the rule is edited."
},
"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))$",
"description": "When the reward was issued."
},
"updatedAt": {
"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": "When the reward was last changed."
},
"customer": {
"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)$",
"description": "Unique identifier for the customer."
},
"email": {
"type": "string",
"format": "email",
"pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$",
"description": "The customer's email address, unique within the organization."
},
"phone": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The customer's phone number in E.164 format."
},
"firstName": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The customer's first name."
},
"lastName": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The customer's last name."
},
"country": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The customer's country, as an ISO 3166-1 code."
},
"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 customer's birthday as `MM-DD`, or null when they have not given one. Month and day only: no year is collected or stored."
},
"metadata": {
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {
"type": "string"
},
"description": "Free-form string key/value pairs for your own data. Individual keys are filterable with a dot path, for example `metadata.category`."
},
"notes": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Internal notes about the customer. Never shown to them."
},
"emailOptOut": {
"type": "boolean",
"description": "Whether the customer has opted out of marketing email. They still receive mail they asked for, such as a wishlist they shared themselves."
},
"emailOptOutAt": {
"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 opt-out was recorded. Null while the customer is still subscribed."
},
"emailOptOutSource": {
"anyOf": [
{
"type": "string",
"enum": ["customer", "merchant", "import", "shopify"],
"description": "Where the opt-out came from."
},
{
"type": "null"
}
],
"description": "Where the opt-out came from: `customer` (they opted out themselves, through an unsubscribe link or your integration), `merchant`, `import` or `shopify`. Null while the customer is still subscribed."
},
"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))$",
"description": "When the customer was first created in Cascade."
},
"updatedAt": {
"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": "When the customer was last changed."
}
},
"required": [
"id",
"email",
"phone",
"firstName",
"lastName",
"country",
"birthday",
"metadata",
"notes",
"emailOptOut",
"emailOptOutAt",
"emailOptOutSource",
"createdAt",
"updatedAt"
],
"additionalProperties": false,
"description": "The customer holding the reward, through the membership."
},
"membership": {
"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)$",
"description": "Unique identifier for the membership."
},
"customerId": {
"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)$",
"description": "The customer the membership belongs to."
},
"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)$",
"description": "The loyalty program the membership is in."
},
"loyaltyTierId": {
"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 tier the member currently sits in, if the program uses tiers."
},
"tierAssignedManually": {
"type": "boolean",
"description": "True when an admin pinned the tier. Pinned tiers are left alone by recalculation until the pin is removed."
},
"tierAttainedAt": {
"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 member reached their current tier, or null without a tier."
},
"tierReviewAt": {
"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 a rolling-window tier is next reviewed for a downgrade. Null for lifetime tiers, pinned tiers, and members without a tier."
},
"leftAt": {
"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 customer left the program, or null while they are an active member. A member who left keeps their row so rejoining can restore their balances."
},
"referralCode": {
"type": "string",
"description": "The member's shareable refer-a-friend code, stable for the life of the membership."
},
"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))$",
"description": "When the customer joined the program."
},
"updatedAt": {
"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": "When the membership was last changed."
}
},
"required": [
"id",
"customerId",
"loyaltyProgramId",
"loyaltyTierId",
"tierAssignedManually",
"tierAttainedAt",
"tierReviewAt",
"leftAt",
"referralCode",
"createdAt",
"updatedAt"
],
"additionalProperties": false,
"description": "The membership the reward belongs to."
},
"rule": {
"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)$",
"description": "Unique identifier for the rule."
},
"name": {
"type": "string",
"description": "The rule's display name."
},
"section": {
"type": "string",
"enum": ["rules", "rewards", "offers", "stampCards"],
"description": "The widget section the rule sits in."
}
},
"required": ["id", "name", "section"],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "The rule that issued the reward, or null when it was issued by hand."
}
},
"required": [
"id",
"loyaltyMembershipId",
"loyaltyRuleId",
"stampCardId",
"loyaltyTransactionId",
"reward",
"status",
"code",
"redeemedAt",
"expiresAt",
"revokedAt",
"metadata",
"fulfillment",
"ruleSnapshot",
"createdAt",
"updatedAt"
],
"additionalProperties": false,
"description": "The new reward."
}GET /loyalty-rewards/lookup
Look a reward up by the code printed on it, so a point of sale or storefront can check it before honoring it. The code is matched whatever case it is typed in. Check status and expiresAt on the result: only a pending reward that has not expired can be used.
Requires the loyalty:read permission.
Query Parameters
codestringrequiredThe code printed on the reward.
includestringA comma-separated list of extra fields to compute and return. Each one costs an additional query, so ask only for what you need. Available: customer, membership, rule.
curl \
"https://api.cascade.dev/loyalty-rewards/lookup" \
-H "Authorization: Bearer sk_YOUR_SECRET_KEY"import { client } from "@cascade-commerce/api/admin/client"
import { getLoyaltyRewardsLookup } from "@cascade-commerce/api/admin"
client.setConfig({
baseUrl: "https://api.cascade.dev",
headers: { Authorization: "Bearer sk_YOUR_SECRET_KEY" },
})
const { data, error } = await getLoyaltyRewardsLookup({
query: { code: "string" },
})require "net/http"
uri = URI("https://api.cascade.dev/loyalty-rewards/lookup")
req = Net::HTTP::Get.new(uri)
req["Authorization"] = "Bearer sk_YOUR_SECRET_KEY"
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/loyalty-rewards/lookup",
headers={"Authorization": "Bearer sk_YOUR_SECRET_KEY"},
)
with urlopen(req) as res:
print(json.load(res)){
"id": "550e8400-e29b-41d4-a716-446655440000",
"loyaltyMembershipId": "550e8400-e29b-41d4-a716-446655440000",
"loyaltyRuleId": "550e8400-e29b-41d4-a716-446655440000",
"stampCardId": "550e8400-e29b-41d4-a716-446655440000",
"loyaltyTransactionId": "550e8400-e29b-41d4-a716-446655440000",
"reward": {
"type": "points",
"amount": 0
},
"status": "pending",
"code": "string",
"redeemedAt": "2025-01-15T09:30:00Z",
"expiresAt": "2025-01-15T09:30:00Z",
"revokedAt": "2025-01-15T09:30:00Z",
"metadata": "...",
"fulfillment": {
"mode": "shopify",
"status": "pending",
"shopifyDiscountId": "string",
"shopifyDiscountGid": "string",
"shop": "string",
"note": "string"
},
"ruleSnapshot": "...",
"createdAt": "2025-01-15T09:30:00Z",
"updatedAt": "2025-01-15T09:30:00Z",
"customer": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"email": "[email protected]",
"phone": "string",
"firstName": "string",
"lastName": "string",
"country": "string",
"birthday": "string",
"metadata": "...",
"notes": "string",
"emailOptOut": true,
"emailOptOutAt": "2025-01-15T09:30:00Z",
"emailOptOutSource": "customer",
"createdAt": "2025-01-15T09:30:00Z",
"updatedAt": "2025-01-15T09:30:00Z"
},
"membership": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"customerId": "550e8400-e29b-41d4-a716-446655440000",
"loyaltyProgramId": "550e8400-e29b-41d4-a716-446655440000",
"loyaltyTierId": "550e8400-e29b-41d4-a716-446655440000",
"tierAssignedManually": true,
"tierAttainedAt": "2025-01-15T09:30:00Z",
"tierReviewAt": "2025-01-15T09:30:00Z",
"leftAt": "2025-01-15T09:30:00Z",
"referralCode": "string",
"createdAt": "2025-01-15T09:30:00Z",
"updatedAt": "2025-01-15T09:30:00Z"
},
"rule": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "string",
"section": "rules"
}
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"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)$",
"description": "Unique identifier for the reward."
},
"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)$",
"description": "The loyalty membership the reward belongs to."
},
"loyaltyRuleId": {
"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 rule that issued the reward, or null when it was issued by hand."
},
"stampCardId": {
"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 that produced the reward, when one did."
},
"loyaltyTransactionId": {
"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 points entry the member paid with, when they claimed the reward. Null for a reward that cost nothing."
},
"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."
},
"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 the member presents at checkout. Unique across the organization."
},
"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"
}
],
"description": "When the code was used at checkout. Null while the reward is unused."
},
"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"
}
],
"description": "When the reward stops being redeemable. Null means it never expires."
},
"revokedAt": {
"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 reward was withdrawn, if it was."
},
"metadata": {
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {
"type": "string"
},
"description": "Free-form string key/value pairs for your own data. Individual keys are filterable with a dot path, for example `metadata.category`."
},
"fulfillment": {
"anyOf": [
{
"type": "object",
"properties": {
"mode": {
"type": "string",
"enum": ["shopify", "manual"],
"description": "How the code is honored. `shopify` means the connector created a real discount on the connected store; `manual` means the merchant applies the code themselves."
},
"status": {
"type": "string",
"enum": ["pending", "active", "deactivated", "failed"],
"description": "Where the Shopify discount got to. `pending` is queued, `active` is live at checkout, `deactivated` is switched off, and `failed` means Shopify refused it."
},
"shopifyDiscountId": {
"description": "The numeric id of the Shopify discount or gift card, when there is one.",
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"shopifyDiscountGid": {
"description": "The `gid://` form of the same id, which is what the Admin API takes.",
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"shop": {
"description": "The myshopify.com domain the discount lives on.",
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"note": {
"description": "Why the reward is honored by hand, when something stopped Shopify fulfilling it.",
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"required": ["mode", "status"],
"additionalProperties": false,
"description": "How a reward's code is honored at checkout."
},
{
"type": "null"
}
],
"description": "How the code is honored at checkout. Null until the reward has been through fulfillment."
},
"ruleSnapshot": {
"anyOf": [
{
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {}
},
{
"type": "null"
}
],
"description": "The rule's configuration when the reward was issued, so the reward still reads correctly after the rule is edited."
},
"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))$",
"description": "When the reward was issued."
},
"updatedAt": {
"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": "When the reward was last changed."
},
"customer": {
"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)$",
"description": "Unique identifier for the customer."
},
"email": {
"type": "string",
"format": "email",
"pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$",
"description": "The customer's email address, unique within the organization."
},
"phone": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The customer's phone number in E.164 format."
},
"firstName": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The customer's first name."
},
"lastName": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The customer's last name."
},
"country": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The customer's country, as an ISO 3166-1 code."
},
"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 customer's birthday as `MM-DD`, or null when they have not given one. Month and day only: no year is collected or stored."
},
"metadata": {
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {
"type": "string"
},
"description": "Free-form string key/value pairs for your own data. Individual keys are filterable with a dot path, for example `metadata.category`."
},
"notes": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Internal notes about the customer. Never shown to them."
},
"emailOptOut": {
"type": "boolean",
"description": "Whether the customer has opted out of marketing email. They still receive mail they asked for, such as a wishlist they shared themselves."
},
"emailOptOutAt": {
"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 opt-out was recorded. Null while the customer is still subscribed."
},
"emailOptOutSource": {
"anyOf": [
{
"type": "string",
"enum": ["customer", "merchant", "import", "shopify"],
"description": "Where the opt-out came from."
},
{
"type": "null"
}
],
"description": "Where the opt-out came from: `customer` (they opted out themselves, through an unsubscribe link or your integration), `merchant`, `import` or `shopify`. Null while the customer is still subscribed."
},
"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))$",
"description": "When the customer was first created in Cascade."
},
"updatedAt": {
"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": "When the customer was last changed."
}
},
"required": [
"id",
"email",
"phone",
"firstName",
"lastName",
"country",
"birthday",
"metadata",
"notes",
"emailOptOut",
"emailOptOutAt",
"emailOptOutSource",
"createdAt",
"updatedAt"
],
"additionalProperties": false,
"description": "The customer holding the reward, through the membership."
},
"membership": {
"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)$",
"description": "Unique identifier for the membership."
},
"customerId": {
"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)$",
"description": "The customer the membership belongs to."
},
"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)$",
"description": "The loyalty program the membership is in."
},
"loyaltyTierId": {
"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 tier the member currently sits in, if the program uses tiers."
},
"tierAssignedManually": {
"type": "boolean",
"description": "True when an admin pinned the tier. Pinned tiers are left alone by recalculation until the pin is removed."
},
"tierAttainedAt": {
"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 member reached their current tier, or null without a tier."
},
"tierReviewAt": {
"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 a rolling-window tier is next reviewed for a downgrade. Null for lifetime tiers, pinned tiers, and members without a tier."
},
"leftAt": {
"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 customer left the program, or null while they are an active member. A member who left keeps their row so rejoining can restore their balances."
},
"referralCode": {
"type": "string",
"description": "The member's shareable refer-a-friend code, stable for the life of the membership."
},
"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))$",
"description": "When the customer joined the program."
},
"updatedAt": {
"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": "When the membership was last changed."
}
},
"required": [
"id",
"customerId",
"loyaltyProgramId",
"loyaltyTierId",
"tierAssignedManually",
"tierAttainedAt",
"tierReviewAt",
"leftAt",
"referralCode",
"createdAt",
"updatedAt"
],
"additionalProperties": false,
"description": "The membership the reward belongs to."
},
"rule": {
"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)$",
"description": "Unique identifier for the rule."
},
"name": {
"type": "string",
"description": "The rule's display name."
},
"section": {
"type": "string",
"enum": ["rules", "rewards", "offers", "stampCards"],
"description": "The widget section the rule sits in."
}
},
"required": ["id", "name", "section"],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "The rule that issued the reward, or null when it was issued by hand."
}
},
"required": [
"id",
"loyaltyMembershipId",
"loyaltyRuleId",
"stampCardId",
"loyaltyTransactionId",
"reward",
"status",
"code",
"redeemedAt",
"expiresAt",
"revokedAt",
"metadata",
"fulfillment",
"ruleSnapshot",
"createdAt",
"updatedAt"
],
"additionalProperties": false,
"description": "The matching reward."
}POST /loyalty-rewards/claims
Spend a member's points on a reward. The balance check, the debit and the reward all happen together, so a claim the member cannot afford leaves nothing behind and returns a 409. Rules with a variable amount take pointsToSpend and scale the reward with it. Claiming is not redeeming: the reward comes out pending and is marked redeemed when its code is used. Triggers the loyaltyReward.issued and loyaltyTransaction.created webhooks.
Requires the loyalty:write permission.
Body Parameters
dataobjectrequiredThe claim to make.
curl \
-X POST \
"https://api.cascade.dev/loyalty-rewards/claims" \
-H "Authorization: Bearer sk_YOUR_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"data": {
"loyaltyMembershipId": "550e8400-e29b-41d4-a716-446655440000",
"loyaltyRuleId": "550e8400-e29b-41d4-a716-446655440000",
"pointsToSpend": 0
}
}'import { client } from "@cascade-commerce/api/admin/client"
import { postLoyaltyRewardsClaims } from "@cascade-commerce/api/admin"
client.setConfig({
baseUrl: "https://api.cascade.dev",
headers: { Authorization: "Bearer sk_YOUR_SECRET_KEY" },
})
const { data, error } = await postLoyaltyRewardsClaims({
body: {
data: {
loyaltyMembershipId: "550e8400-e29b-41d4-a716-446655440000",
loyaltyRuleId: "550e8400-e29b-41d4-a716-446655440000",
pointsToSpend: 0,
},
},
})require "net/http"
uri = URI("https://api.cascade.dev/loyalty-rewards/claims")
req = Net::HTTP::Post.new(uri)
req["Authorization"] = "Bearer sk_YOUR_SECRET_KEY"
req["Content-Type"] = "application/json"
req.body = <<~JSON
{
"data": {
"loyaltyMembershipId": "550e8400-e29b-41d4-a716-446655440000",
"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 = """{
"data": {
"loyaltyMembershipId": "550e8400-e29b-41d4-a716-446655440000",
"loyaltyRuleId": "550e8400-e29b-41d4-a716-446655440000",
"pointsToSpend": 0
}
}"""
req = Request(
"https://api.cascade.dev/loyalty-rewards/claims",
data=body.encode(),
headers={
"Authorization": "Bearer sk_YOUR_SECRET_KEY",
"Content-Type": "application/json",
},
)
with urlopen(req) as res:
print(json.load(res)){
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"loyaltyMembershipId": "550e8400-e29b-41d4-a716-446655440000",
"loyaltyRuleId": "550e8400-e29b-41d4-a716-446655440000",
"stampCardId": "550e8400-e29b-41d4-a716-446655440000",
"loyaltyTransactionId": "550e8400-e29b-41d4-a716-446655440000",
"reward": {
"type": "points",
"amount": 0
},
"status": "pending",
"code": "string",
"redeemedAt": "2025-01-15T09:30:00Z",
"expiresAt": "2025-01-15T09:30:00Z",
"revokedAt": "2025-01-15T09:30:00Z",
"metadata": "...",
"fulfillment": {
"mode": "shopify",
"status": "pending",
"shopifyDiscountId": "string",
"shopifyDiscountGid": "string",
"shop": "string",
"note": "string"
},
"ruleSnapshot": "...",
"createdAt": "2025-01-15T09:30:00Z",
"updatedAt": "2025-01-15T09:30:00Z",
"customer": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"email": "[email protected]",
"phone": "string",
"firstName": "string",
"lastName": "string",
"country": "string",
"birthday": "string",
"metadata": "...",
"notes": "string",
"emailOptOut": true,
"emailOptOutAt": "2025-01-15T09:30:00Z",
"emailOptOutSource": "customer",
"createdAt": "2025-01-15T09:30:00Z",
"updatedAt": "2025-01-15T09:30:00Z"
},
"membership": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"customerId": "550e8400-e29b-41d4-a716-446655440000",
"loyaltyProgramId": "550e8400-e29b-41d4-a716-446655440000",
"loyaltyTierId": "550e8400-e29b-41d4-a716-446655440000",
"tierAssignedManually": true,
"tierAttainedAt": "2025-01-15T09:30:00Z",
"tierReviewAt": "2025-01-15T09:30:00Z",
"leftAt": "2025-01-15T09:30:00Z",
"referralCode": "string",
"createdAt": "2025-01-15T09:30:00Z",
"updatedAt": "2025-01-15T09:30:00Z"
},
"rule": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "string",
"section": "rules"
}
},
"pointsSpent": 0
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"data": {
"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)$",
"description": "Unique identifier for the reward."
},
"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)$",
"description": "The loyalty membership the reward belongs to."
},
"loyaltyRuleId": {
"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 rule that issued the reward, or null when it was issued by hand."
},
"stampCardId": {
"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 that produced the reward, when one did."
},
"loyaltyTransactionId": {
"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 points entry the member paid with, when they claimed the reward. Null for a reward that cost nothing."
},
"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."
},
"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 the member presents at checkout. Unique across the organization."
},
"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"
}
],
"description": "When the code was used at checkout. Null while the reward is unused."
},
"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"
}
],
"description": "When the reward stops being redeemable. Null means it never expires."
},
"revokedAt": {
"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 reward was withdrawn, if it was."
},
"metadata": {
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {
"type": "string"
},
"description": "Free-form string key/value pairs for your own data. Individual keys are filterable with a dot path, for example `metadata.category`."
},
"fulfillment": {
"anyOf": [
{
"type": "object",
"properties": {
"mode": {
"type": "string",
"enum": ["shopify", "manual"],
"description": "How the code is honored. `shopify` means the connector created a real discount on the connected store; `manual` means the merchant applies the code themselves."
},
"status": {
"type": "string",
"enum": ["pending", "active", "deactivated", "failed"],
"description": "Where the Shopify discount got to. `pending` is queued, `active` is live at checkout, `deactivated` is switched off, and `failed` means Shopify refused it."
},
"shopifyDiscountId": {
"description": "The numeric id of the Shopify discount or gift card, when there is one.",
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"shopifyDiscountGid": {
"description": "The `gid://` form of the same id, which is what the Admin API takes.",
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"shop": {
"description": "The myshopify.com domain the discount lives on.",
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"note": {
"description": "Why the reward is honored by hand, when something stopped Shopify fulfilling it.",
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"required": ["mode", "status"],
"additionalProperties": false,
"description": "How a reward's code is honored at checkout."
},
{
"type": "null"
}
],
"description": "How the code is honored at checkout. Null until the reward has been through fulfillment."
},
"ruleSnapshot": {
"anyOf": [
{
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {}
},
{
"type": "null"
}
],
"description": "The rule's configuration when the reward was issued, so the reward still reads correctly after the rule is edited."
},
"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))$",
"description": "When the reward was issued."
},
"updatedAt": {
"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": "When the reward was last changed."
},
"customer": {
"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)$",
"description": "Unique identifier for the customer."
},
"email": {
"type": "string",
"format": "email",
"pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$",
"description": "The customer's email address, unique within the organization."
},
"phone": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The customer's phone number in E.164 format."
},
"firstName": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The customer's first name."
},
"lastName": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The customer's last name."
},
"country": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The customer's country, as an ISO 3166-1 code."
},
"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 customer's birthday as `MM-DD`, or null when they have not given one. Month and day only: no year is collected or stored."
},
"metadata": {
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {
"type": "string"
},
"description": "Free-form string key/value pairs for your own data. Individual keys are filterable with a dot path, for example `metadata.category`."
},
"notes": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Internal notes about the customer. Never shown to them."
},
"emailOptOut": {
"type": "boolean",
"description": "Whether the customer has opted out of marketing email. They still receive mail they asked for, such as a wishlist they shared themselves."
},
"emailOptOutAt": {
"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 opt-out was recorded. Null while the customer is still subscribed."
},
"emailOptOutSource": {
"anyOf": [
{
"type": "string",
"enum": ["customer", "merchant", "import", "shopify"],
"description": "Where the opt-out came from."
},
{
"type": "null"
}
],
"description": "Where the opt-out came from: `customer` (they opted out themselves, through an unsubscribe link or your integration), `merchant`, `import` or `shopify`. Null while the customer is still subscribed."
},
"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))$",
"description": "When the customer was first created in Cascade."
},
"updatedAt": {
"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": "When the customer was last changed."
}
},
"required": [
"id",
"email",
"phone",
"firstName",
"lastName",
"country",
"birthday",
"metadata",
"notes",
"emailOptOut",
"emailOptOutAt",
"emailOptOutSource",
"createdAt",
"updatedAt"
],
"additionalProperties": false,
"description": "The customer holding the reward, through the membership."
},
"membership": {
"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)$",
"description": "Unique identifier for the membership."
},
"customerId": {
"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)$",
"description": "The customer the membership belongs to."
},
"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)$",
"description": "The loyalty program the membership is in."
},
"loyaltyTierId": {
"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 tier the member currently sits in, if the program uses tiers."
},
"tierAssignedManually": {
"type": "boolean",
"description": "True when an admin pinned the tier. Pinned tiers are left alone by recalculation until the pin is removed."
},
"tierAttainedAt": {
"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 member reached their current tier, or null without a tier."
},
"tierReviewAt": {
"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 a rolling-window tier is next reviewed for a downgrade. Null for lifetime tiers, pinned tiers, and members without a tier."
},
"leftAt": {
"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 customer left the program, or null while they are an active member. A member who left keeps their row so rejoining can restore their balances."
},
"referralCode": {
"type": "string",
"description": "The member's shareable refer-a-friend code, stable for the life of the membership."
},
"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))$",
"description": "When the customer joined the program."
},
"updatedAt": {
"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": "When the membership was last changed."
}
},
"required": [
"id",
"customerId",
"loyaltyProgramId",
"loyaltyTierId",
"tierAssignedManually",
"tierAttainedAt",
"tierReviewAt",
"leftAt",
"referralCode",
"createdAt",
"updatedAt"
],
"additionalProperties": false,
"description": "The membership the reward belongs to."
},
"rule": {
"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)$",
"description": "Unique identifier for the rule."
},
"name": {
"type": "string",
"description": "The rule's display name."
},
"section": {
"type": "string",
"enum": ["rules", "rewards", "offers", "stampCards"],
"description": "The widget section the rule sits in."
}
},
"required": ["id", "name", "section"],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "The rule that issued the reward, or null when it was issued by hand."
}
},
"required": [
"id",
"loyaltyMembershipId",
"loyaltyRuleId",
"stampCardId",
"loyaltyTransactionId",
"reward",
"status",
"code",
"redeemedAt",
"expiresAt",
"revokedAt",
"metadata",
"fulfillment",
"ruleSnapshot",
"createdAt",
"updatedAt"
],
"additionalProperties": false,
"description": "The reward the member now holds."
},
"pointsSpent": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How many points the claim cost."
}
},
"required": ["data", "pointsSpent"],
"additionalProperties": false
}POST /loyalty-rewards/{id}/redeem
Mark a reward's code as used at checkout. Only a pending reward that has not expired can be redeemed, and only once: anything else returns a 409. Triggers the loyaltyReward.redeemed webhook.
Requires the loyalty:write permission.
Path Parameters
idstring (uuid)requiredThe ID of the reward being used.
curl \
-X POST \
"https://api.cascade.dev/loyalty-rewards/550e8400-e29b-41d4-a716-446655440000/redeem" \
-H "Authorization: Bearer sk_YOUR_SECRET_KEY"import { client } from "@cascade-commerce/api/admin/client"
import { postLoyaltyRewardsByIdRedeem } from "@cascade-commerce/api/admin"
client.setConfig({
baseUrl: "https://api.cascade.dev",
headers: { Authorization: "Bearer sk_YOUR_SECRET_KEY" },
})
const { data, error } = await postLoyaltyRewardsByIdRedeem({
path: { id: "550e8400-e29b-41d4-a716-446655440000" },
})require "net/http"
uri = URI("https://api.cascade.dev/loyalty-rewards/550e8400-e29b-41d4-a716-446655440000/redeem")
req = Net::HTTP::Post.new(uri)
req["Authorization"] = "Bearer sk_YOUR_SECRET_KEY"
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/loyalty-rewards/550e8400-e29b-41d4-a716-446655440000/redeem",
method="POST",
headers={"Authorization": "Bearer sk_YOUR_SECRET_KEY"},
)
with urlopen(req) as res:
print(json.load(res)){
"id": "550e8400-e29b-41d4-a716-446655440000",
"loyaltyMembershipId": "550e8400-e29b-41d4-a716-446655440000",
"loyaltyRuleId": "550e8400-e29b-41d4-a716-446655440000",
"stampCardId": "550e8400-e29b-41d4-a716-446655440000",
"loyaltyTransactionId": "550e8400-e29b-41d4-a716-446655440000",
"reward": {
"type": "points",
"amount": 0
},
"status": "pending",
"code": "string",
"redeemedAt": "2025-01-15T09:30:00Z",
"expiresAt": "2025-01-15T09:30:00Z",
"revokedAt": "2025-01-15T09:30:00Z",
"metadata": "...",
"fulfillment": {
"mode": "shopify",
"status": "pending",
"shopifyDiscountId": "string",
"shopifyDiscountGid": "string",
"shop": "string",
"note": "string"
},
"ruleSnapshot": "...",
"createdAt": "2025-01-15T09:30:00Z",
"updatedAt": "2025-01-15T09:30:00Z",
"customer": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"email": "[email protected]",
"phone": "string",
"firstName": "string",
"lastName": "string",
"country": "string",
"birthday": "string",
"metadata": "...",
"notes": "string",
"emailOptOut": true,
"emailOptOutAt": "2025-01-15T09:30:00Z",
"emailOptOutSource": "customer",
"createdAt": "2025-01-15T09:30:00Z",
"updatedAt": "2025-01-15T09:30:00Z"
},
"membership": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"customerId": "550e8400-e29b-41d4-a716-446655440000",
"loyaltyProgramId": "550e8400-e29b-41d4-a716-446655440000",
"loyaltyTierId": "550e8400-e29b-41d4-a716-446655440000",
"tierAssignedManually": true,
"tierAttainedAt": "2025-01-15T09:30:00Z",
"tierReviewAt": "2025-01-15T09:30:00Z",
"leftAt": "2025-01-15T09:30:00Z",
"referralCode": "string",
"createdAt": "2025-01-15T09:30:00Z",
"updatedAt": "2025-01-15T09:30:00Z"
},
"rule": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "string",
"section": "rules"
}
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"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)$",
"description": "Unique identifier for the reward."
},
"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)$",
"description": "The loyalty membership the reward belongs to."
},
"loyaltyRuleId": {
"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 rule that issued the reward, or null when it was issued by hand."
},
"stampCardId": {
"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 that produced the reward, when one did."
},
"loyaltyTransactionId": {
"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 points entry the member paid with, when they claimed the reward. Null for a reward that cost nothing."
},
"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."
},
"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 the member presents at checkout. Unique across the organization."
},
"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"
}
],
"description": "When the code was used at checkout. Null while the reward is unused."
},
"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"
}
],
"description": "When the reward stops being redeemable. Null means it never expires."
},
"revokedAt": {
"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 reward was withdrawn, if it was."
},
"metadata": {
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {
"type": "string"
},
"description": "Free-form string key/value pairs for your own data. Individual keys are filterable with a dot path, for example `metadata.category`."
},
"fulfillment": {
"anyOf": [
{
"type": "object",
"properties": {
"mode": {
"type": "string",
"enum": ["shopify", "manual"],
"description": "How the code is honored. `shopify` means the connector created a real discount on the connected store; `manual` means the merchant applies the code themselves."
},
"status": {
"type": "string",
"enum": ["pending", "active", "deactivated", "failed"],
"description": "Where the Shopify discount got to. `pending` is queued, `active` is live at checkout, `deactivated` is switched off, and `failed` means Shopify refused it."
},
"shopifyDiscountId": {
"description": "The numeric id of the Shopify discount or gift card, when there is one.",
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"shopifyDiscountGid": {
"description": "The `gid://` form of the same id, which is what the Admin API takes.",
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"shop": {
"description": "The myshopify.com domain the discount lives on.",
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"note": {
"description": "Why the reward is honored by hand, when something stopped Shopify fulfilling it.",
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"required": ["mode", "status"],
"additionalProperties": false,
"description": "How a reward's code is honored at checkout."
},
{
"type": "null"
}
],
"description": "How the code is honored at checkout. Null until the reward has been through fulfillment."
},
"ruleSnapshot": {
"anyOf": [
{
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {}
},
{
"type": "null"
}
],
"description": "The rule's configuration when the reward was issued, so the reward still reads correctly after the rule is edited."
},
"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))$",
"description": "When the reward was issued."
},
"updatedAt": {
"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": "When the reward was last changed."
},
"customer": {
"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)$",
"description": "Unique identifier for the customer."
},
"email": {
"type": "string",
"format": "email",
"pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$",
"description": "The customer's email address, unique within the organization."
},
"phone": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The customer's phone number in E.164 format."
},
"firstName": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The customer's first name."
},
"lastName": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The customer's last name."
},
"country": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The customer's country, as an ISO 3166-1 code."
},
"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 customer's birthday as `MM-DD`, or null when they have not given one. Month and day only: no year is collected or stored."
},
"metadata": {
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {
"type": "string"
},
"description": "Free-form string key/value pairs for your own data. Individual keys are filterable with a dot path, for example `metadata.category`."
},
"notes": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Internal notes about the customer. Never shown to them."
},
"emailOptOut": {
"type": "boolean",
"description": "Whether the customer has opted out of marketing email. They still receive mail they asked for, such as a wishlist they shared themselves."
},
"emailOptOutAt": {
"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 opt-out was recorded. Null while the customer is still subscribed."
},
"emailOptOutSource": {
"anyOf": [
{
"type": "string",
"enum": ["customer", "merchant", "import", "shopify"],
"description": "Where the opt-out came from."
},
{
"type": "null"
}
],
"description": "Where the opt-out came from: `customer` (they opted out themselves, through an unsubscribe link or your integration), `merchant`, `import` or `shopify`. Null while the customer is still subscribed."
},
"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))$",
"description": "When the customer was first created in Cascade."
},
"updatedAt": {
"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": "When the customer was last changed."
}
},
"required": [
"id",
"email",
"phone",
"firstName",
"lastName",
"country",
"birthday",
"metadata",
"notes",
"emailOptOut",
"emailOptOutAt",
"emailOptOutSource",
"createdAt",
"updatedAt"
],
"additionalProperties": false,
"description": "The customer holding the reward, through the membership."
},
"membership": {
"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)$",
"description": "Unique identifier for the membership."
},
"customerId": {
"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)$",
"description": "The customer the membership belongs to."
},
"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)$",
"description": "The loyalty program the membership is in."
},
"loyaltyTierId": {
"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 tier the member currently sits in, if the program uses tiers."
},
"tierAssignedManually": {
"type": "boolean",
"description": "True when an admin pinned the tier. Pinned tiers are left alone by recalculation until the pin is removed."
},
"tierAttainedAt": {
"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 member reached their current tier, or null without a tier."
},
"tierReviewAt": {
"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 a rolling-window tier is next reviewed for a downgrade. Null for lifetime tiers, pinned tiers, and members without a tier."
},
"leftAt": {
"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 customer left the program, or null while they are an active member. A member who left keeps their row so rejoining can restore their balances."
},
"referralCode": {
"type": "string",
"description": "The member's shareable refer-a-friend code, stable for the life of the membership."
},
"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))$",
"description": "When the customer joined the program."
},
"updatedAt": {
"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": "When the membership was last changed."
}
},
"required": [
"id",
"customerId",
"loyaltyProgramId",
"loyaltyTierId",
"tierAssignedManually",
"tierAttainedAt",
"tierReviewAt",
"leftAt",
"referralCode",
"createdAt",
"updatedAt"
],
"additionalProperties": false,
"description": "The membership the reward belongs to."
},
"rule": {
"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)$",
"description": "Unique identifier for the rule."
},
"name": {
"type": "string",
"description": "The rule's display name."
},
"section": {
"type": "string",
"enum": ["rules", "rewards", "offers", "stampCards"],
"description": "The widget section the rule sits in."
}
},
"required": ["id", "name", "section"],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "The rule that issued the reward, or null when it was issued by hand."
}
},
"required": [
"id",
"loyaltyMembershipId",
"loyaltyRuleId",
"stampCardId",
"loyaltyTransactionId",
"reward",
"status",
"code",
"redeemedAt",
"expiresAt",
"revokedAt",
"metadata",
"fulfillment",
"ruleSnapshot",
"createdAt",
"updatedAt"
],
"additionalProperties": false,
"description": "The redeemed reward."
}POST /loyalty-rewards/{id}/revoke
Withdraw a reward with a reason, which is kept on the audit trail. Points the member spent claiming it are given back through the ledger. A reward whose code was already used cannot be withdrawn; adjust the member's balance by hand instead. Triggers the loyaltyReward.revoked webhook.
Requires the loyalty:write permission.
Path Parameters
idstring (uuid)requiredThe ID of the reward to withdraw.
Body Parameters
dataobjectrequiredWhy the reward is being withdrawn.
curl \
-X POST \
"https://api.cascade.dev/loyalty-rewards/550e8400-e29b-41d4-a716-446655440000/revoke" \
-H "Authorization: Bearer sk_YOUR_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"data": {
"reason": "string"
}
}'import { client } from "@cascade-commerce/api/admin/client"
import { postLoyaltyRewardsByIdRevoke } from "@cascade-commerce/api/admin"
client.setConfig({
baseUrl: "https://api.cascade.dev",
headers: { Authorization: "Bearer sk_YOUR_SECRET_KEY" },
})
const { data, error } = await postLoyaltyRewardsByIdRevoke({
path: { id: "550e8400-e29b-41d4-a716-446655440000" },
body: {
data: {
reason: "string",
},
},
})require "net/http"
uri = URI("https://api.cascade.dev/loyalty-rewards/550e8400-e29b-41d4-a716-446655440000/revoke")
req = Net::HTTP::Post.new(uri)
req["Authorization"] = "Bearer sk_YOUR_SECRET_KEY"
req["Content-Type"] = "application/json"
req.body = <<~JSON
{
"data": {
"reason": "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 = """{
"data": {
"reason": "string"
}
}"""
req = Request(
"https://api.cascade.dev/loyalty-rewards/550e8400-e29b-41d4-a716-446655440000/revoke",
data=body.encode(),
headers={
"Authorization": "Bearer sk_YOUR_SECRET_KEY",
"Content-Type": "application/json",
},
)
with urlopen(req) as res:
print(json.load(res)){
"id": "550e8400-e29b-41d4-a716-446655440000",
"loyaltyMembershipId": "550e8400-e29b-41d4-a716-446655440000",
"loyaltyRuleId": "550e8400-e29b-41d4-a716-446655440000",
"stampCardId": "550e8400-e29b-41d4-a716-446655440000",
"loyaltyTransactionId": "550e8400-e29b-41d4-a716-446655440000",
"reward": {
"type": "points",
"amount": 0
},
"status": "pending",
"code": "string",
"redeemedAt": "2025-01-15T09:30:00Z",
"expiresAt": "2025-01-15T09:30:00Z",
"revokedAt": "2025-01-15T09:30:00Z",
"metadata": "...",
"fulfillment": {
"mode": "shopify",
"status": "pending",
"shopifyDiscountId": "string",
"shopifyDiscountGid": "string",
"shop": "string",
"note": "string"
},
"ruleSnapshot": "...",
"createdAt": "2025-01-15T09:30:00Z",
"updatedAt": "2025-01-15T09:30:00Z",
"customer": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"email": "[email protected]",
"phone": "string",
"firstName": "string",
"lastName": "string",
"country": "string",
"birthday": "string",
"metadata": "...",
"notes": "string",
"emailOptOut": true,
"emailOptOutAt": "2025-01-15T09:30:00Z",
"emailOptOutSource": "customer",
"createdAt": "2025-01-15T09:30:00Z",
"updatedAt": "2025-01-15T09:30:00Z"
},
"membership": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"customerId": "550e8400-e29b-41d4-a716-446655440000",
"loyaltyProgramId": "550e8400-e29b-41d4-a716-446655440000",
"loyaltyTierId": "550e8400-e29b-41d4-a716-446655440000",
"tierAssignedManually": true,
"tierAttainedAt": "2025-01-15T09:30:00Z",
"tierReviewAt": "2025-01-15T09:30:00Z",
"leftAt": "2025-01-15T09:30:00Z",
"referralCode": "string",
"createdAt": "2025-01-15T09:30:00Z",
"updatedAt": "2025-01-15T09:30:00Z"
},
"rule": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "string",
"section": "rules"
}
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"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)$",
"description": "Unique identifier for the reward."
},
"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)$",
"description": "The loyalty membership the reward belongs to."
},
"loyaltyRuleId": {
"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 rule that issued the reward, or null when it was issued by hand."
},
"stampCardId": {
"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 that produced the reward, when one did."
},
"loyaltyTransactionId": {
"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 points entry the member paid with, when they claimed the reward. Null for a reward that cost nothing."
},
"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."
},
"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 the member presents at checkout. Unique across the organization."
},
"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"
}
],
"description": "When the code was used at checkout. Null while the reward is unused."
},
"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"
}
],
"description": "When the reward stops being redeemable. Null means it never expires."
},
"revokedAt": {
"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 reward was withdrawn, if it was."
},
"metadata": {
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {
"type": "string"
},
"description": "Free-form string key/value pairs for your own data. Individual keys are filterable with a dot path, for example `metadata.category`."
},
"fulfillment": {
"anyOf": [
{
"type": "object",
"properties": {
"mode": {
"type": "string",
"enum": ["shopify", "manual"],
"description": "How the code is honored. `shopify` means the connector created a real discount on the connected store; `manual` means the merchant applies the code themselves."
},
"status": {
"type": "string",
"enum": ["pending", "active", "deactivated", "failed"],
"description": "Where the Shopify discount got to. `pending` is queued, `active` is live at checkout, `deactivated` is switched off, and `failed` means Shopify refused it."
},
"shopifyDiscountId": {
"description": "The numeric id of the Shopify discount or gift card, when there is one.",
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"shopifyDiscountGid": {
"description": "The `gid://` form of the same id, which is what the Admin API takes.",
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"shop": {
"description": "The myshopify.com domain the discount lives on.",
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"note": {
"description": "Why the reward is honored by hand, when something stopped Shopify fulfilling it.",
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"required": ["mode", "status"],
"additionalProperties": false,
"description": "How a reward's code is honored at checkout."
},
{
"type": "null"
}
],
"description": "How the code is honored at checkout. Null until the reward has been through fulfillment."
},
"ruleSnapshot": {
"anyOf": [
{
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {}
},
{
"type": "null"
}
],
"description": "The rule's configuration when the reward was issued, so the reward still reads correctly after the rule is edited."
},
"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))$",
"description": "When the reward was issued."
},
"updatedAt": {
"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": "When the reward was last changed."
},
"customer": {
"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)$",
"description": "Unique identifier for the customer."
},
"email": {
"type": "string",
"format": "email",
"pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$",
"description": "The customer's email address, unique within the organization."
},
"phone": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The customer's phone number in E.164 format."
},
"firstName": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The customer's first name."
},
"lastName": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The customer's last name."
},
"country": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The customer's country, as an ISO 3166-1 code."
},
"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 customer's birthday as `MM-DD`, or null when they have not given one. Month and day only: no year is collected or stored."
},
"metadata": {
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {
"type": "string"
},
"description": "Free-form string key/value pairs for your own data. Individual keys are filterable with a dot path, for example `metadata.category`."
},
"notes": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Internal notes about the customer. Never shown to them."
},
"emailOptOut": {
"type": "boolean",
"description": "Whether the customer has opted out of marketing email. They still receive mail they asked for, such as a wishlist they shared themselves."
},
"emailOptOutAt": {
"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 opt-out was recorded. Null while the customer is still subscribed."
},
"emailOptOutSource": {
"anyOf": [
{
"type": "string",
"enum": ["customer", "merchant", "import", "shopify"],
"description": "Where the opt-out came from."
},
{
"type": "null"
}
],
"description": "Where the opt-out came from: `customer` (they opted out themselves, through an unsubscribe link or your integration), `merchant`, `import` or `shopify`. Null while the customer is still subscribed."
},
"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))$",
"description": "When the customer was first created in Cascade."
},
"updatedAt": {
"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": "When the customer was last changed."
}
},
"required": [
"id",
"email",
"phone",
"firstName",
"lastName",
"country",
"birthday",
"metadata",
"notes",
"emailOptOut",
"emailOptOutAt",
"emailOptOutSource",
"createdAt",
"updatedAt"
],
"additionalProperties": false,
"description": "The customer holding the reward, through the membership."
},
"membership": {
"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)$",
"description": "Unique identifier for the membership."
},
"customerId": {
"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)$",
"description": "The customer the membership belongs to."
},
"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)$",
"description": "The loyalty program the membership is in."
},
"loyaltyTierId": {
"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 tier the member currently sits in, if the program uses tiers."
},
"tierAssignedManually": {
"type": "boolean",
"description": "True when an admin pinned the tier. Pinned tiers are left alone by recalculation until the pin is removed."
},
"tierAttainedAt": {
"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 member reached their current tier, or null without a tier."
},
"tierReviewAt": {
"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 a rolling-window tier is next reviewed for a downgrade. Null for lifetime tiers, pinned tiers, and members without a tier."
},
"leftAt": {
"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 customer left the program, or null while they are an active member. A member who left keeps their row so rejoining can restore their balances."
},
"referralCode": {
"type": "string",
"description": "The member's shareable refer-a-friend code, stable for the life of the membership."
},
"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))$",
"description": "When the customer joined the program."
},
"updatedAt": {
"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": "When the membership was last changed."
}
},
"required": [
"id",
"customerId",
"loyaltyProgramId",
"loyaltyTierId",
"tierAssignedManually",
"tierAttainedAt",
"tierReviewAt",
"leftAt",
"referralCode",
"createdAt",
"updatedAt"
],
"additionalProperties": false,
"description": "The membership the reward belongs to."
},
"rule": {
"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)$",
"description": "Unique identifier for the rule."
},
"name": {
"type": "string",
"description": "The rule's display name."
},
"section": {
"type": "string",
"enum": ["rules", "rewards", "offers", "stampCards"],
"description": "The widget section the rule sits in."
}
},
"required": ["id", "name", "section"],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "The rule that issued the reward, or null when it was issued by hand."
}
},
"required": [
"id",
"loyaltyMembershipId",
"loyaltyRuleId",
"stampCardId",
"loyaltyTransactionId",
"reward",
"status",
"code",
"redeemedAt",
"expiresAt",
"revokedAt",
"metadata",
"fulfillment",
"ruleSnapshot",
"createdAt",
"updatedAt"
],
"additionalProperties": false,
"description": "The withdrawn reward."
}GET /loyalty-rewards/{id}
Get a single reward by ID.
Requires the loyalty:read permission.
Path Parameters
idstring (uuid)requiredThe ID of the reward.
Query Parameters
includestringA comma-separated list of extra fields to compute and return. Each one costs an additional query, so ask only for what you need. Available: customer, membership, rule.
curl \
"https://api.cascade.dev/loyalty-rewards/550e8400-e29b-41d4-a716-446655440000" \
-H "Authorization: Bearer sk_YOUR_SECRET_KEY"import { client } from "@cascade-commerce/api/admin/client"
import { getLoyaltyRewardsById } from "@cascade-commerce/api/admin"
client.setConfig({
baseUrl: "https://api.cascade.dev",
headers: { Authorization: "Bearer sk_YOUR_SECRET_KEY" },
})
const { data, error } = await getLoyaltyRewardsById({
path: { id: "550e8400-e29b-41d4-a716-446655440000" },
})require "net/http"
uri = URI("https://api.cascade.dev/loyalty-rewards/550e8400-e29b-41d4-a716-446655440000")
req = Net::HTTP::Get.new(uri)
req["Authorization"] = "Bearer sk_YOUR_SECRET_KEY"
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/loyalty-rewards/550e8400-e29b-41d4-a716-446655440000",
headers={"Authorization": "Bearer sk_YOUR_SECRET_KEY"},
)
with urlopen(req) as res:
print(json.load(res)){
"id": "550e8400-e29b-41d4-a716-446655440000",
"loyaltyMembershipId": "550e8400-e29b-41d4-a716-446655440000",
"loyaltyRuleId": "550e8400-e29b-41d4-a716-446655440000",
"stampCardId": "550e8400-e29b-41d4-a716-446655440000",
"loyaltyTransactionId": "550e8400-e29b-41d4-a716-446655440000",
"reward": {
"type": "points",
"amount": 0
},
"status": "pending",
"code": "string",
"redeemedAt": "2025-01-15T09:30:00Z",
"expiresAt": "2025-01-15T09:30:00Z",
"revokedAt": "2025-01-15T09:30:00Z",
"metadata": "...",
"fulfillment": {
"mode": "shopify",
"status": "pending",
"shopifyDiscountId": "string",
"shopifyDiscountGid": "string",
"shop": "string",
"note": "string"
},
"ruleSnapshot": "...",
"createdAt": "2025-01-15T09:30:00Z",
"updatedAt": "2025-01-15T09:30:00Z",
"customer": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"email": "[email protected]",
"phone": "string",
"firstName": "string",
"lastName": "string",
"country": "string",
"birthday": "string",
"metadata": "...",
"notes": "string",
"emailOptOut": true,
"emailOptOutAt": "2025-01-15T09:30:00Z",
"emailOptOutSource": "customer",
"createdAt": "2025-01-15T09:30:00Z",
"updatedAt": "2025-01-15T09:30:00Z"
},
"membership": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"customerId": "550e8400-e29b-41d4-a716-446655440000",
"loyaltyProgramId": "550e8400-e29b-41d4-a716-446655440000",
"loyaltyTierId": "550e8400-e29b-41d4-a716-446655440000",
"tierAssignedManually": true,
"tierAttainedAt": "2025-01-15T09:30:00Z",
"tierReviewAt": "2025-01-15T09:30:00Z",
"leftAt": "2025-01-15T09:30:00Z",
"referralCode": "string",
"createdAt": "2025-01-15T09:30:00Z",
"updatedAt": "2025-01-15T09:30:00Z"
},
"rule": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "string",
"section": "rules"
}
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"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)$",
"description": "Unique identifier for the reward."
},
"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)$",
"description": "The loyalty membership the reward belongs to."
},
"loyaltyRuleId": {
"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 rule that issued the reward, or null when it was issued by hand."
},
"stampCardId": {
"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 that produced the reward, when one did."
},
"loyaltyTransactionId": {
"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 points entry the member paid with, when they claimed the reward. Null for a reward that cost nothing."
},
"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."
},
"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 the member presents at checkout. Unique across the organization."
},
"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"
}
],
"description": "When the code was used at checkout. Null while the reward is unused."
},
"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"
}
],
"description": "When the reward stops being redeemable. Null means it never expires."
},
"revokedAt": {
"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 reward was withdrawn, if it was."
},
"metadata": {
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {
"type": "string"
},
"description": "Free-form string key/value pairs for your own data. Individual keys are filterable with a dot path, for example `metadata.category`."
},
"fulfillment": {
"anyOf": [
{
"type": "object",
"properties": {
"mode": {
"type": "string",
"enum": ["shopify", "manual"],
"description": "How the code is honored. `shopify` means the connector created a real discount on the connected store; `manual` means the merchant applies the code themselves."
},
"status": {
"type": "string",
"enum": ["pending", "active", "deactivated", "failed"],
"description": "Where the Shopify discount got to. `pending` is queued, `active` is live at checkout, `deactivated` is switched off, and `failed` means Shopify refused it."
},
"shopifyDiscountId": {
"description": "The numeric id of the Shopify discount or gift card, when there is one.",
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"shopifyDiscountGid": {
"description": "The `gid://` form of the same id, which is what the Admin API takes.",
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"shop": {
"description": "The myshopify.com domain the discount lives on.",
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"note": {
"description": "Why the reward is honored by hand, when something stopped Shopify fulfilling it.",
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"required": ["mode", "status"],
"additionalProperties": false,
"description": "How a reward's code is honored at checkout."
},
{
"type": "null"
}
],
"description": "How the code is honored at checkout. Null until the reward has been through fulfillment."
},
"ruleSnapshot": {
"anyOf": [
{
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {}
},
{
"type": "null"
}
],
"description": "The rule's configuration when the reward was issued, so the reward still reads correctly after the rule is edited."
},
"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))$",
"description": "When the reward was issued."
},
"updatedAt": {
"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": "When the reward was last changed."
},
"customer": {
"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)$",
"description": "Unique identifier for the customer."
},
"email": {
"type": "string",
"format": "email",
"pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$",
"description": "The customer's email address, unique within the organization."
},
"phone": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The customer's phone number in E.164 format."
},
"firstName": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The customer's first name."
},
"lastName": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The customer's last name."
},
"country": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The customer's country, as an ISO 3166-1 code."
},
"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 customer's birthday as `MM-DD`, or null when they have not given one. Month and day only: no year is collected or stored."
},
"metadata": {
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {
"type": "string"
},
"description": "Free-form string key/value pairs for your own data. Individual keys are filterable with a dot path, for example `metadata.category`."
},
"notes": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Internal notes about the customer. Never shown to them."
},
"emailOptOut": {
"type": "boolean",
"description": "Whether the customer has opted out of marketing email. They still receive mail they asked for, such as a wishlist they shared themselves."
},
"emailOptOutAt": {
"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 opt-out was recorded. Null while the customer is still subscribed."
},
"emailOptOutSource": {
"anyOf": [
{
"type": "string",
"enum": ["customer", "merchant", "import", "shopify"],
"description": "Where the opt-out came from."
},
{
"type": "null"
}
],
"description": "Where the opt-out came from: `customer` (they opted out themselves, through an unsubscribe link or your integration), `merchant`, `import` or `shopify`. Null while the customer is still subscribed."
},
"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))$",
"description": "When the customer was first created in Cascade."
},
"updatedAt": {
"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": "When the customer was last changed."
}
},
"required": [
"id",
"email",
"phone",
"firstName",
"lastName",
"country",
"birthday",
"metadata",
"notes",
"emailOptOut",
"emailOptOutAt",
"emailOptOutSource",
"createdAt",
"updatedAt"
],
"additionalProperties": false,
"description": "The customer holding the reward, through the membership."
},
"membership": {
"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)$",
"description": "Unique identifier for the membership."
},
"customerId": {
"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)$",
"description": "The customer the membership belongs to."
},
"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)$",
"description": "The loyalty program the membership is in."
},
"loyaltyTierId": {
"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 tier the member currently sits in, if the program uses tiers."
},
"tierAssignedManually": {
"type": "boolean",
"description": "True when an admin pinned the tier. Pinned tiers are left alone by recalculation until the pin is removed."
},
"tierAttainedAt": {
"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 member reached their current tier, or null without a tier."
},
"tierReviewAt": {
"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 a rolling-window tier is next reviewed for a downgrade. Null for lifetime tiers, pinned tiers, and members without a tier."
},
"leftAt": {
"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 customer left the program, or null while they are an active member. A member who left keeps their row so rejoining can restore their balances."
},
"referralCode": {
"type": "string",
"description": "The member's shareable refer-a-friend code, stable for the life of the membership."
},
"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))$",
"description": "When the customer joined the program."
},
"updatedAt": {
"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": "When the membership was last changed."
}
},
"required": [
"id",
"customerId",
"loyaltyProgramId",
"loyaltyTierId",
"tierAssignedManually",
"tierAttainedAt",
"tierReviewAt",
"leftAt",
"referralCode",
"createdAt",
"updatedAt"
],
"additionalProperties": false,
"description": "The membership the reward belongs to."
},
"rule": {
"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)$",
"description": "Unique identifier for the rule."
},
"name": {
"type": "string",
"description": "The rule's display name."
},
"section": {
"type": "string",
"enum": ["rules", "rewards", "offers", "stampCards"],
"description": "The widget section the rule sits in."
}
},
"required": ["id", "name", "section"],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "The rule that issued the reward, or null when it was issued by hand."
}
},
"required": [
"id",
"loyaltyMembershipId",
"loyaltyRuleId",
"stampCardId",
"loyaltyTransactionId",
"reward",
"status",
"code",
"redeemedAt",
"expiresAt",
"revokedAt",
"metadata",
"fulfillment",
"ruleSnapshot",
"createdAt",
"updatedAt"
],
"additionalProperties": false,
"description": "The requested reward."
}