#Customers
GET /customers
List the organization's customers, newest first by default. Supports search, filtering and cursor pagination.
Requires the customers: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.
sortemail | -email | createdAt | -createdAt | stringThe sort order. Prefix a field with - to sort descending. Defaults to -createdAt. A custom field sorts as metadata.<key>, records without a value last; GET /metadata-definitions lists the fields.
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, email, firstName, lastName, metadata, emailOptOut, emailOptOutSource, createdAt, tags.name, externalIds.externalId, orders.createdAt, orders.totalCents. A custom field's metadata.<key> path takes the operators its type supports: $gt, $gte, $lt and $lte for numbers and dates, $includes for text and pick-any fields, and $exists for any key. GET /metadata-definitions lists the fields. 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, externalId, externalIds, ordersCount, reviewsCount, wishlistsCount, loyaltyPointsBalance, tags.
curl \
"https://your-store.meisei.app/api/customers" \
-H "Authorization: Bearer sk_YOUR_SECRET_KEY"import { client } from "@meisei/api/admin/client"
import { getCustomers } from "@meisei/api/admin"
client.setConfig({
baseUrl: "https://your-store.meisei.app/api",
headers: { Authorization: "Bearer sk_YOUR_SECRET_KEY" },
})
const { data, error } = await getCustomers()require "meisei"
meisei = Meisei::Client.new(
secret_key: "sk_YOUR_SECRET_KEY",
base_url: "https://your-store.meisei.app/api",
)
page = meisei.customers.listfrom meisei import Client
meisei = Client(
secret_key="sk_YOUR_SECRET_KEY",
base_url="https://your-store.meisei.app/api",
)
page = meisei.customers.list(){
"data": [
{
"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",
"importJob": {
"id": "...",
"type": "...",
"action": "...",
"row": "...",
"createdAt": "..."
},
"externalId": "string",
"externalIds": ["..."],
"ordersCount": 0,
"reviewsCount": 0,
"wishlistsCount": 0,
"loyaltyPointsBalance": 0,
"tags": ["..."]
}
],
"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 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."
},
"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."
},
"externalId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The record's ID when it has exactly one, as it does when you have one site. Null when it has none, or one on each of several sites (see `externalIds`)."
},
"externalIds": {
"type": "array",
"items": {
"type": "object",
"properties": {
"site": {
"type": "string",
"description": "The slug of the site the ID belongs to."
},
"id": {
"type": "string",
"description": "The record's ID on that site, such as a Shopify product ID."
}
},
"required": ["site", "id"],
"additionalProperties": false
},
"description": "The record's IDs on the sites it is linked to, one per site. A Shopify store's IDs are its numeric IDs, not `gid://` ones."
},
"ordersCount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How many orders the customer has placed."
},
"reviewsCount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How many reviews the customer has written."
},
"wishlistsCount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How many wishlists the customer has."
},
"loyaltyPointsBalance": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "The customer's current loyalty points balance, or zero when they hold none."
},
"tags": {
"type": "array",
"items": {
"type": "string"
},
"description": "Names of the tags applied to the customer."
}
},
"required": [
"id",
"email",
"phone",
"firstName",
"lastName",
"country",
"metadata",
"notes",
"emailOptOut",
"emailOptOutAt",
"emailOptOutSource",
"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
}PATCH /customers/bulk-update
Add or remove tags and metadata, or set the marketing email opt-out, across every customer matching a filter, or an exact list of ids, in one request. Sending neither applies the changes to every customer in the organization. At least one change is required. Each customer that actually changes triggers the customer.updated webhook.
Requires the customers:write permission.
Costs 5 rate limit tokens instead of the usual one. See Rate limits.
Query parameters
filterstringA JSON-encoded filter document selecting the customers to update. Omit it and every customer in the organization is updated. See Filtering for the syntax.
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.
Body parameters
idsstring[]The ids of the customers to update, for changing an exact list. Combined with filter and search using AND when those are sent too.
addTagsstring[]Tag names to add. Tags that do not exist yet are created.
removeTagsstring[]Tag names to remove.
addMetadataobjectMetadata keys to set, merged onto whatever each customer already has.
removeMetadataKeysstring[]Metadata keys to delete.
emailOptOutbooleanSet to true to stop sending every matching customer marketing email, false to opt them all back in. Omit to leave it alone.
emailOptOutSourcecustomer | merchant | import | shopifyWhere the opt-out came from. Defaults to merchant.
curl \
-X PATCH \
"https://your-store.meisei.app/api/customers/bulk-update" \
-H "Authorization: Bearer sk_YOUR_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"ids": [
"string"
],
"addTags": [
"string"
],
"removeTags": [
"string"
],
"addMetadata": "...",
"removeMetadataKeys": [
"string"
],
"emailOptOut": true,
"emailOptOutSource": "customer"
}'import { client } from "@meisei/api/admin/client"
import { patchCustomersBulkUpdate } from "@meisei/api/admin"
client.setConfig({
baseUrl: "https://your-store.meisei.app/api",
headers: { Authorization: "Bearer sk_YOUR_SECRET_KEY" },
})
const { data, error } = await patchCustomersBulkUpdate({
body: {
ids: ["string"],
addTags: ["string"],
removeTags: ["string"],
addMetadata: "...",
removeMetadataKeys: ["string"],
emailOptOut: true,
emailOptOutSource: "customer",
},
})require "meisei"
meisei = Meisei::Client.new(
secret_key: "sk_YOUR_SECRET_KEY",
base_url: "https://your-store.meisei.app/api",
)
result = meisei.customers.bulk_update(
ids: ["string"],
add_tags: ["string"],
remove_tags: ["string"],
add_metadata: "...",
remove_metadata_keys: ["string"],
email_opt_out: true,
email_opt_out_source: "customer",
)from meisei import Client
meisei = Client(
secret_key="sk_YOUR_SECRET_KEY",
base_url="https://your-store.meisei.app/api",
)
result = meisei.customers.bulk_update(
ids=["string"],
add_tags=["string"],
remove_tags=["string"],
add_metadata={},
remove_metadata_keys=["string"],
email_opt_out=True,
email_opt_out_source="customer",
){
"count": 0
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"count": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How many customers the filter matched."
}
},
"required": ["count"],
"additionalProperties": false
}POST /customers/import
Create or update customers in bulk, matching on externalIds first and then on email. A customer matched by an external ID takes the row's email when no other customer has it. Every column you send overwrites what is already stored, so send complete records rather than partial ones. Rows naming a site that doesn't exist are skipped and returned in skipped; set strict to fail on the first such row instead. Each new customer triggers the customer.created webhook and each existing one customer.updated.
Requires the customers:write permission.
Costs 10 rate limit tokens instead of the usual one. See Rate limits.
Body parameters
dataobject[]requiredThe customers to create or update.
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/customers/import" \
-H "Authorization: Bearer sk_YOUR_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"data": [
{
"email": "user@example.com",
"phone": "string",
"firstName": "string",
"lastName": "string",
"metadata": "...",
"tags": [
"..."
],
"emailOptOut": true,
"emailOptOutSource": "customer",
"externalId": "string",
"externalIds": [
"..."
]
}
],
"strict": true
}'import { client } from "@meisei/api/admin/client"
import { postCustomersImport } from "@meisei/api/admin"
client.setConfig({
baseUrl: "https://your-store.meisei.app/api",
headers: { Authorization: "Bearer sk_YOUR_SECRET_KEY" },
})
const { data, error } = await postCustomersImport({
body: {
data: [
{
email: "user@example.com",
phone: "string",
firstName: "string",
lastName: "string",
metadata: "...",
tags: ["..."],
emailOptOut: true,
emailOptOutSource: "customer",
externalId: "string",
externalIds: ["..."],
},
],
strict: true,
},
})require "meisei"
meisei = Meisei::Client.new(
secret_key: "sk_YOUR_SECRET_KEY",
base_url: "https://your-store.meisei.app/api",
)
result = meisei.customers.import(
data: [
{
email: "user@example.com",
phone: "string",
first_name: "string",
last_name: "string",
metadata: "...",
tags: ["..."],
email_opt_out: true,
email_opt_out_source: "customer",
external_id: "string",
external_ids: ["..."],
},
],
strict: true,
)from meisei import Client
meisei = Client(
secret_key="sk_YOUR_SECRET_KEY",
base_url="https://your-store.meisei.app/api",
)
result = meisei.customers.import_(
data=[
{
"email": "user@example.com",
"phone": "string",
"first_name": "string",
"last_name": "string",
"metadata": {},
"tags": ["..."],
"email_opt_out": True,
"email_opt_out_source": "customer",
"external_id": "string",
"external_ids": [],
},
],
strict=True,
){
"data": [
{
"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",
"importJob": {
"id": "...",
"type": "...",
"action": "...",
"row": "...",
"createdAt": "..."
},
"externalId": "string",
"externalIds": ["..."],
"ordersCount": 0,
"reviewsCount": 0,
"wishlistsCount": 0,
"loyaltyPointsBalance": 0,
"tags": ["..."]
}
],
"skipped": [
{
"index": 0,
"message": "string"
}
],
"notices": [
{
"index": 0,
"message": "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 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."
},
"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."
},
"externalId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The record's ID when it has exactly one, as it does when you have one site. Null when it has none, or one on each of several sites (see `externalIds`)."
},
"externalIds": {
"type": "array",
"items": {
"type": "object",
"properties": {
"site": {
"type": "string",
"description": "The slug of the site the ID belongs to."
},
"id": {
"type": "string",
"description": "The record's ID on that site, such as a Shopify product ID."
}
},
"required": ["site", "id"],
"additionalProperties": false
},
"description": "The record's IDs on the sites it is linked to, one per site. A Shopify store's IDs are its numeric IDs, not `gid://` ones."
},
"ordersCount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How many orders the customer has placed."
},
"reviewsCount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How many reviews the customer has written."
},
"wishlistsCount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How many wishlists the customer has."
},
"loyaltyPointsBalance": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "The customer's current loyalty points balance, or zero when they hold none."
},
"tags": {
"type": "array",
"items": {
"type": "string"
},
"description": "Names of the tags applied to the customer."
}
},
"required": [
"id",
"email",
"phone",
"firstName",
"lastName",
"country",
"metadata",
"notes",
"emailOptOut",
"emailOptOutAt",
"emailOptOutSource",
"createdAt",
"updatedAt"
],
"additionalProperties": false
},
"description": "The imported customers, as they now stand."
},
"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 imported, and why."
},
"notices": {
"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": "Rows that were imported with part of them left as it was, and why. A row with `emailOptOut: false` never opts a customer back in once they have opted out."
}
},
"required": ["data", "skipped", "notices"],
"additionalProperties": false
}GET /customers/{id}
Get a single customer. The customer may be named by Meisei's ID or by its ID on one of your sites, such as a Shopify ID: Meisei's is tried first, then every site. Send by to say which kind it is, and site when the same external ID is on more than one of your sites (otherwise that answers 400).
Requires the customers:read permission.
Path parameters
idstringrequiredThe customer's ID, or its ID on one of your sites. URL-encode a gid:// one.
Query parameters
byid | externalIdWhat the ID in the path is: id for Meisei's own ID, externalId for its ID on one of your sites (such as a Shopify ID, in either form). Leave it out to try Meisei's ID first and then every site.
sitestringThe slug of the site an external ID belongs to. Only needed when the same ID is on more than one of your sites.
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, externalId, externalIds, ordersCount, reviewsCount, wishlistsCount, loyaltyPointsBalance, tags.
curl \
"https://your-store.meisei.app/api/customers/A4Ws1igz" \
-H "Authorization: Bearer sk_YOUR_SECRET_KEY"import { client } from "@meisei/api/admin/client"
import { getCustomersById } from "@meisei/api/admin"
client.setConfig({
baseUrl: "https://your-store.meisei.app/api",
headers: { Authorization: "Bearer sk_YOUR_SECRET_KEY" },
})
const { data, error } = await getCustomersById({
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.customers.retrieve("A4Ws1igz")from meisei import Client
meisei = Client(
secret_key="sk_YOUR_SECRET_KEY",
base_url="https://your-store.meisei.app/api",
)
result = meisei.customers.retrieve("A4Ws1igz"){
"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",
"importJob": {
"id": "A4Ws1igz",
"type": "customers",
"action": "created",
"row": 0,
"createdAt": "2025-01-15T09:30:00Z"
},
"externalId": "string",
"externalIds": [
{
"site": "string",
"id": "string"
}
],
"ordersCount": 0,
"reviewsCount": 0,
"wishlistsCount": 0,
"loyaltyPointsBalance": 0,
"tags": ["string"]
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"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."
},
"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."
},
"externalId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The record's ID when it has exactly one, as it does when you have one site. Null when it has none, or one on each of several sites (see `externalIds`)."
},
"externalIds": {
"type": "array",
"items": {
"type": "object",
"properties": {
"site": {
"type": "string",
"description": "The slug of the site the ID belongs to."
},
"id": {
"type": "string",
"description": "The record's ID on that site, such as a Shopify product ID."
}
},
"required": ["site", "id"],
"additionalProperties": false
},
"description": "The record's IDs on the sites it is linked to, one per site. A Shopify store's IDs are its numeric IDs, not `gid://` ones."
},
"ordersCount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How many orders the customer has placed."
},
"reviewsCount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How many reviews the customer has written."
},
"wishlistsCount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How many wishlists the customer has."
},
"loyaltyPointsBalance": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "The customer's current loyalty points balance, or zero when they hold none."
},
"tags": {
"type": "array",
"items": {
"type": "string"
},
"description": "Names of the tags applied to the customer."
}
},
"required": [
"id",
"email",
"phone",
"firstName",
"lastName",
"country",
"metadata",
"notes",
"emailOptOut",
"emailOptOutAt",
"emailOptOutSource",
"createdAt",
"updatedAt"
],
"additionalProperties": false,
"description": "The requested customer."
}PATCH /customers/{id}
Update a customer's metadata, notes, tags or marketing email opt-out. Profile fields such as the email and name come from the store and cannot be edited here. Triggers the customer.updated webhook. The customer may be named by Meisei's ID or by its ID on one of your sites, such as a Shopify ID: Meisei's is tried first, then every site. Send by to say which kind it is, and site when the same external ID is on more than one of your sites (otherwise that answers 400).
Requires the customers:write permission.
Path parameters
idstringrequiredThe customer's ID, or its ID on one of your sites. URL-encode a gid:// one.
Query parameters
byid | externalIdWhat the ID in the path is: id for Meisei's own ID, externalId for its ID on one of your sites (such as a Shopify ID, in either form). Leave it out to try Meisei's ID first and then every site.
sitestringThe slug of the site an external ID belongs to. Only needed when the same ID is on more than one of your sites.
Body parameters
metadataobjectReplaces the customer's metadata wholesale. Omit to leave it alone.
notesstringInternal notes about the customer.
tagsstring[]The customer's full set of tag names. Omit to leave the tags alone.
emailOptOutbooleanSet to true to stop sending the customer marketing email, false to opt them back in. Omit to leave it alone.
emailOptOutSourcecustomer | merchant | import | shopifyWhere the opt-out came from. Defaults to merchant.
curl \
-X PATCH \
"https://your-store.meisei.app/api/customers/A4Ws1igz" \
-H "Authorization: Bearer sk_YOUR_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"metadata": "...",
"notes": "string",
"tags": [
"string"
],
"emailOptOut": true,
"emailOptOutSource": "customer"
}'import { client } from "@meisei/api/admin/client"
import { patchCustomersById } from "@meisei/api/admin"
client.setConfig({
baseUrl: "https://your-store.meisei.app/api",
headers: { Authorization: "Bearer sk_YOUR_SECRET_KEY" },
})
const { data, error } = await patchCustomersById({
path: { id: "A4Ws1igz" },
body: {
metadata: "...",
notes: "string",
tags: ["string"],
emailOptOut: true,
emailOptOutSource: "customer",
},
})require "meisei"
meisei = Meisei::Client.new(
secret_key: "sk_YOUR_SECRET_KEY",
base_url: "https://your-store.meisei.app/api",
)
result = meisei.customers.update(
"A4Ws1igz",
metadata: "...",
notes: "string",
tags: ["string"],
email_opt_out: true,
email_opt_out_source: "customer",
)from meisei import Client
meisei = Client(
secret_key="sk_YOUR_SECRET_KEY",
base_url="https://your-store.meisei.app/api",
)
result = meisei.customers.update(
"A4Ws1igz",
metadata={},
notes="string",
tags=["string"],
email_opt_out=True,
email_opt_out_source="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",
"importJob": {
"id": "A4Ws1igz",
"type": "customers",
"action": "created",
"row": 0,
"createdAt": "2025-01-15T09:30:00Z"
},
"externalId": "string",
"externalIds": [
{
"site": "string",
"id": "string"
}
],
"ordersCount": 0,
"reviewsCount": 0,
"wishlistsCount": 0,
"loyaltyPointsBalance": 0,
"tags": ["string"]
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"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."
},
"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."
},
"externalId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The record's ID when it has exactly one, as it does when you have one site. Null when it has none, or one on each of several sites (see `externalIds`)."
},
"externalIds": {
"type": "array",
"items": {
"type": "object",
"properties": {
"site": {
"type": "string",
"description": "The slug of the site the ID belongs to."
},
"id": {
"type": "string",
"description": "The record's ID on that site, such as a Shopify product ID."
}
},
"required": ["site", "id"],
"additionalProperties": false
},
"description": "The record's IDs on the sites it is linked to, one per site. A Shopify store's IDs are its numeric IDs, not `gid://` ones."
},
"ordersCount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How many orders the customer has placed."
},
"reviewsCount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How many reviews the customer has written."
},
"wishlistsCount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How many wishlists the customer has."
},
"loyaltyPointsBalance": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "The customer's current loyalty points balance, or zero when they hold none."
},
"tags": {
"type": "array",
"items": {
"type": "string"
},
"description": "Names of the tags applied to the customer."
}
},
"required": [
"id",
"email",
"phone",
"firstName",
"lastName",
"country",
"metadata",
"notes",
"emailOptOut",
"emailOptOutAt",
"emailOptOutSource",
"createdAt",
"updatedAt"
],
"additionalProperties": false,
"description": "The updated customer."
}DELETE /customers/{id}
Delete a customer along with their orders, loyalty memberships and tags. Their reviews are kept but unlinked and unpublished, unless the organization has cascadeDeleteReviews turned on. Triggers the customer.deleted webhook. The customer may be named by Meisei's ID or by its ID on one of your sites, such as a Shopify ID: Meisei's is tried first, then every site. Send by to say which kind it is, and site when the same external ID is on more than one of your sites (otherwise that answers 400).
Requires the customers:write permission.
Path parameters
idstringrequiredThe customer's ID, or its ID on one of your sites. URL-encode a gid:// one.
Query parameters
byid | externalIdWhat the ID in the path is: id for Meisei's own ID, externalId for its ID on one of your sites (such as a Shopify ID, in either form). Leave it out to try Meisei's ID first and then every site.
sitestringThe slug of the site an external ID belongs to. Only needed when the same ID is on more than one of your sites.
curl \
-X DELETE \
"https://your-store.meisei.app/api/customers/A4Ws1igz" \
-H "Authorization: Bearer sk_YOUR_SECRET_KEY"import { client } from "@meisei/api/admin/client"
import { deleteCustomersById } from "@meisei/api/admin"
client.setConfig({
baseUrl: "https://your-store.meisei.app/api",
headers: { Authorization: "Bearer sk_YOUR_SECRET_KEY" },
})
const { data, error } = await deleteCustomersById({
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.customers.delete("A4Ws1igz")from meisei import Client
meisei = Client(
secret_key="sk_YOUR_SECRET_KEY",
base_url="https://your-store.meisei.app/api",
)
result = meisei.customers.delete("A4Ws1igz"){
"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",
"importJob": {
"id": "A4Ws1igz",
"type": "customers",
"action": "created",
"row": 0,
"createdAt": "2025-01-15T09:30:00Z"
},
"externalId": "string",
"externalIds": [
{
"site": "string",
"id": "string"
}
],
"ordersCount": 0,
"reviewsCount": 0,
"wishlistsCount": 0,
"loyaltyPointsBalance": 0,
"tags": ["string"]
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"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."
},
"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."
},
"externalId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The record's ID when it has exactly one, as it does when you have one site. Null when it has none, or one on each of several sites (see `externalIds`)."
},
"externalIds": {
"type": "array",
"items": {
"type": "object",
"properties": {
"site": {
"type": "string",
"description": "The slug of the site the ID belongs to."
},
"id": {
"type": "string",
"description": "The record's ID on that site, such as a Shopify product ID."
}
},
"required": ["site", "id"],
"additionalProperties": false
},
"description": "The record's IDs on the sites it is linked to, one per site. A Shopify store's IDs are its numeric IDs, not `gid://` ones."
},
"ordersCount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How many orders the customer has placed."
},
"reviewsCount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How many reviews the customer has written."
},
"wishlistsCount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How many wishlists the customer has."
},
"loyaltyPointsBalance": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "The customer's current loyalty points balance, or zero when they hold none."
},
"tags": {
"type": "array",
"items": {
"type": "string"
},
"description": "Names of the tags applied to the customer."
}
},
"required": [
"id",
"email",
"phone",
"firstName",
"lastName",
"country",
"metadata",
"notes",
"emailOptOut",
"emailOptOutAt",
"emailOptOutSource",
"createdAt",
"updatedAt"
],
"additionalProperties": false,
"description": "The deleted customer."
}POST /customers/{id}/send-email
Send a customer one of an automation's own emails, filled in with the data you send. It is marketing email unless you pass transactional, so a customer who has opted out, an imported customer who has never been emailed and a suppressed address are skipped: the first two answer with a 409 saying so, and nothing is sent. A transactional email skips only suppressed addresses. It goes out through the same queue, send ramp and pauses as every other email the store sends.
Requires the customers:write permission.
Path parameters
idstringrequiredThe ID of the customer to email.
Body parameters
emailstringrequiredThe key of the automation's email to send, as the automation lists it.
automationIdstringThe automation whose email it is. Leave it out when an automation's own script is calling, which sends one of its own emails.
dataobjectWhat the email's subject, preview line and body fill their variables from, beside customer and site, which Meisei fills in itself.
siteIdstringThe site the email is sent for, which decides its branding, sending address and unsubscribe link. Defaults to the organization's first site.
transactionalbooleanSend it as transactional email, which reaches a customer who has opted out of marketing. Only use it for a message about something the customer did or holds, such as an order, a reward they claimed or their account: your store is responsible for that, and a store whose transactional mail draws complaints has it paused. Suppressed addresses are still skipped.
curl \
-X POST \
"https://your-store.meisei.app/api/customers/A4Ws1igz/send-email" \
-H "Authorization: Bearer sk_YOUR_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"email": "string",
"automationId": "string",
"data": "...",
"siteId": "string",
"transactional": true
}'import { client } from "@meisei/api/admin/client"
import { postCustomersByIdSendEmail } from "@meisei/api/admin"
client.setConfig({
baseUrl: "https://your-store.meisei.app/api",
headers: { Authorization: "Bearer sk_YOUR_SECRET_KEY" },
})
const { data, error } = await postCustomersByIdSendEmail({
path: { id: "A4Ws1igz" },
body: {
email: "string",
automationId: "string",
data: "...",
siteId: "string",
transactional: true,
},
})require "meisei"
meisei = Meisei::Client.new(
secret_key: "sk_YOUR_SECRET_KEY",
base_url: "https://your-store.meisei.app/api",
)
result = meisei.customers.send_email(
"A4Ws1igz",
email: "string",
automation_id: "string",
data: "...",
site_id: "string",
transactional: true,
)from meisei import Client
meisei = Client(
secret_key="sk_YOUR_SECRET_KEY",
base_url="https://your-store.meisei.app/api",
)
result = meisei.customers.send_email(
"A4Ws1igz",
email="string",
automation_id="string",
data={},
site_id="string",
transactional=True,
){
"to": "string",
"subject": "string"
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"to": {
"type": "string",
"description": "The address the email is going to."
},
"subject": {
"type": "string",
"description": "The subject line, as the customer will see it."
}
},
"required": ["to", "subject"],
"additionalProperties": false,
"description": "Where the email is going, and its subject."
}