Loyalty memberships

POST /loyalty-memberships/import

Bring loyalty members in from another platform in bulk, matching the customer by email and the program, tier, and currencies by name. Each member is enrolled, placed in their tier without pinning it, and each balance is written as a single opening-adjustment transaction so history starts clean. Importing a member again reconciles their balances to the new values instead of stacking a second opening. Rows whose references don't resolve are skipped and returned in skipped rather than failing the whole batch; set strict to fail on the first such row instead. Stamp card balances are not imported. To import a CSV instead, upload it and queue an import job with POST /import-jobs.

Requires the loyalty:write permission.

Costs 10 rate limit tokens instead of the usual one. See Rate limits.

Body Parameters

dataobject[]required

The members to enroll or reconcile.

strictboolean

When true, a row that references something missing fails the whole import with an error. Off by default: rows whose references don't resolve are skipped and reported in skipped.

Request
curl \
  -X POST \
  "https://api.cascade.dev/loyalty-memberships/import" \
  -H "Authorization: Bearer sk_YOUR_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "data": [
    {
      "email": "[email protected]",
      "program": "string",
      "tier": "string",
      "points": 0
    }
  ],
  "strict": true
}'
Response
{
  "imported": [
    {
      "index": 0,
      "loyaltyMembershipId": "550e8400-e29b-41d4-a716-446655440000"
    }
  ],
  "skipped": [
    {
      "index": 0,
      "message": "string"
    }
  ]
}

GET /loyalty-memberships

List loyalty program memberships, newest first. Members who left are included; an active membership has a null leftAt. Supports search, filtering and cursor pagination.

Requires the loyalty:read permission.

Query Parameters

limitnumber

The number of records to return.

cursorstring

A pagination cursor. Fetch the next page by passing the nextCursor value from the previous response.

sortcreatedAt | -createdAt | leftAt | -leftAt

The sort order. Prefix a field with - to sort descending. Defaults to -createdAt.

searchstring

A free-text search term, matched case-insensitively against the record's most identifying fields. Combined with filter using AND.

filterstring

A JSON-encoded filter document, for example {"createdAt":{"$gte":"2025-01-01T00:00:00Z"}}. Filterable fields: id, loyaltyProgramId, loyaltyTierId, tierAssignedManually, leftAt, createdAt, customer.id, customer.email, customer.firstName, customer.lastName, customer.metadata, customer.emailOptOut, customer.emailOptOutSource, customer.createdAt, tier.id, tier.name. See Filtering for the syntax.

includestring

A comma-separated list of extra fields to compute and return. Each one costs an additional query, so ask only for what you need. Available: importJob, customer, tier, pointsBalance, tierProgress.

Request
curl \
  "https://api.cascade.dev/loyalty-memberships" \
  -H "Authorization: Bearer sk_YOUR_SECRET_KEY"
Response
{
  "data": [
    {
      "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",
      "importJob": {
        "id": "...",
        "type": "...",
        "action": "...",
        "row": "...",
        "createdAt": "..."
      },
      "customer": {
        "id": "...",
        "email": "...",
        "phone": "...",
        "firstName": "...",
        "lastName": "...",
        "country": "...",
        "birthday": "...",
        "metadata": "...",
        "notes": "...",
        "emailOptOut": "...",
        "emailOptOutAt": "...",
        "emailOptOutSource": "...",
        "createdAt": "...",
        "updatedAt": "..."
      },
      "tier": {
        "id": "...",
        "name": "...",
        "loyaltyProgramId": "...",
        "threshold": "...",
        "pointsMultiplier": "...",
        "color": "...",
        "metadata": "...",
        "archivedAt": "...",
        "createdAt": "...",
        "updatedAt": "..."
      },
      "pointsBalance": 0,
      "tierProgress": {
        "currentTier": "...",
        "nextTier": "...",
        "earned": "...",
        "remaining": "..."
      }
    }
  ],
  "nextCursor": "string"
}

POST /loyalty-memberships

Enroll a customer in a loyalty program. A customer who previously left is restored with their balances intact unless freshStart is set. Enrolling an active member fails with a conflict. Triggers the loyaltyMembership.created webhook.

Requires the loyalty:write permission.

Body Parameters

dataobjectrequired

The enrollment to create.

Request
curl \
  -X POST \
  "https://api.cascade.dev/loyalty-memberships" \
  -H "Authorization: Bearer sk_YOUR_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "data": {
    "customerId": "550e8400-e29b-41d4-a716-446655440000",
    "loyaltyProgramId": "550e8400-e29b-41d4-a716-446655440000",
    "freshStart": true
  }
}'
Response
{
  "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",
  "importJob": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "type": "customers",
    "action": "created",
    "row": 0,
    "createdAt": "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"
  },
  "tier": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "string",
    "loyaltyProgramId": "550e8400-e29b-41d4-a716-446655440000",
    "threshold": {
      "type": "earnedPoints",
      "amount": 0,
      "window": "lifetime"
    },
    "pointsMultiplier": 0,
    "color": "string",
    "metadata": "...",
    "archivedAt": "2025-01-15T09:30:00Z",
    "createdAt": "2025-01-15T09:30:00Z",
    "updatedAt": "2025-01-15T09:30:00Z"
  },
  "pointsBalance": 0,
  "tierProgress": {
    "currentTier": {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "string",
      "color": "string",
      "pointsMultiplier": 0
    },
    "nextTier": {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "string",
      "color": "string",
      "threshold": {
        "type": "...",
        "amount": "...",
        "window": "..."
      }
    },
    "earned": 0,
    "remaining": 0
  }
}

