Loyalty

These endpoints are called from a shopper's browser, so they take your publishable key rather than a secret one. Anything specific to one customer also takes a signed email and timestamp: see Identifying the customer for how to produce the signature.

GET /widgets/loyalty

Get the store's loyalty program: its tiers, ways to earn, offers, stamp cards and rewards, plus the signed-in shopper's own state when the request is signed

Query Parameters

apiPublishableKeystringrequired

Your organization's publishable key, from Settings, API keys.

customerEmailstring (email)

The signed-in shopper's address. Leave it out for the guest catalog.

timestampstring

The Unix seconds the signature was made at. Required with customerEmail.

signaturestring

The HMAC of the email and timestamp. Required with customerEmail.

siteSlugstring

Which of the store's sites the widget is embedded on. Leave it out and the oldest site is used.

previewTokenstring

A preview token from POST /loyalty-programs/{id}/preview. It adds the one draft program it names to the response, so a merchant can check a program before publishing it. The publishable key on its own never reaches a draft.

Request
curl \
  "https://api.cascade.dev/widgets/loyalty?apiPublishableKey=pk_YOUR_PUBLISHABLE_KEY"
Response
{
  "organizationName": "string",
  "siteSlug": "string",
  "authenticated": true,
  "programs": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "string",
      "pointsNames": {
        "singular": "...",
        "plural": "..."
      },
      "paused": true,
      "draft": true,
      "sectionsEnabled": {
        "tiers": "...",
        "rules": "...",
        "offers": "...",
        "stampCards": "...",
        "rewards": "..."
      },
      "sectionOrder": ["..."],
      "tiers": ["..."],
      "rules": ["..."],
      "offers": ["..."],
      "rewards": ["..."],
      "stampCardTypes": ["..."],
      "member": {
        "loyaltyMembershipId": "...",
        "joinedAt": "...",
        "balance": "...",
        "tier": "...",
        "nextTier": "...",
        "tierEarned": "...",
        "tierRemaining": "...",
        "tierTarget": "...",
        "stampCards": "...",
        "rewards": "...",
        "referralCode": "...",
        "referralShareUrl": "...",
        "referralsPending": "...",
        "referralsCompleted": "...",
        "birthday": "..."
      }
    }
  ]
}

GET /widgets/loyalty/earnable

Get the points a purchase would earn, for a badge on a product page. Send the price and it comes back with the points every live purchase rule adds up to, with the signed-in shopper's tier multiplier applied when the request is signed. A program that would earn nothing is left out, so a store with no purchase rule gets an empty list.

Query Parameters

apiPublishableKeystringrequired

Your organization's publishable key, from Settings, API keys.

customerEmailstring (email)

The signed-in shopper's address. Leave it out for the guest catalog.

timestampstring

The Unix seconds the signature was made at. Required with customerEmail.

signaturestring

The HMAC of the email and timestamp. Required with customerEmail.

siteSlugstring

Which of the store's sites the widget is embedded on. Leave it out and the oldest site is used.

previewTokenstring

A preview token from POST /loyalty-programs/{id}/preview. It adds the one draft program it names to the response, so a merchant can check a program before publishing it. The publishable key on its own never reaches a draft.

pricenumberrequired

What the shopper would spend, in whole currency units rather than cents. For example 12.50.

currencystring

The currency the price is in, such as USD. Points are earned per whole currency unit, so this is only carried back to the widget.

Request
curl \
  "https://api.cascade.dev/widgets/loyalty/earnable?apiPublishableKey=pk_YOUR_PUBLISHABLE_KEY&price=0"
Response
{
  "siteSlug": "string",
  "authenticated": true,
  "currency": "string",
  "programs": [
    {
      "loyaltyProgramId": "550e8400-e29b-41d4-a716-446655440000",
      "programName": "string",
      "pointsNames": {
        "singular": "...",
        "plural": "..."
      },
      "points": 0,
      "pointsMultiplier": 0,
      "member": true
    }
  ]
}

POST /widgets/loyalty/join

