#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. 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[]requiredThe members to enroll or reconcile.
strictbooleanWhen 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.
curl \
-X POST \
"https://your-store.meisei.app/api/loyalty-memberships/import" \
-H "Authorization: Bearer sk_YOUR_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"data": [
{
"email": "user@example.com",
"program": "string",
"tier": "string",
"points": 0,
"customerMetadata": "..."
}
],
"strict": true
}'import { client } from "@meisei/api/admin/client"
import { postLoyaltyMembershipsImport } from "@meisei/api/admin"
client.setConfig({
baseUrl: "https://your-store.meisei.app/api",
headers: { Authorization: "Bearer sk_YOUR_SECRET_KEY" },
})
const { data, error } = await postLoyaltyMembershipsImport({
body: {
data: [
{
email: "user@example.com",
program: "string",
tier: "string",
points: 0,
customerMetadata: "...",
},
],
strict: true,
},
})require "meisei"
meisei = Meisei::Client.new(
secret_key: "sk_YOUR_SECRET_KEY",
base_url: "https://your-store.meisei.app/api",
)
result = meisei.loyalty_memberships.import(
data: [
{
email: "user@example.com",
program: "string",
tier: "string",
points: 0,
customer_metadata: "...",
},
],
strict: true,
)from meisei import Client
meisei = Client(
secret_key="sk_YOUR_SECRET_KEY",
base_url="https://your-store.meisei.app/api",
)
result = meisei.loyalty_memberships.import_(
data=[
{
"email": "user@example.com",
"program": "string",
"tier": "string",
"points": 0,
"customer_metadata": {},
},
],
strict=True,
){
"imported": [
{
"index": 0,
"loyaltyMembershipId": "A4Ws1igz"
}
],
"skipped": [
{
"index": 0,
"message": "string"
}
]
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"imported": {
"type": "array",
"items": {
"type": "object",
"properties": {
"index": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "Position of the row in the batch you sent."
},
"loyaltyMembershipId": {
"type": "string",
"description": "The membership that was created or updated.",
"idCodec": "sqid"
}
},
"required": ["index", "loyaltyMembershipId"],
"additionalProperties": false
},
"description": "The rows that landed."
},
"skipped": {
"type": "array",
"items": {
"type": "object",
"properties": {
"index": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "Position of the row in the batch you sent."
},
"message": {
"type": "string",
"description": "Why the row could not be imported."
}
},
"required": ["index", "message"],
"additionalProperties": false
},
"description": "The rows that could not be placed, and why."
}
},
"required": ["imported", "skipped"],
"additionalProperties": false
}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
limitnumberThe number of records to return.
cursorstringA pagination cursor. Fetch the next page by passing the nextCursor value from the previous response.
sortcreatedAt | -createdAt | leftAt | -leftAtThe sort order. Prefix a field with - to sort descending. Defaults to -createdAt.
searchstringA free-text search term. Every word has to match, from the start of a word, in any order and ignoring case and accents, 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, 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, tags.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: importJob, customer, tier, pointsBalance, tierProgress, earnedPoints.
curl \
"https://your-store.meisei.app/api/loyalty-memberships" \
-H "Authorization: Bearer sk_YOUR_SECRET_KEY"import { client } from "@meisei/api/admin/client"
import { getLoyaltyMemberships } from "@meisei/api/admin"
client.setConfig({
baseUrl: "https://your-store.meisei.app/api",
headers: { Authorization: "Bearer sk_YOUR_SECRET_KEY" },
})
const { data, error } = await getLoyaltyMemberships()require "meisei"
meisei = Meisei::Client.new(
secret_key: "sk_YOUR_SECRET_KEY",
base_url: "https://your-store.meisei.app/api",
)
page = meisei.loyalty_memberships.listfrom meisei import Client
meisei = Client(
secret_key="sk_YOUR_SECRET_KEY",
base_url="https://your-store.meisei.app/api",
)
page = meisei.loyalty_memberships.list(){
"data": [
{
"id": "A4Ws1igz",
"customerId": "A4Ws1igz",
"loyaltyProgramId": "A4Ws1igz",
"loyaltyTierId": "A4Ws1igz",
"tierAssignedManually": true,
"tierAttainedAt": "2025-01-15T09:30:00Z",
"leftAt": "2025-01-15T09:30:00Z",
"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": "...",
"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": "..."
},
"earnedPoints": {
"lifetime": "...",
"rolling12Months": "..."
}
}
],
"nextCursor": "string"
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"data": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Unique identifier for the membership.",
"idCodec": "sqid"
},
"customerId": {
"type": "string",
"description": "The customer the membership belongs to.",
"idCodec": "sqid"
},
"loyaltyProgramId": {
"type": "string",
"description": "The loyalty program the membership is in.",
"idCodec": "sqid"
},
"loyaltyTierId": {
"anyOf": [
{
"type": "string",
"description": "The tier the member currently sits in, if the program uses tiers.",
"idCodec": "sqid"
},
{
"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. The loyalty tiers automation leaves a pinned tier alone 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."
},
"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."
},
"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."
},
"importJob": {
"anyOf": [
{
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "The import job that wrote the record.",
"idCodec": "sqid"
},
"type": {
"type": "string",
"enum": [
"customers",
"products",
"orders",
"wishlists",
"reviews",
"loyalty-members"
],
"description": "What that import's file contained."
},
"action": {
"type": "string",
"enum": ["created", "updated"],
"description": "Whether the import created this record or updated one that was already there."
},
"row": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "The 1-based row in the import's CSV that became this record, where it is known."
},
"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 that import was started."
}
},
"required": ["id", "type", "action", "row", "createdAt"],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "The import that last wrote this record, or null when no import did."
},
"customer": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Unique identifier for the customer.",
"idCodec": "sqid"
},
"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."
},
"metadata": {
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {
"anyOf": [
{
"type": "string"
},
{
"type": "number"
},
{
"type": "boolean"
},
{
"type": "array",
"items": {
"type": "string"
}
}
],
"description": "A metadata value: a string, a number, true or false, or a list of strings. A key with a metadata definition holds the type its definition names."
},
"description": "Key/value pairs for your own data. Keys you define with `/metadata-definitions` are typed and validated; any other key is free-form. 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 Meisei."
},
"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",
"metadata",
"notes",
"emailOptOut",
"emailOptOutAt",
"emailOptOutSource",
"createdAt",
"updatedAt"
],
"additionalProperties": false,
"description": "The customer the membership belongs to."
},
"tier": {
"anyOf": [
{
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Unique identifier for the tier.",
"idCodec": "sqid"
},
"name": {
"type": "string",
"description": "The tier's display name, shown to members."
},
"loyaltyProgramId": {
"type": "string",
"description": "The loyalty program the tier belongs to.",
"idCodec": "sqid"
},
"threshold": {
"anyOf": [
{
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "earnedPoints",
"description": "The tier is attained by earning points."
},
"amount": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991,
"description": "How many points have to be earned within the window to attain the tier."
},
"window": {
"type": "string",
"enum": ["lifetime", "rolling12Months"],
"description": "The period earning is summed over. `lifetime` counts everything the member ever earned; `rolling12Months` counts the last 12 months."
}
},
"required": ["type", "amount", "window"],
"additionalProperties": false
}
],
"description": "What a member has to do to attain the tier."
},
{
"type": "null"
}
],
"description": "What a member has to earn to attain the tier, or null for a tier that is only assigned by hand. The loyalty tier placement automation, installed with the first tier that has a threshold, moves members up as soon as they qualify and moves a member down from a rolling-window tier once they have held it for a year and stopped qualifying."
},
"pointsMultiplier": {
"type": "number",
"description": "Multiplies points issued by rules for members of this tier, for example 2 for double points. It never applies to manual adjustments, imports, or non-points rewards."
},
"color": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "A CSS color used to badge the tier, or null for the default look."
},
"metadata": {
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {
"anyOf": [
{
"type": "string"
},
{
"type": "number"
},
{
"type": "boolean"
},
{
"type": "array",
"items": {
"type": "string"
}
}
],
"description": "A metadata value: a string, a number, true or false, or a list of strings. A key with a metadata definition holds the type its definition names."
},
"description": "Key/value pairs for your own data. Keys you define with `/metadata-definitions` are typed and validated; any other key is free-form. Individual keys are filterable with a dot path, for example `metadata.category`."
},
"archivedAt": {
"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 tier was archived, or null if it is still in use. Archived tiers take no new members but keep the ones they have."
},
"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 tier was created."
},
"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 tier was last changed."
}
},
"required": [
"id",
"name",
"loyaltyProgramId",
"threshold",
"pointsMultiplier",
"color",
"metadata",
"archivedAt",
"createdAt",
"updatedAt"
],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "The tier the member currently sits in, or null when the program has not placed them in one."
},
"pointsBalance": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "The member's current points balance, or zero when they hold none."
},
"tierProgress": {
"anyOf": [
{
"type": "object",
"properties": {
"currentTier": {
"anyOf": [
{
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Unique identifier for the tier.",
"idCodec": "sqid"
},
"name": {
"type": "string",
"description": "The tier's display name."
},
"color": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The tier's badge color, if one is set."
},
"pointsMultiplier": {
"type": "number",
"description": "The multiplier the tier applies to rule earnings."
}
},
"required": ["id", "name", "color", "pointsMultiplier"],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "The tier the member sits in, or null when they have none."
},
"nextTier": {
"anyOf": [
{
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Unique identifier for the tier.",
"idCodec": "sqid"
},
"name": {
"type": "string",
"description": "The tier's display name."
},
"color": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The tier's badge color, if one is set."
},
"threshold": {
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "earnedPoints",
"description": "The tier is attained by earning points."
},
"amount": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991,
"description": "How many points have to be earned within the window to attain the tier."
},
"window": {
"type": "string",
"enum": ["lifetime", "rolling12Months"],
"description": "The period earning is summed over. `lifetime` counts everything the member ever earned; `rolling12Months` counts the last 12 months."
}
},
"required": ["type", "amount", "window"],
"additionalProperties": false
}
],
"description": "What the member has to earn to reach it."
}
},
"required": ["id", "name", "color", "threshold"],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "The next tier up, or null when the member is at the top."
},
"earned": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "What the member has earned toward the next tier, within its window. Null when there is no next tier."
},
"remaining": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "How much more earning the next tier takes. Null when there is no next tier."
}
},
"required": ["currentTier", "nextTier", "earned", "remaining"],
"additionalProperties": false,
"description": "Where the member stands in the program's tier ladder."
},
{
"type": "null"
}
],
"description": "Where the member stands in the program's tier ladder."
},
"earnedPoints": {
"anyOf": [
{
"type": "object",
"properties": {
"lifetime": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "Points earned since the member joined."
},
"rolling12Months": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "Points earned in the last 12 months."
}
},
"required": ["lifetime", "rolling12Months"],
"additionalProperties": false,
"description": "Points the member has earned, the measure tier thresholds use: earns minus their revocations, never reduced by spending or expiry."
},
{
"type": "null"
}
],
"description": "Points the member has earned, the measure tier thresholds use: earns minus their revocations, never reduced by spending or expiry."
}
},
"required": [
"id",
"customerId",
"loyaltyProgramId",
"loyaltyTierId",
"tierAssignedManually",
"tierAttainedAt",
"leftAt",
"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-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
customerIdstringrequiredThe customer to enroll.
loyaltyProgramIdstringrequiredThe loyalty program to enroll them in.
freshStartbooleanOnly matters for a customer who previously left the program. When true, their earlier balances, rewards, and tier are cleared so the membership starts over; otherwise rejoining restores them.
curl \
-X POST \
"https://your-store.meisei.app/api/loyalty-memberships" \
-H "Authorization: Bearer sk_YOUR_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"customerId": "string",
"loyaltyProgramId": "string",
"freshStart": true
}'import { client } from "@meisei/api/admin/client"
import { postLoyaltyMemberships } from "@meisei/api/admin"
client.setConfig({
baseUrl: "https://your-store.meisei.app/api",
headers: { Authorization: "Bearer sk_YOUR_SECRET_KEY" },
})
const { data, error } = await postLoyaltyMemberships({
body: {
customerId: "string",
loyaltyProgramId: "string",
freshStart: true,
},
})require "meisei"
meisei = Meisei::Client.new(
secret_key: "sk_YOUR_SECRET_KEY",
base_url: "https://your-store.meisei.app/api",
)
result = meisei.loyalty_memberships.create(
customer_id: "string",
loyalty_program_id: "string",
fresh_start: true,
)from meisei import Client
meisei = Client(
secret_key="sk_YOUR_SECRET_KEY",
base_url="https://your-store.meisei.app/api",
)
result = meisei.loyalty_memberships.create(
customer_id="string",
loyalty_program_id="string",
fresh_start=True,
){
"id": "A4Ws1igz",
"customerId": "A4Ws1igz",
"loyaltyProgramId": "A4Ws1igz",
"loyaltyTierId": "A4Ws1igz",
"tierAssignedManually": true,
"tierAttainedAt": "2025-01-15T09:30:00Z",
"leftAt": "2025-01-15T09:30:00Z",
"createdAt": "2025-01-15T09:30:00Z",
"updatedAt": "2025-01-15T09:30:00Z",
"importJob": {
"id": "A4Ws1igz",
"type": "customers",
"action": "created",
"row": 0,
"createdAt": "2025-01-15T09:30:00Z"
},
"customer": {
"id": "A4Ws1igz",
"email": "user@example.com",
"phone": "string",
"firstName": "string",
"lastName": "string",
"country": "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": "A4Ws1igz",
"name": "string",
"loyaltyProgramId": "A4Ws1igz",
"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": "A4Ws1igz",
"name": "string",
"color": "string",
"pointsMultiplier": 0
},
"nextTier": {
"id": "A4Ws1igz",
"name": "string",
"color": "string",
"threshold": {
"type": "...",
"amount": "...",
"window": "..."
}
},
"earned": 0,
"remaining": 0
},
"earnedPoints": {
"lifetime": 0,
"rolling12Months": 0
}
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Unique identifier for the membership.",
"idCodec": "sqid"
},
"customerId": {
"type": "string",
"description": "The customer the membership belongs to.",
"idCodec": "sqid"
},
"loyaltyProgramId": {
"type": "string",
"description": "The loyalty program the membership is in.",
"idCodec": "sqid"
},
"loyaltyTierId": {
"anyOf": [
{
"type": "string",
"description": "The tier the member currently sits in, if the program uses tiers.",
"idCodec": "sqid"
},
{
"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. The loyalty tiers automation leaves a pinned tier alone 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."
},
"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."
},
"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."
},
"importJob": {
"anyOf": [
{
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "The import job that wrote the record.",
"idCodec": "sqid"
},
"type": {
"type": "string",
"enum": [
"customers",
"products",
"orders",
"wishlists",
"reviews",
"loyalty-members"
],
"description": "What that import's file contained."
},
"action": {
"type": "string",
"enum": ["created", "updated"],
"description": "Whether the import created this record or updated one that was already there."
},
"row": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "The 1-based row in the import's CSV that became this record, where it is known."
},
"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 that import was started."
}
},
"required": ["id", "type", "action", "row", "createdAt"],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "The import that last wrote this record, or null when no import did."
},
"customer": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Unique identifier for the customer.",
"idCodec": "sqid"
},
"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."
},
"metadata": {
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {
"anyOf": [
{
"type": "string"
},
{
"type": "number"
},
{
"type": "boolean"
},
{
"type": "array",
"items": {
"type": "string"
}
}
],
"description": "A metadata value: a string, a number, true or false, or a list of strings. A key with a metadata definition holds the type its definition names."
},
"description": "Key/value pairs for your own data. Keys you define with `/metadata-definitions` are typed and validated; any other key is free-form. 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 Meisei."
},
"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",
"metadata",
"notes",
"emailOptOut",
"emailOptOutAt",
"emailOptOutSource",
"createdAt",
"updatedAt"
],
"additionalProperties": false,
"description": "The customer the membership belongs to."
},
"tier": {
"anyOf": [
{
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Unique identifier for the tier.",
"idCodec": "sqid"
},
"name": {
"type": "string",
"description": "The tier's display name, shown to members."
},
"loyaltyProgramId": {
"type": "string",
"description": "The loyalty program the tier belongs to.",
"idCodec": "sqid"
},
"threshold": {
"anyOf": [
{
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "earnedPoints",
"description": "The tier is attained by earning points."
},
"amount": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991,
"description": "How many points have to be earned within the window to attain the tier."
},
"window": {
"type": "string",
"enum": ["lifetime", "rolling12Months"],
"description": "The period earning is summed over. `lifetime` counts everything the member ever earned; `rolling12Months` counts the last 12 months."
}
},
"required": ["type", "amount", "window"],
"additionalProperties": false
}
],
"description": "What a member has to do to attain the tier."
},
{
"type": "null"
}
],
"description": "What a member has to earn to attain the tier, or null for a tier that is only assigned by hand. The loyalty tier placement automation, installed with the first tier that has a threshold, moves members up as soon as they qualify and moves a member down from a rolling-window tier once they have held it for a year and stopped qualifying."
},
"pointsMultiplier": {
"type": "number",
"description": "Multiplies points issued by rules for members of this tier, for example 2 for double points. It never applies to manual adjustments, imports, or non-points rewards."
},
"color": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "A CSS color used to badge the tier, or null for the default look."
},
"metadata": {
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {
"anyOf": [
{
"type": "string"
},
{
"type": "number"
},
{
"type": "boolean"
},
{
"type": "array",
"items": {
"type": "string"
}
}
],
"description": "A metadata value: a string, a number, true or false, or a list of strings. A key with a metadata definition holds the type its definition names."
},
"description": "Key/value pairs for your own data. Keys you define with `/metadata-definitions` are typed and validated; any other key is free-form. Individual keys are filterable with a dot path, for example `metadata.category`."
},
"archivedAt": {
"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 tier was archived, or null if it is still in use. Archived tiers take no new members but keep the ones they have."
},
"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 tier was created."
},
"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 tier was last changed."
}
},
"required": [
"id",
"name",
"loyaltyProgramId",
"threshold",
"pointsMultiplier",
"color",
"metadata",
"archivedAt",
"createdAt",
"updatedAt"
],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "The tier the member currently sits in, or null when the program has not placed them in one."
},
"pointsBalance": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "The member's current points balance, or zero when they hold none."
},
"tierProgress": {
"anyOf": [
{
"type": "object",
"properties": {
"currentTier": {
"anyOf": [
{
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Unique identifier for the tier.",
"idCodec": "sqid"
},
"name": {
"type": "string",
"description": "The tier's display name."
},
"color": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The tier's badge color, if one is set."
},
"pointsMultiplier": {
"type": "number",
"description": "The multiplier the tier applies to rule earnings."
}
},
"required": ["id", "name", "color", "pointsMultiplier"],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "The tier the member sits in, or null when they have none."
},
"nextTier": {
"anyOf": [
{
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Unique identifier for the tier.",
"idCodec": "sqid"
},
"name": {
"type": "string",
"description": "The tier's display name."
},
"color": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The tier's badge color, if one is set."
},
"threshold": {
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "earnedPoints",
"description": "The tier is attained by earning points."
},
"amount": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991,
"description": "How many points have to be earned within the window to attain the tier."
},
"window": {
"type": "string",
"enum": ["lifetime", "rolling12Months"],
"description": "The period earning is summed over. `lifetime` counts everything the member ever earned; `rolling12Months` counts the last 12 months."
}
},
"required": ["type", "amount", "window"],
"additionalProperties": false
}
],
"description": "What the member has to earn to reach it."
}
},
"required": ["id", "name", "color", "threshold"],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "The next tier up, or null when the member is at the top."
},
"earned": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "What the member has earned toward the next tier, within its window. Null when there is no next tier."
},
"remaining": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "How much more earning the next tier takes. Null when there is no next tier."
}
},
"required": ["currentTier", "nextTier", "earned", "remaining"],
"additionalProperties": false,
"description": "Where the member stands in the program's tier ladder."
},
{
"type": "null"
}
],
"description": "Where the member stands in the program's tier ladder."
},
"earnedPoints": {
"anyOf": [
{
"type": "object",
"properties": {
"lifetime": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "Points earned since the member joined."
},
"rolling12Months": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "Points earned in the last 12 months."
}
},
"required": ["lifetime", "rolling12Months"],
"additionalProperties": false,
"description": "Points the member has earned, the measure tier thresholds use: earns minus their revocations, never reduced by spending or expiry."
},
{
"type": "null"
}
],
"description": "Points the member has earned, the measure tier thresholds use: earns minus their revocations, never reduced by spending or expiry."
}
},
"required": [
"id",
"customerId",
"loyaltyProgramId",
"loyaltyTierId",
"tierAssignedManually",
"tierAttainedAt",
"leftAt",
"createdAt",
"updatedAt"
],
"additionalProperties": false,
"description": "The new or restored membership."
}GET /loyalty-memberships/{id}
Get a single loyalty membership by ID.
Requires the loyalty:read permission.
Path parameters
idstringrequiredThe ID of the membership.
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: importJob, customer, tier, pointsBalance, tierProgress, earnedPoints.
curl \
"https://your-store.meisei.app/api/loyalty-memberships/A4Ws1igz" \
-H "Authorization: Bearer sk_YOUR_SECRET_KEY"import { client } from "@meisei/api/admin/client"
import { getLoyaltyMembershipsById } from "@meisei/api/admin"
client.setConfig({
baseUrl: "https://your-store.meisei.app/api",
headers: { Authorization: "Bearer sk_YOUR_SECRET_KEY" },
})
const { data, error } = await getLoyaltyMembershipsById({
path: { id: "A4Ws1igz" },
})require "meisei"
meisei = Meisei::Client.new(
secret_key: "sk_YOUR_SECRET_KEY",
base_url: "https://your-store.meisei.app/api",
)
result = meisei.loyalty_memberships.retrieve("A4Ws1igz")from meisei import Client
meisei = Client(
secret_key="sk_YOUR_SECRET_KEY",
base_url="https://your-store.meisei.app/api",
)
result = meisei.loyalty_memberships.retrieve("A4Ws1igz"){
"id": "A4Ws1igz",
"customerId": "A4Ws1igz",
"loyaltyProgramId": "A4Ws1igz",
"loyaltyTierId": "A4Ws1igz",
"tierAssignedManually": true,
"tierAttainedAt": "2025-01-15T09:30:00Z",
"leftAt": "2025-01-15T09:30:00Z",
"createdAt": "2025-01-15T09:30:00Z",
"updatedAt": "2025-01-15T09:30:00Z",
"importJob": {
"id": "A4Ws1igz",
"type": "customers",
"action": "created",
"row": 0,
"createdAt": "2025-01-15T09:30:00Z"
},
"customer": {
"id": "A4Ws1igz",
"email": "user@example.com",
"phone": "string",
"firstName": "string",
"lastName": "string",
"country": "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": "A4Ws1igz",
"name": "string",
"loyaltyProgramId": "A4Ws1igz",
"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": "A4Ws1igz",
"name": "string",
"color": "string",
"pointsMultiplier": 0
},
"nextTier": {
"id": "A4Ws1igz",
"name": "string",
"color": "string",
"threshold": {
"type": "...",
"amount": "...",
"window": "..."
}
},
"earned": 0,
"remaining": 0
},
"earnedPoints": {
"lifetime": 0,
"rolling12Months": 0
}
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Unique identifier for the membership.",
"idCodec": "sqid"
},
"customerId": {
"type": "string",
"description": "The customer the membership belongs to.",
"idCodec": "sqid"
},
"loyaltyProgramId": {
"type": "string",
"description": "The loyalty program the membership is in.",
"idCodec": "sqid"
},
"loyaltyTierId": {
"anyOf": [
{
"type": "string",
"description": "The tier the member currently sits in, if the program uses tiers.",
"idCodec": "sqid"
},
{
"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. The loyalty tiers automation leaves a pinned tier alone 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."
},
"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."
},
"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."
},
"importJob": {
"anyOf": [
{
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "The import job that wrote the record.",
"idCodec": "sqid"
},
"type": {
"type": "string",
"enum": [
"customers",
"products",
"orders",
"wishlists",
"reviews",
"loyalty-members"
],
"description": "What that import's file contained."
},
"action": {
"type": "string",
"enum": ["created", "updated"],
"description": "Whether the import created this record or updated one that was already there."
},
"row": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "The 1-based row in the import's CSV that became this record, where it is known."
},
"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 that import was started."
}
},
"required": ["id", "type", "action", "row", "createdAt"],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "The import that last wrote this record, or null when no import did."
},
"customer": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Unique identifier for the customer.",
"idCodec": "sqid"
},
"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."
},
"metadata": {
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {
"anyOf": [
{
"type": "string"
},
{
"type": "number"
},
{
"type": "boolean"
},
{
"type": "array",
"items": {
"type": "string"
}
}
],
"description": "A metadata value: a string, a number, true or false, or a list of strings. A key with a metadata definition holds the type its definition names."
},
"description": "Key/value pairs for your own data. Keys you define with `/metadata-definitions` are typed and validated; any other key is free-form. 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 Meisei."
},
"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",
"metadata",
"notes",
"emailOptOut",
"emailOptOutAt",
"emailOptOutSource",
"createdAt",
"updatedAt"
],
"additionalProperties": false,
"description": "The customer the membership belongs to."
},
"tier": {
"anyOf": [
{
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Unique identifier for the tier.",
"idCodec": "sqid"
},
"name": {
"type": "string",
"description": "The tier's display name, shown to members."
},
"loyaltyProgramId": {
"type": "string",
"description": "The loyalty program the tier belongs to.",
"idCodec": "sqid"
},
"threshold": {
"anyOf": [
{
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "earnedPoints",
"description": "The tier is attained by earning points."
},
"amount": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991,
"description": "How many points have to be earned within the window to attain the tier."
},
"window": {
"type": "string",
"enum": ["lifetime", "rolling12Months"],
"description": "The period earning is summed over. `lifetime` counts everything the member ever earned; `rolling12Months` counts the last 12 months."
}
},
"required": ["type", "amount", "window"],
"additionalProperties": false
}
],
"description": "What a member has to do to attain the tier."
},
{
"type": "null"
}
],
"description": "What a member has to earn to attain the tier, or null for a tier that is only assigned by hand. The loyalty tier placement automation, installed with the first tier that has a threshold, moves members up as soon as they qualify and moves a member down from a rolling-window tier once they have held it for a year and stopped qualifying."
},
"pointsMultiplier": {
"type": "number",
"description": "Multiplies points issued by rules for members of this tier, for example 2 for double points. It never applies to manual adjustments, imports, or non-points rewards."
},
"color": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "A CSS color used to badge the tier, or null for the default look."
},
"metadata": {
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {
"anyOf": [
{
"type": "string"
},
{
"type": "number"
},
{
"type": "boolean"
},
{
"type": "array",
"items": {
"type": "string"
}
}
],
"description": "A metadata value: a string, a number, true or false, or a list of strings. A key with a metadata definition holds the type its definition names."
},
"description": "Key/value pairs for your own data. Keys you define with `/metadata-definitions` are typed and validated; any other key is free-form. Individual keys are filterable with a dot path, for example `metadata.category`."
},
"archivedAt": {
"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 tier was archived, or null if it is still in use. Archived tiers take no new members but keep the ones they have."
},
"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 tier was created."
},
"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 tier was last changed."
}
},
"required": [
"id",
"name",
"loyaltyProgramId",
"threshold",
"pointsMultiplier",
"color",
"metadata",
"archivedAt",
"createdAt",
"updatedAt"
],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "The tier the member currently sits in, or null when the program has not placed them in one."
},
"pointsBalance": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "The member's current points balance, or zero when they hold none."
},
"tierProgress": {
"anyOf": [
{
"type": "object",
"properties": {
"currentTier": {
"anyOf": [
{
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Unique identifier for the tier.",
"idCodec": "sqid"
},
"name": {
"type": "string",
"description": "The tier's display name."
},
"color": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The tier's badge color, if one is set."
},
"pointsMultiplier": {
"type": "number",
"description": "The multiplier the tier applies to rule earnings."
}
},
"required": ["id", "name", "color", "pointsMultiplier"],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "The tier the member sits in, or null when they have none."
},
"nextTier": {
"anyOf": [
{
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Unique identifier for the tier.",
"idCodec": "sqid"
},
"name": {
"type": "string",
"description": "The tier's display name."
},
"color": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The tier's badge color, if one is set."
},
"threshold": {
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "earnedPoints",
"description": "The tier is attained by earning points."
},
"amount": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991,
"description": "How many points have to be earned within the window to attain the tier."
},
"window": {
"type": "string",
"enum": ["lifetime", "rolling12Months"],
"description": "The period earning is summed over. `lifetime` counts everything the member ever earned; `rolling12Months` counts the last 12 months."
}
},
"required": ["type", "amount", "window"],
"additionalProperties": false
}
],
"description": "What the member has to earn to reach it."
}
},
"required": ["id", "name", "color", "threshold"],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "The next tier up, or null when the member is at the top."
},
"earned": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "What the member has earned toward the next tier, within its window. Null when there is no next tier."
},
"remaining": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "How much more earning the next tier takes. Null when there is no next tier."
}
},
"required": ["currentTier", "nextTier", "earned", "remaining"],
"additionalProperties": false,
"description": "Where the member stands in the program's tier ladder."
},
{
"type": "null"
}
],
"description": "Where the member stands in the program's tier ladder."
},
"earnedPoints": {
"anyOf": [
{
"type": "object",
"properties": {
"lifetime": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "Points earned since the member joined."
},
"rolling12Months": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "Points earned in the last 12 months."
}
},
"required": ["lifetime", "rolling12Months"],
"additionalProperties": false,
"description": "Points the member has earned, the measure tier thresholds use: earns minus their revocations, never reduced by spending or expiry."
},
{
"type": "null"
}
],
"description": "Points the member has earned, the measure tier thresholds use: earns minus their revocations, never reduced by spending or expiry."
}
},
"required": [
"id",
"customerId",
"loyaltyProgramId",
"loyaltyTierId",
"tierAssignedManually",
"tierAttainedAt",
"leftAt",
"createdAt",
"updatedAt"
],
"additionalProperties": false,
"description": "The requested membership."
}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.updated webhook, and loyaltyMembership.tierChanged as well when the tier changes.
Requires the loyalty:write permission.
Path parameters
idstringrequiredThe ID of the membership to update.
Body parameters
loyaltyTierIdstring | nullThe tier to place the member in, or null to clear it. Setting a tier pins it so the loyalty tiers automation leaves the member where you put them, unless tierAssignedManually is passed as false.
tierAssignedManuallybooleanWhether the member's tier is pinned. Set false to hand the member back to the loyalty tiers automation, which places them on its next pass.
curl \
-X PUT \
"https://your-store.meisei.app/api/loyalty-memberships/A4Ws1igz" \
-H "Authorization: Bearer sk_YOUR_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"loyaltyTierId": "string",
"tierAssignedManually": true
}'import { client } from "@meisei/api/admin/client"
import { putLoyaltyMembershipsById } from "@meisei/api/admin"
client.setConfig({
baseUrl: "https://your-store.meisei.app/api",
headers: { Authorization: "Bearer sk_YOUR_SECRET_KEY" },
})
const { data, error } = await putLoyaltyMembershipsById({
path: { id: "A4Ws1igz" },
body: {
loyaltyTierId: "string",
tierAssignedManually: true,
},
})require "meisei"
meisei = Meisei::Client.new(
secret_key: "sk_YOUR_SECRET_KEY",
base_url: "https://your-store.meisei.app/api",
)
result = meisei.loyalty_memberships.update(
"A4Ws1igz",
loyalty_tier_id: "string",
tier_assigned_manually: true,
)from meisei import Client
meisei = Client(
secret_key="sk_YOUR_SECRET_KEY",
base_url="https://your-store.meisei.app/api",
)
result = meisei.loyalty_memberships.update(
"A4Ws1igz",
loyalty_tier_id="string",
tier_assigned_manually=True,
){
"id": "A4Ws1igz",
"customerId": "A4Ws1igz",
"loyaltyProgramId": "A4Ws1igz",
"loyaltyTierId": "A4Ws1igz",
"tierAssignedManually": true,
"tierAttainedAt": "2025-01-15T09:30:00Z",
"leftAt": "2025-01-15T09:30:00Z",
"createdAt": "2025-01-15T09:30:00Z",
"updatedAt": "2025-01-15T09:30:00Z",
"importJob": {
"id": "A4Ws1igz",
"type": "customers",
"action": "created",
"row": 0,
"createdAt": "2025-01-15T09:30:00Z"
},
"customer": {
"id": "A4Ws1igz",
"email": "user@example.com",
"phone": "string",
"firstName": "string",
"lastName": "string",
"country": "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": "A4Ws1igz",
"name": "string",
"loyaltyProgramId": "A4Ws1igz",
"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": "A4Ws1igz",
"name": "string",
"color": "string",
"pointsMultiplier": 0
},
"nextTier": {
"id": "A4Ws1igz",
"name": "string",
"color": "string",
"threshold": {
"type": "...",
"amount": "...",
"window": "..."
}
},
"earned": 0,
"remaining": 0
},
"earnedPoints": {
"lifetime": 0,
"rolling12Months": 0
}
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Unique identifier for the membership.",
"idCodec": "sqid"
},
"customerId": {
"type": "string",
"description": "The customer the membership belongs to.",
"idCodec": "sqid"
},
"loyaltyProgramId": {
"type": "string",
"description": "The loyalty program the membership is in.",
"idCodec": "sqid"
},
"loyaltyTierId": {
"anyOf": [
{
"type": "string",
"description": "The tier the member currently sits in, if the program uses tiers.",
"idCodec": "sqid"
},
{
"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. The loyalty tiers automation leaves a pinned tier alone 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."
},
"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."
},
"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."
},
"importJob": {
"anyOf": [
{
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "The import job that wrote the record.",
"idCodec": "sqid"
},
"type": {
"type": "string",
"enum": [
"customers",
"products",
"orders",
"wishlists",
"reviews",
"loyalty-members"
],
"description": "What that import's file contained."
},
"action": {
"type": "string",
"enum": ["created", "updated"],
"description": "Whether the import created this record or updated one that was already there."
},
"row": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "The 1-based row in the import's CSV that became this record, where it is known."
},
"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 that import was started."
}
},
"required": ["id", "type", "action", "row", "createdAt"],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "The import that last wrote this record, or null when no import did."
},
"customer": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Unique identifier for the customer.",
"idCodec": "sqid"
},
"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."
},
"metadata": {
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {
"anyOf": [
{
"type": "string"
},
{
"type": "number"
},
{
"type": "boolean"
},
{
"type": "array",
"items": {
"type": "string"
}
}
],
"description": "A metadata value: a string, a number, true or false, or a list of strings. A key with a metadata definition holds the type its definition names."
},
"description": "Key/value pairs for your own data. Keys you define with `/metadata-definitions` are typed and validated; any other key is free-form. 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 Meisei."
},
"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",
"metadata",
"notes",
"emailOptOut",
"emailOptOutAt",
"emailOptOutSource",
"createdAt",
"updatedAt"
],
"additionalProperties": false,
"description": "The customer the membership belongs to."
},
"tier": {
"anyOf": [
{
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Unique identifier for the tier.",
"idCodec": "sqid"
},
"name": {
"type": "string",
"description": "The tier's display name, shown to members."
},
"loyaltyProgramId": {
"type": "string",
"description": "The loyalty program the tier belongs to.",
"idCodec": "sqid"
},
"threshold": {
"anyOf": [
{
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "earnedPoints",
"description": "The tier is attained by earning points."
},
"amount": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991,
"description": "How many points have to be earned within the window to attain the tier."
},
"window": {
"type": "string",
"enum": ["lifetime", "rolling12Months"],
"description": "The period earning is summed over. `lifetime` counts everything the member ever earned; `rolling12Months` counts the last 12 months."
}
},
"required": ["type", "amount", "window"],
"additionalProperties": false
}
],
"description": "What a member has to do to attain the tier."
},
{
"type": "null"
}
],
"description": "What a member has to earn to attain the tier, or null for a tier that is only assigned by hand. The loyalty tier placement automation, installed with the first tier that has a threshold, moves members up as soon as they qualify and moves a member down from a rolling-window tier once they have held it for a year and stopped qualifying."
},
"pointsMultiplier": {
"type": "number",
"description": "Multiplies points issued by rules for members of this tier, for example 2 for double points. It never applies to manual adjustments, imports, or non-points rewards."
},
"color": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "A CSS color used to badge the tier, or null for the default look."
},
"metadata": {
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {
"anyOf": [
{
"type": "string"
},
{
"type": "number"
},
{
"type": "boolean"
},
{
"type": "array",
"items": {
"type": "string"
}
}
],
"description": "A metadata value: a string, a number, true or false, or a list of strings. A key with a metadata definition holds the type its definition names."
},
"description": "Key/value pairs for your own data. Keys you define with `/metadata-definitions` are typed and validated; any other key is free-form. Individual keys are filterable with a dot path, for example `metadata.category`."
},
"archivedAt": {
"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 tier was archived, or null if it is still in use. Archived tiers take no new members but keep the ones they have."
},
"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 tier was created."
},
"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 tier was last changed."
}
},
"required": [
"id",
"name",
"loyaltyProgramId",
"threshold",
"pointsMultiplier",
"color",
"metadata",
"archivedAt",
"createdAt",
"updatedAt"
],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "The tier the member currently sits in, or null when the program has not placed them in one."
},
"pointsBalance": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "The member's current points balance, or zero when they hold none."
},
"tierProgress": {
"anyOf": [
{
"type": "object",
"properties": {
"currentTier": {
"anyOf": [
{
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Unique identifier for the tier.",
"idCodec": "sqid"
},
"name": {
"type": "string",
"description": "The tier's display name."
},
"color": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The tier's badge color, if one is set."
},
"pointsMultiplier": {
"type": "number",
"description": "The multiplier the tier applies to rule earnings."
}
},
"required": ["id", "name", "color", "pointsMultiplier"],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "The tier the member sits in, or null when they have none."
},
"nextTier": {
"anyOf": [
{
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Unique identifier for the tier.",
"idCodec": "sqid"
},
"name": {
"type": "string",
"description": "The tier's display name."
},
"color": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The tier's badge color, if one is set."
},
"threshold": {
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "earnedPoints",
"description": "The tier is attained by earning points."
},
"amount": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991,
"description": "How many points have to be earned within the window to attain the tier."
},
"window": {
"type": "string",
"enum": ["lifetime", "rolling12Months"],
"description": "The period earning is summed over. `lifetime` counts everything the member ever earned; `rolling12Months` counts the last 12 months."
}
},
"required": ["type", "amount", "window"],
"additionalProperties": false
}
],
"description": "What the member has to earn to reach it."
}
},
"required": ["id", "name", "color", "threshold"],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "The next tier up, or null when the member is at the top."
},
"earned": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "What the member has earned toward the next tier, within its window. Null when there is no next tier."
},
"remaining": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "How much more earning the next tier takes. Null when there is no next tier."
}
},
"required": ["currentTier", "nextTier", "earned", "remaining"],
"additionalProperties": false,
"description": "Where the member stands in the program's tier ladder."
},
{
"type": "null"
}
],
"description": "Where the member stands in the program's tier ladder."
},
"earnedPoints": {
"anyOf": [
{
"type": "object",
"properties": {
"lifetime": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "Points earned since the member joined."
},
"rolling12Months": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "Points earned in the last 12 months."
}
},
"required": ["lifetime", "rolling12Months"],
"additionalProperties": false,
"description": "Points the member has earned, the measure tier thresholds use: earns minus their revocations, never reduced by spending or expiry."
},
{
"type": "null"
}
],
"description": "Points the member has earned, the measure tier thresholds use: earns minus their revocations, never reduced by spending or expiry."
}
},
"required": [
"id",
"customerId",
"loyaltyProgramId",
"loyaltyTierId",
"tierAssignedManually",
"tierAttainedAt",
"leftAt",
"createdAt",
"updatedAt"
],
"additionalProperties": false,
"description": "The updated membership."
}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
idstringrequiredThe ID of the membership to remove.
curl \
-X DELETE \
"https://your-store.meisei.app/api/loyalty-memberships/A4Ws1igz" \
-H "Authorization: Bearer sk_YOUR_SECRET_KEY"import { client } from "@meisei/api/admin/client"
import { deleteLoyaltyMembershipsById } from "@meisei/api/admin"
client.setConfig({
baseUrl: "https://your-store.meisei.app/api",
headers: { Authorization: "Bearer sk_YOUR_SECRET_KEY" },
})
const { data, error } = await deleteLoyaltyMembershipsById({
path: { id: "A4Ws1igz" },
})require "meisei"
meisei = Meisei::Client.new(
secret_key: "sk_YOUR_SECRET_KEY",
base_url: "https://your-store.meisei.app/api",
)
result = meisei.loyalty_memberships.delete("A4Ws1igz")from meisei import Client
meisei = Client(
secret_key="sk_YOUR_SECRET_KEY",
base_url="https://your-store.meisei.app/api",
)
result = meisei.loyalty_memberships.delete("A4Ws1igz"){
"id": "A4Ws1igz",
"customerId": "A4Ws1igz",
"loyaltyProgramId": "A4Ws1igz",
"loyaltyTierId": "A4Ws1igz",
"tierAssignedManually": true,
"tierAttainedAt": "2025-01-15T09:30:00Z",
"leftAt": "2025-01-15T09:30:00Z",
"createdAt": "2025-01-15T09:30:00Z",
"updatedAt": "2025-01-15T09:30:00Z",
"importJob": {
"id": "A4Ws1igz",
"type": "customers",
"action": "created",
"row": 0,
"createdAt": "2025-01-15T09:30:00Z"
},
"customer": {
"id": "A4Ws1igz",
"email": "user@example.com",
"phone": "string",
"firstName": "string",
"lastName": "string",
"country": "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": "A4Ws1igz",
"name": "string",
"loyaltyProgramId": "A4Ws1igz",
"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": "A4Ws1igz",
"name": "string",
"color": "string",
"pointsMultiplier": 0
},
"nextTier": {
"id": "A4Ws1igz",
"name": "string",
"color": "string",
"threshold": {
"type": "...",
"amount": "...",
"window": "..."
}
},
"earned": 0,
"remaining": 0
},
"earnedPoints": {
"lifetime": 0,
"rolling12Months": 0
}
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Unique identifier for the membership.",
"idCodec": "sqid"
},
"customerId": {
"type": "string",
"description": "The customer the membership belongs to.",
"idCodec": "sqid"
},
"loyaltyProgramId": {
"type": "string",
"description": "The loyalty program the membership is in.",
"idCodec": "sqid"
},
"loyaltyTierId": {
"anyOf": [
{
"type": "string",
"description": "The tier the member currently sits in, if the program uses tiers.",
"idCodec": "sqid"
},
{
"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. The loyalty tiers automation leaves a pinned tier alone 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."
},
"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."
},
"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."
},
"importJob": {
"anyOf": [
{
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "The import job that wrote the record.",
"idCodec": "sqid"
},
"type": {
"type": "string",
"enum": [
"customers",
"products",
"orders",
"wishlists",
"reviews",
"loyalty-members"
],
"description": "What that import's file contained."
},
"action": {
"type": "string",
"enum": ["created", "updated"],
"description": "Whether the import created this record or updated one that was already there."
},
"row": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "The 1-based row in the import's CSV that became this record, where it is known."
},
"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 that import was started."
}
},
"required": ["id", "type", "action", "row", "createdAt"],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "The import that last wrote this record, or null when no import did."
},
"customer": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Unique identifier for the customer.",
"idCodec": "sqid"
},
"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."
},
"metadata": {
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {
"anyOf": [
{
"type": "string"
},
{
"type": "number"
},
{
"type": "boolean"
},
{
"type": "array",
"items": {
"type": "string"
}
}
],
"description": "A metadata value: a string, a number, true or false, or a list of strings. A key with a metadata definition holds the type its definition names."
},
"description": "Key/value pairs for your own data. Keys you define with `/metadata-definitions` are typed and validated; any other key is free-form. 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 Meisei."
},
"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",
"metadata",
"notes",
"emailOptOut",
"emailOptOutAt",
"emailOptOutSource",
"createdAt",
"updatedAt"
],
"additionalProperties": false,
"description": "The customer the membership belongs to."
},
"tier": {
"anyOf": [
{
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Unique identifier for the tier.",
"idCodec": "sqid"
},
"name": {
"type": "string",
"description": "The tier's display name, shown to members."
},
"loyaltyProgramId": {
"type": "string",
"description": "The loyalty program the tier belongs to.",
"idCodec": "sqid"
},
"threshold": {
"anyOf": [
{
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "earnedPoints",
"description": "The tier is attained by earning points."
},
"amount": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991,
"description": "How many points have to be earned within the window to attain the tier."
},
"window": {
"type": "string",
"enum": ["lifetime", "rolling12Months"],
"description": "The period earning is summed over. `lifetime` counts everything the member ever earned; `rolling12Months` counts the last 12 months."
}
},
"required": ["type", "amount", "window"],
"additionalProperties": false
}
],
"description": "What a member has to do to attain the tier."
},
{
"type": "null"
}
],
"description": "What a member has to earn to attain the tier, or null for a tier that is only assigned by hand. The loyalty tier placement automation, installed with the first tier that has a threshold, moves members up as soon as they qualify and moves a member down from a rolling-window tier once they have held it for a year and stopped qualifying."
},
"pointsMultiplier": {
"type": "number",
"description": "Multiplies points issued by rules for members of this tier, for example 2 for double points. It never applies to manual adjustments, imports, or non-points rewards."
},
"color": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "A CSS color used to badge the tier, or null for the default look."
},
"metadata": {
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {
"anyOf": [
{
"type": "string"
},
{
"type": "number"
},
{
"type": "boolean"
},
{
"type": "array",
"items": {
"type": "string"
}
}
],
"description": "A metadata value: a string, a number, true or false, or a list of strings. A key with a metadata definition holds the type its definition names."
},
"description": "Key/value pairs for your own data. Keys you define with `/metadata-definitions` are typed and validated; any other key is free-form. Individual keys are filterable with a dot path, for example `metadata.category`."
},
"archivedAt": {
"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 tier was archived, or null if it is still in use. Archived tiers take no new members but keep the ones they have."
},
"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 tier was created."
},
"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 tier was last changed."
}
},
"required": [
"id",
"name",
"loyaltyProgramId",
"threshold",
"pointsMultiplier",
"color",
"metadata",
"archivedAt",
"createdAt",
"updatedAt"
],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "The tier the member currently sits in, or null when the program has not placed them in one."
},
"pointsBalance": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "The member's current points balance, or zero when they hold none."
},
"tierProgress": {
"anyOf": [
{
"type": "object",
"properties": {
"currentTier": {
"anyOf": [
{
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Unique identifier for the tier.",
"idCodec": "sqid"
},
"name": {
"type": "string",
"description": "The tier's display name."
},
"color": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The tier's badge color, if one is set."
},
"pointsMultiplier": {
"type": "number",
"description": "The multiplier the tier applies to rule earnings."
}
},
"required": ["id", "name", "color", "pointsMultiplier"],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "The tier the member sits in, or null when they have none."
},
"nextTier": {
"anyOf": [
{
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Unique identifier for the tier.",
"idCodec": "sqid"
},
"name": {
"type": "string",
"description": "The tier's display name."
},
"color": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The tier's badge color, if one is set."
},
"threshold": {
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "earnedPoints",
"description": "The tier is attained by earning points."
},
"amount": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991,
"description": "How many points have to be earned within the window to attain the tier."
},
"window": {
"type": "string",
"enum": ["lifetime", "rolling12Months"],
"description": "The period earning is summed over. `lifetime` counts everything the member ever earned; `rolling12Months` counts the last 12 months."
}
},
"required": ["type", "amount", "window"],
"additionalProperties": false
}
],
"description": "What the member has to earn to reach it."
}
},
"required": ["id", "name", "color", "threshold"],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "The next tier up, or null when the member is at the top."
},
"earned": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "What the member has earned toward the next tier, within its window. Null when there is no next tier."
},
"remaining": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "How much more earning the next tier takes. Null when there is no next tier."
}
},
"required": ["currentTier", "nextTier", "earned", "remaining"],
"additionalProperties": false,
"description": "Where the member stands in the program's tier ladder."
},
{
"type": "null"
}
],
"description": "Where the member stands in the program's tier ladder."
},
"earnedPoints": {
"anyOf": [
{
"type": "object",
"properties": {
"lifetime": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "Points earned since the member joined."
},
"rolling12Months": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "Points earned in the last 12 months."
}
},
"required": ["lifetime", "rolling12Months"],
"additionalProperties": false,
"description": "Points the member has earned, the measure tier thresholds use: earns minus their revocations, never reduced by spending or expiry."
},
{
"type": "null"
}
],
"description": "Points the member has earned, the measure tier thresholds use: earns minus their revocations, never reduced by spending or expiry."
}
},
"required": [
"id",
"customerId",
"loyaltyProgramId",
"loyaltyTierId",
"tierAssignedManually",
"tierAttainedAt",
"leftAt",
"createdAt",
"updatedAt"
],
"additionalProperties": false,
"description": "The membership that was left."
}