GET /loyalty-memberships/{id}

Get a single loyalty membership by ID.

Requires the loyalty:read permission.

Path Parameters

idstring (uuid)required

The ID of the membership.

Query Parameters

includestring

A comma-separated list of extra fields to compute and return. Each one costs an additional query, so ask only for what you need. Available: importJob, customer, tier, pointsBalance, tierProgress.

Request
curl \
  "https://api.cascade.dev/loyalty-memberships/550e8400-e29b-41d4-a716-446655440000" \
  -H "Authorization: Bearer sk_YOUR_SECRET_KEY"
Response
{
  "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",
  "importJob": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "type": "customers",
    "action": "created",
    "row": 0,
    "createdAt": "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"
  },
  "tier": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "string",
    "loyaltyProgramId": "550e8400-e29b-41d4-a716-446655440000",
    "threshold": {
      "type": "earnedPoints",
      "amount": 0,
      "window": "lifetime"
    },
    "pointsMultiplier": 0,
    "color": "string",
    "metadata": "...",
    "archivedAt": "2025-01-15T09:30:00Z",
    "createdAt": "2025-01-15T09:30:00Z",
    "updatedAt": "2025-01-15T09:30:00Z"
  },
  "pointsBalance": 0,
  "tierProgress": {
    "currentTier": {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "string",
      "color": "string",
      "pointsMultiplier": 0
    },
    "nextTier": {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "string",
      "color": "string",
      "threshold": {
        "type": "...",
        "amount": "...",
        "window": "..."
      }
    },
    "earned": 0,
    "remaining": 0
  }
}

PUT /loyalty-memberships/{id}

Assign or pin a member's tier. Setting loyaltyTierId pins the member to that tier so recalculation leaves them there; passing tierAssignedManually: false unpins them and recalculates their tier right away. Triggers the loyaltyMembership.tierChanged webhook when the tier changes.

Requires the loyalty:write permission.

Path Parameters

idstring (uuid)required

The ID of the membership to update.

Body Parameters

dataobjectrequired

The tier assignment to apply.

Request
curl \
  -X PUT \
  "https://api.cascade.dev/loyalty-memberships/550e8400-e29b-41d4-a716-446655440000" \
  -H "Authorization: Bearer sk_YOUR_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "data": {
    "loyaltyTierId": "550e8400-e29b-41d4-a716-446655440000",
    "tierAssignedManually": true
  }
}'
Response
{
  "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",
  "importJob": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "type": "customers",
    "action": "created",
    "row": 0,
    "createdAt": "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"
  },
  "tier": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "string",
    "loyaltyProgramId": "550e8400-e29b-41d4-a716-446655440000",
    "threshold": {
      "type": "earnedPoints",
      "amount": 0,
      "window": "lifetime"
    },
    "pointsMultiplier": 0,
    "color": "string",
    "metadata": "...",
    "archivedAt": "2025-01-15T09:30:00Z",
    "createdAt": "2025-01-15T09:30:00Z",
    "updatedAt": "2025-01-15T09:30:00Z"
  },
  "pointsBalance": 0,
  "tierProgress": {
    "currentTier": {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "string",
      "color": "string",
      "pointsMultiplier": 0
    },
    "nextTier": {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "string",
      "color": "string",
      "threshold": {
        "type": "...",
        "amount": "...",
        "window": "..."
      }
    },
    "earned": 0,
    "remaining": 0
  }
}

DELETE /loyalty-memberships/{id}

Remove a member from their loyalty program. The membership is kept and flagged as left so re-enrolling can restore their balances; leaving twice returns a 404. Triggers the loyaltyMembership.deleted webhook.

Requires the loyalty:write permission.

Path Parameters

idstring (uuid)required

The ID of the membership to remove.

Request
curl \
  -X DELETE \
  "https://api.cascade.dev/loyalty-memberships/550e8400-e29b-41d4-a716-446655440000" \
  -H "Authorization: Bearer sk_YOUR_SECRET_KEY"
Response
{
  "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",
  "importJob": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "type": "customers",
    "action": "created",
    "row": 0,
    "createdAt": "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"
  },
  "tier": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "string",
    "loyaltyProgramId": "550e8400-e29b-41d4-a716-446655440000",
    "threshold": {
      "type": "earnedPoints",
      "amount": 0,
      "window": "lifetime"
    },
    "pointsMultiplier": 0,
    "color": "string",
    "metadata": "...",
    "archivedAt": "2025-01-15T09:30:00Z",
    "createdAt": "2025-01-15T09:30:00Z",
    "updatedAt": "2025-01-15T09:30:00Z"
  },
  "pointsBalance": 0,
  "tierProgress": {
    "currentTier": {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "string",
      "color": "string",
      "pointsMultiplier": 0
    },
    "nextTier": {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "string",
      "color": "string",
      "threshold": {
        "type": "...",
        "amount": "...",
        "window": "..."
      }
    },
    "earned": 0,
    "remaining": 0
  }
}