Join a loyalty program as the signed-in shopper

Query Parameters

apiPublishableKeystringrequired

Your organization's publishable key, from Settings, API keys.

customerEmailstring (email)required

Email address of the customer the storefront signed in.

timestampstringrequired

Unix time in seconds when the signature was generated. It is accepted for 24 hours.

signaturestringrequired

Hex HMAC-SHA256 of customerEmail followed by timestamp, keyed with your widget signing secret. See the widgets guide for the recipe.

siteSlugstring

Which of the store's sites the widget is embedded on. Leave it out and the oldest site is used.

previewTokenstring

A preview token from POST /loyalty-programs/{id}/preview. It adds the one draft program it names to the response, so a merchant can check a program before publishing it. The publishable key on its own never reaches a draft.

Body Parameters

loyaltyProgramIdstring (uuid)required

The program to join.

referralCodestring

The code the shopper arrived with, so the friend who sent them gets credit.

Request
curl \
  -X POST \
  "https://api.cascade.dev/widgets/loyalty/join?apiPublishableKey=pk_YOUR_PUBLISHABLE_KEY&customerEmail=shopper%40example.com&timestamp=1735689600&signature=9f86d081..." \
  -H "Content-Type: application/json" \
  -d '{
  "loyaltyProgramId": "550e8400-e29b-41d4-a716-446655440000",
  "referralCode": "string"
}'
Response
{
  "program": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "string",
    "pointsNames": {
      "singular": "string",
      "plural": "string"
    },
    "paused": true,
    "draft": true,
    "sectionsEnabled": {
      "tiers": true,
      "rules": true,
      "offers": true,
      "stampCards": true,
      "rewards": true
    },
    "sectionOrder": ["tiers"],
    "tiers": [
      {
        "id": "...",
        "name": "...",
        "color": "...",
        "pointsMultiplier": "...",
        "thresholdAmount": "...",
        "thresholdWindow": "..."
      }
    ],
    "rules": [
      {
        "id": "...",
        "name": "...",
        "section": "...",
        "triggerType": "...",
        "reward": "...",
        "pointsPrice": "...",
        "variableAmount": "...",
        "stampCardTypeId": "...",
        "stampsRequired": "...",
        "remainingCount": "...",
        "availableTo": "...",
        "eligible": "..."
      }
    ],
    "offers": [
      {
        "id": "...",
        "name": "...",
        "section": "...",
        "triggerType": "...",
        "reward": "...",
        "pointsPrice": "...",
        "variableAmount": "...",
        "stampCardTypeId": "...",
        "stampsRequired": "...",
        "remainingCount": "...",
        "availableTo": "...",
        "eligible": "..."
      }
    ],
    "rewards": [
      {
        "id": "...",
        "name": "...",
        "section": "...",
        "triggerType": "...",
        "reward": "...",
        "pointsPrice": "...",
        "variableAmount": "...",
        "stampCardTypeId": "...",
        "stampsRequired": "...",
        "remainingCount": "...",
        "availableTo": "...",
        "eligible": "..."
      }
    ],
    "stampCardTypes": [
      {
        "id": "...",
        "name": "...",
        "target": "..."
      }
    ],
    "member": {
      "loyaltyMembershipId": "550e8400-e29b-41d4-a716-446655440000",
      "joinedAt": "2025-01-15T09:30:00Z",
      "balance": 0,
      "tier": {
        "id": "...",
        "name": "...",
        "color": "...",
        "pointsMultiplier": "..."
      },
      "nextTier": {
        "id": "...",
        "name": "...",
        "color": "..."
      },
      "tierEarned": 0,
      "tierRemaining": 0,
      "tierTarget": 0,
      "stampCards": ["..."],
      "rewards": ["..."],
      "referralCode": "string",
      "referralShareUrl": "https://example.com",
      "referralsPending": 0,
      "referralsCompleted": 0,
      "birthday": "string"
    }
  },
  "alreadyMember": true,
  "referral": "attributed"
}

POST /widgets/loyalty/claims

Spend points on a reward. The reward comes back with the code to use at checkout.

Query Parameters

apiPublishableKeystringrequired

Your organization's publishable key, from Settings, API keys.

customerEmailstring (email)required

Email address of the customer the storefront signed in.

timestampstringrequired

Unix time in seconds when the signature was generated. It is accepted for 24 hours.

signaturestringrequired

Hex HMAC-SHA256 of customerEmail followed by timestamp, keyed with your widget signing secret. See the widgets guide for the recipe.

siteSlugstring

Which of the store's sites the widget is embedded on. Leave it out and the oldest site is used.

previewTokenstring

A preview token from POST /loyalty-programs/{id}/preview. It adds the one draft program it names to the response, so a merchant can check a program before publishing it. The publishable key on its own never reaches a draft.

Body Parameters

loyaltyRuleIdstring (uuid)required

The reward to spend points on.

pointsToSpendinteger

How many points to spend. Only rewards that let the member choose accept more than one unit.

Request
curl \
  -X POST \
  "https://api.cascade.dev/widgets/loyalty/claims?apiPublishableKey=pk_YOUR_PUBLISHABLE_KEY&customerEmail=shopper%40example.com&timestamp=1735689600&signature=9f86d081..." \
  -H "Content-Type: application/json" \
  -d '{
  "loyaltyRuleId": "550e8400-e29b-41d4-a716-446655440000",
  "pointsToSpend": 0
}'
Response
{
  "reward": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "string",
    "reward": {
      "type": "points",
      "amount": 0
    },
    "status": "pending",
    "code": "string",
    "pointsSpent": 0,
    "expiresAt": "2025-01-15T09:30:00Z",
    "redeemedAt": "2025-01-15T09:30:00Z",
    "createdAt": "2025-01-15T09:30:00Z",
    "cancelable": true
  },
  "balance": 0
}

GET /widgets/loyalty/rewards

List the rewards the shopper holds, with their codes

Query Parameters

apiPublishableKeystringrequired

Your organization's publishable key, from Settings, API keys.

customerEmailstring (email)required

Email address of the customer the storefront signed in.

timestampstringrequired

Unix time in seconds when the signature was generated. It is accepted for 24 hours.

signaturestringrequired

Hex HMAC-SHA256 of customerEmail followed by timestamp, keyed with your widget signing secret. See the widgets guide for the recipe.

siteSlugstring

Which of the store's sites the widget is embedded on. Leave it out and the oldest site is used.

previewTokenstring

A preview token from POST /loyalty-programs/{id}/preview. It adds the one draft program it names to the response, so a merchant can check a program before publishing it. The publishable key on its own never reaches a draft.

loyaltyProgramIdstring (uuid)required

The program whose rewards to list.

Request
curl \
  "https://api.cascade.dev/widgets/loyalty/rewards?apiPublishableKey=pk_YOUR_PUBLISHABLE_KEY&customerEmail=shopper%40example.com&timestamp=1735689600&signature=9f86d081...&loyaltyProgramId=550e8400-e29b-41d4-a716-446655440000"
Response
{
  "rewards": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "string",
      "reward": {
        "type": "...",
        "amount": "..."
      },
      "status": "pending",
      "code": "string",
      "pointsSpent": 0,
      "expiresAt": "2025-01-15T09:30:00Z",
      "redeemedAt": "2025-01-15T09:30:00Z",
      "createdAt": "2025-01-15T09:30:00Z",
      "cancelable": true
    }
  ]
}

POST /widgets/loyalty/rewards/{id}/cancel

Cancel an unused reward and take the points it cost back

Path Parameters

idstring (uuid)required

The ID of the reward.

Query Parameters

apiPublishableKeystringrequired

Your organization's publishable key, from Settings, API keys.

customerEmailstring (email)required

Email address of the customer the storefront signed in.

timestampstringrequired

Unix time in seconds when the signature was generated. It is accepted for 24 hours.

signaturestringrequired

Hex HMAC-SHA256 of customerEmail followed by timestamp, keyed with your widget signing secret. See the widgets guide for the recipe.

siteSlugstring

Which of the store's sites the widget is embedded on. Leave it out and the oldest site is used.

previewTokenstring

A preview token from POST /loyalty-programs/{id}/preview. It adds the one draft program it names to the response, so a merchant can check a program before publishing it. The publishable key on its own never reaches a draft.

Request
curl \
  -X POST \
  "https://api.cascade.dev/widgets/loyalty/rewards/550e8400-e29b-41d4-a716-446655440000/cancel?apiPublishableKey=pk_YOUR_PUBLISHABLE_KEY&customerEmail=shopper%40example.com&timestamp=1735689600&signature=9f86d081..."
Response
{
  "reward": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "string",
    "reward": {
      "type": "points",
      "amount": 0
    },
    "status": "pending",
    "code": "string",
    "pointsSpent": 0,
    "expiresAt": "2025-01-15T09:30:00Z",
    "redeemedAt": "2025-01-15T09:30:00Z",
    "createdAt": "2025-01-15T09:30:00Z",
    "cancelable": true
  },
  "balance": 0
}

PATCH /widgets/loyalty/profile

Update the signed-in shopper's own loyalty details. The birthday is month and day only, so a birthday reward can find them without the store holding a date of birth.

Query Parameters

apiPublishableKeystringrequired

Your organization's publishable key, from Settings, API keys.

customerEmailstring (email)required

Email address of the customer the storefront signed in.

timestampstringrequired

Unix time in seconds when the signature was generated. It is accepted for 24 hours.

signaturestringrequired

Hex HMAC-SHA256 of customerEmail followed by timestamp, keyed with your widget signing secret. See the widgets guide for the recipe.

siteSlugstring

Which of the store's sites the widget is embedded on. Leave it out and the oldest site is used.

previewTokenstring

A preview token from POST /loyalty-programs/{id}/preview. It adds the one draft program it names to the response, so a merchant can check a program before publishing it. The publishable key on its own never reaches a draft.

Body Parameters

profileobjectrequired
Request
curl \
  -X PATCH \
  "https://api.cascade.dev/widgets/loyalty/profile?apiPublishableKey=pk_YOUR_PUBLISHABLE_KEY&customerEmail=shopper%40example.com&timestamp=1735689600&signature=9f86d081..." \
  -H "Content-Type: application/json" \
  -d '{
  "profile": {
    "birthday": "string"
  }
}'
Response
{
  "profile": {
    "birthday": "string"
  }
}

POST /widgets/loyalty/events

Record a storefront event for the shopper. The loyalty widget sends a visit on load; send it yourself if you built your own storefront.

Query Parameters

apiPublishableKeystringrequired

Your organization's publishable key, from Settings, API keys.

customerEmailstring (email)required

Email address of the customer the storefront signed in.

timestampstringrequired

Unix time in seconds when the signature was generated. It is accepted for 24 hours.

signaturestringrequired

Hex HMAC-SHA256 of customerEmail followed by timestamp, keyed with your widget signing secret. See the widgets guide for the recipe.

siteSlugstring

Which of the store's sites the widget is embedded on. Leave it out and the oldest site is used.

previewTokenstring

A preview token from POST /loyalty-programs/{id}/preview. It adds the one draft program it names to the response, so a merchant can check a program before publishing it. The publishable key on its own never reaches a draft.

Body Parameters

eventobjectrequired
Request
curl \
  -X POST \
  "https://api.cascade.dev/widgets/loyalty/events?apiPublishableKey=pk_YOUR_PUBLISHABLE_KEY&customerEmail=shopper%40example.com&timestamp=1735689600&signature=9f86d081..." \
  -H "Content-Type: application/json" \
  -d '{
  "event": {
    "type": "visit",
    "dedupeKey": "string",
    "loyaltyProgramId": "550e8400-e29b-41d4-a716-446655440000"
  }
}'
Response
{
  "accepted": true
}