#Products
GET /products/{id}/review-summary
Get the generated summary of what this product's reviewers say, along with its themes and what it was generated from. Returns null until the product has enough published reviews for one.
Requires the reviews:read permission.
Path parameters
idstringrequiredThe ID of the product.
curl \
"https://your-store.meisei.app/api/products/A4Ws1igz/review-summary" \
-H "Authorization: Bearer sk_YOUR_SECRET_KEY"import { client } from "@meisei/api/admin/client"
import { getProductsByIdReviewSummary } from "@meisei/api/admin"
client.setConfig({
baseUrl: "https://your-store.meisei.app/api",
headers: { Authorization: "Bearer sk_YOUR_SECRET_KEY" },
})
const { data, error } = await getProductsByIdReviewSummary({
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.products.review_summary.retrieve("A4Ws1igz")from meisei import Client
meisei = Client(
secret_key="sk_YOUR_SECRET_KEY",
base_url="https://your-store.meisei.app/api",
)
result = meisei.products.review_summary.retrieve("A4Ws1igz"){
"productId": "A4Ws1igz",
"summary": "string",
"themes": [
{
"label": "string",
"sentiment": "positive",
"reviewCount": 0
}
],
"language": "string",
"reviewCount": 0,
"newestReviewAt": "2025-01-15T09:30:00Z",
"modelId": "string",
"promptVersion": 0,
"generatedAt": "2025-01-15T09:30:00Z",
"suppressedAt": "2025-01-15T09:30:00Z",
"previous": {
"summary": "string",
"themes": [
{
"label": "...",
"sentiment": "...",
"reviewCount": "..."
}
],
"language": "string",
"reviewCount": 0,
"newestReviewAt": "2025-01-15T09:30:00Z",
"modelId": "string",
"promptVersion": 0,
"generatedAt": "2025-01-15T09:30:00Z"
}
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"anyOf": [
{
"type": "object",
"properties": {
"productId": {
"type": "string",
"description": "The product this summary describes.",
"idCodec": "sqid"
},
"summary": {
"type": "string",
"description": "Two or three sentences on what reviewers say, generated from the reviews."
},
"themes": {
"type": "array",
"items": {
"type": "object",
"properties": {
"label": {
"type": "string",
"description": "Short name for the theme, such as `Fit runs small`, in a few words at most."
},
"sentiment": {
"type": "string",
"enum": ["positive", "negative", "mixed"],
"description": "Whether reviewers raise this theme approvingly, as a complaint, or both."
},
"reviewCount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "Roughly how many of the published reviews mention this theme."
}
},
"required": ["label", "sentiment", "reviewCount"],
"additionalProperties": false
},
"description": "Recurring topics reviewers raise, most mentioned first."
},
"language": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The language the summary is written in, which is the one most reviews are in."
},
"reviewCount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How many published reviews the summary was generated from."
},
"newestReviewAt": {
"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 newest review it covers was written."
},
"modelId": {
"type": "string",
"description": "The model that produced it."
},
"promptVersion": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "Which version of the instructions produced it."
},
"generatedAt": {
"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 it was generated."
},
"suppressedAt": {
"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 it was hidden from the storefront, or null if it is showing."
},
"previous": {
"anyOf": [
{
"type": "object",
"properties": {
"summary": {
"type": "string"
},
"themes": {
"type": "array",
"items": {
"type": "object",
"properties": {
"label": {
"type": "string",
"description": "Short name for the theme, such as `Fit runs small`, in a few words at most."
},
"sentiment": {
"type": "string",
"enum": ["positive", "negative", "mixed"],
"description": "Whether reviewers raise this theme approvingly, as a complaint, or both."
},
"reviewCount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "Roughly how many of the published reviews mention this theme."
}
},
"required": ["label", "sentiment", "reviewCount"],
"additionalProperties": false
},
"description": "Recurring topics reviewers raise, most mentioned first."
},
"language": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"reviewCount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"newestReviewAt": {
"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"
}
]
},
"modelId": {
"type": "string"
},
"promptVersion": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"generatedAt": {
"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))$"
}
},
"required": [
"summary",
"themes",
"language",
"reviewCount",
"newestReviewAt",
"modelId",
"promptVersion",
"generatedAt"
],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "The summary this one replaced, kept until it is accepted or reverted to. Null once there is nothing to go back to."
}
},
"required": [
"productId",
"summary",
"themes",
"language",
"reviewCount",
"newestReviewAt",
"modelId",
"promptVersion",
"generatedAt",
"suppressedAt",
"previous"
],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "The product's review summary, or null if it has none."
}PATCH /products/{id}/review-summary
Hide a product's review summary from your storefront, or put it back. A hidden summary is also left alone by the regeneration policy, so hiding it is not undone by the next batch of reviews.
Requires the reviews:write permission.
Path parameters
idstringrequiredThe ID of the product.
Body parameters
suppressedbooleanrequiredTrue hides the summary from your storefront and stops it being regenerated. False puts it back.
curl \
-X PATCH \
"https://your-store.meisei.app/api/products/A4Ws1igz/review-summary" \
-H "Authorization: Bearer sk_YOUR_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"suppressed": true
}'import { client } from "@meisei/api/admin/client"
import { patchProductsByIdReviewSummary } from "@meisei/api/admin"
client.setConfig({
baseUrl: "https://your-store.meisei.app/api",
headers: { Authorization: "Bearer sk_YOUR_SECRET_KEY" },
})
const { data, error } = await patchProductsByIdReviewSummary({
path: { id: "A4Ws1igz" },
body: {
suppressed: true,
},
})require "meisei"
meisei = Meisei::Client.new(
secret_key: "sk_YOUR_SECRET_KEY",
base_url: "https://your-store.meisei.app/api",
)
result = meisei.products.review_summary.update("A4Ws1igz", suppressed: true)from meisei import Client
meisei = Client(
secret_key="sk_YOUR_SECRET_KEY",
base_url="https://your-store.meisei.app/api",
)
result = meisei.products.review_summary.update("A4Ws1igz", suppressed=True){
"productId": "A4Ws1igz",
"summary": "string",
"themes": [
{
"label": "string",
"sentiment": "positive",
"reviewCount": 0
}
],
"language": "string",
"reviewCount": 0,
"newestReviewAt": "2025-01-15T09:30:00Z",
"modelId": "string",
"promptVersion": 0,
"generatedAt": "2025-01-15T09:30:00Z",
"suppressedAt": "2025-01-15T09:30:00Z",
"previous": {
"summary": "string",
"themes": [
{
"label": "...",
"sentiment": "...",
"reviewCount": "..."
}
],
"language": "string",
"reviewCount": 0,
"newestReviewAt": "2025-01-15T09:30:00Z",
"modelId": "string",
"promptVersion": 0,
"generatedAt": "2025-01-15T09:30:00Z"
}
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"productId": {
"type": "string",
"description": "The product this summary describes.",
"idCodec": "sqid"
},
"summary": {
"type": "string",
"description": "Two or three sentences on what reviewers say, generated from the reviews."
},
"themes": {
"type": "array",
"items": {
"type": "object",
"properties": {
"label": {
"type": "string",
"description": "Short name for the theme, such as `Fit runs small`, in a few words at most."
},
"sentiment": {
"type": "string",
"enum": ["positive", "negative", "mixed"],
"description": "Whether reviewers raise this theme approvingly, as a complaint, or both."
},
"reviewCount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "Roughly how many of the published reviews mention this theme."
}
},
"required": ["label", "sentiment", "reviewCount"],
"additionalProperties": false
},
"description": "Recurring topics reviewers raise, most mentioned first."
},
"language": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The language the summary is written in, which is the one most reviews are in."
},
"reviewCount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How many published reviews the summary was generated from."
},
"newestReviewAt": {
"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 newest review it covers was written."
},
"modelId": {
"type": "string",
"description": "The model that produced it."
},
"promptVersion": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "Which version of the instructions produced it."
},
"generatedAt": {
"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 it was generated."
},
"suppressedAt": {
"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 it was hidden from the storefront, or null if it is showing."
},
"previous": {
"anyOf": [
{
"type": "object",
"properties": {
"summary": {
"type": "string"
},
"themes": {
"type": "array",
"items": {
"type": "object",
"properties": {
"label": {
"type": "string",
"description": "Short name for the theme, such as `Fit runs small`, in a few words at most."
},
"sentiment": {
"type": "string",
"enum": ["positive", "negative", "mixed"],
"description": "Whether reviewers raise this theme approvingly, as a complaint, or both."
},
"reviewCount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "Roughly how many of the published reviews mention this theme."
}
},
"required": ["label", "sentiment", "reviewCount"],
"additionalProperties": false
},
"description": "Recurring topics reviewers raise, most mentioned first."
},
"language": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"reviewCount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"newestReviewAt": {
"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"
}
]
},
"modelId": {
"type": "string"
},
"promptVersion": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"generatedAt": {
"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))$"
}
},
"required": [
"summary",
"themes",
"language",
"reviewCount",
"newestReviewAt",
"modelId",
"promptVersion",
"generatedAt"
],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "The summary this one replaced, kept until it is accepted or reverted to. Null once there is nothing to go back to."
}
},
"required": [
"productId",
"summary",
"themes",
"language",
"reviewCount",
"newestReviewAt",
"modelId",
"promptVersion",
"generatedAt",
"suppressedAt",
"previous"
],
"additionalProperties": false,
"description": "The updated review summary."
}POST /products/{id}/review-summary/regenerate
Queue a fresh summary for this product. The new one goes live as soon as it is written, and the one it replaces is kept so you can go back to it. A product can be regenerated once a day. Answers 409 while review summaries are switched off in the review settings.
Requires the reviews:write permission.
Costs 5 rate limit tokens instead of the usual one. See Rate limits.
Path parameters
idstringrequiredThe ID of the product.
curl \
-X POST \
"https://your-store.meisei.app/api/products/A4Ws1igz/review-summary/regenerate" \
-H "Authorization: Bearer sk_YOUR_SECRET_KEY"import { client } from "@meisei/api/admin/client"
import { postProductsByIdReviewSummaryRegenerate } from "@meisei/api/admin"
client.setConfig({
baseUrl: "https://your-store.meisei.app/api",
headers: { Authorization: "Bearer sk_YOUR_SECRET_KEY" },
})
const { data, error } = await postProductsByIdReviewSummaryRegenerate({
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.products.review_summary.regenerate("A4Ws1igz")from meisei import Client
meisei = Client(
secret_key="sk_YOUR_SECRET_KEY",
base_url="https://your-store.meisei.app/api",
)
result = meisei.products.review_summary.regenerate("A4Ws1igz"){
"queued": true
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"queued": {
"type": "boolean",
"description": "True once the regeneration has been queued."
}
},
"required": ["queued"],
"additionalProperties": false
}POST /products/{id}/review-summary/accept
Keep the current summary and forget the one it replaced. After this there is nothing to revert to.
Requires the reviews:write permission.
Path parameters
idstringrequiredThe ID of the product.
curl \
-X POST \
"https://your-store.meisei.app/api/products/A4Ws1igz/review-summary/accept" \
-H "Authorization: Bearer sk_YOUR_SECRET_KEY"import { client } from "@meisei/api/admin/client"
import { postProductsByIdReviewSummaryAccept } from "@meisei/api/admin"
client.setConfig({
baseUrl: "https://your-store.meisei.app/api",
headers: { Authorization: "Bearer sk_YOUR_SECRET_KEY" },
})
const { data, error } = await postProductsByIdReviewSummaryAccept({
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.products.review_summary.accept("A4Ws1igz")from meisei import Client
meisei = Client(
secret_key="sk_YOUR_SECRET_KEY",
base_url="https://your-store.meisei.app/api",
)
result = meisei.products.review_summary.accept("A4Ws1igz"){
"productId": "A4Ws1igz",
"summary": "string",
"themes": [
{
"label": "string",
"sentiment": "positive",
"reviewCount": 0
}
],
"language": "string",
"reviewCount": 0,
"newestReviewAt": "2025-01-15T09:30:00Z",
"modelId": "string",
"promptVersion": 0,
"generatedAt": "2025-01-15T09:30:00Z",
"suppressedAt": "2025-01-15T09:30:00Z",
"previous": {
"summary": "string",
"themes": [
{
"label": "...",
"sentiment": "...",
"reviewCount": "..."
}
],
"language": "string",
"reviewCount": 0,
"newestReviewAt": "2025-01-15T09:30:00Z",
"modelId": "string",
"promptVersion": 0,
"generatedAt": "2025-01-15T09:30:00Z"
}
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"productId": {
"type": "string",
"description": "The product this summary describes.",
"idCodec": "sqid"
},
"summary": {
"type": "string",
"description": "Two or three sentences on what reviewers say, generated from the reviews."
},
"themes": {
"type": "array",
"items": {
"type": "object",
"properties": {
"label": {
"type": "string",
"description": "Short name for the theme, such as `Fit runs small`, in a few words at most."
},
"sentiment": {
"type": "string",
"enum": ["positive", "negative", "mixed"],
"description": "Whether reviewers raise this theme approvingly, as a complaint, or both."
},
"reviewCount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "Roughly how many of the published reviews mention this theme."
}
},
"required": ["label", "sentiment", "reviewCount"],
"additionalProperties": false
},
"description": "Recurring topics reviewers raise, most mentioned first."
},
"language": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The language the summary is written in, which is the one most reviews are in."
},
"reviewCount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How many published reviews the summary was generated from."
},
"newestReviewAt": {
"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 newest review it covers was written."
},
"modelId": {
"type": "string",
"description": "The model that produced it."
},
"promptVersion": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "Which version of the instructions produced it."
},
"generatedAt": {
"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 it was generated."
},
"suppressedAt": {
"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 it was hidden from the storefront, or null if it is showing."
},
"previous": {
"anyOf": [
{
"type": "object",
"properties": {
"summary": {
"type": "string"
},
"themes": {
"type": "array",
"items": {
"type": "object",
"properties": {
"label": {
"type": "string",
"description": "Short name for the theme, such as `Fit runs small`, in a few words at most."
},
"sentiment": {
"type": "string",
"enum": ["positive", "negative", "mixed"],
"description": "Whether reviewers raise this theme approvingly, as a complaint, or both."
},
"reviewCount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "Roughly how many of the published reviews mention this theme."
}
},
"required": ["label", "sentiment", "reviewCount"],
"additionalProperties": false
},
"description": "Recurring topics reviewers raise, most mentioned first."
},
"language": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"reviewCount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"newestReviewAt": {
"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"
}
]
},
"modelId": {
"type": "string"
},
"promptVersion": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"generatedAt": {
"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))$"
}
},
"required": [
"summary",
"themes",
"language",
"reviewCount",
"newestReviewAt",
"modelId",
"promptVersion",
"generatedAt"
],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "The summary this one replaced, kept until it is accepted or reverted to. Null once there is nothing to go back to."
}
},
"required": [
"productId",
"summary",
"themes",
"language",
"reviewCount",
"newestReviewAt",
"modelId",
"promptVersion",
"generatedAt",
"suppressedAt",
"previous"
],
"additionalProperties": false,
"description": "The accepted review summary."
}POST /products/{id}/review-summary/revert
Put the previous summary back and discard the one that replaced it. Only the most recent regeneration can be undone.
Requires the reviews:write permission.
Path parameters
idstringrequiredThe ID of the product.
curl \
-X POST \
"https://your-store.meisei.app/api/products/A4Ws1igz/review-summary/revert" \
-H "Authorization: Bearer sk_YOUR_SECRET_KEY"import { client } from "@meisei/api/admin/client"
import { postProductsByIdReviewSummaryRevert } from "@meisei/api/admin"
client.setConfig({
baseUrl: "https://your-store.meisei.app/api",
headers: { Authorization: "Bearer sk_YOUR_SECRET_KEY" },
})
const { data, error } = await postProductsByIdReviewSummaryRevert({
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.products.review_summary.revert("A4Ws1igz")from meisei import Client
meisei = Client(
secret_key="sk_YOUR_SECRET_KEY",
base_url="https://your-store.meisei.app/api",
)
result = meisei.products.review_summary.revert("A4Ws1igz"){
"productId": "A4Ws1igz",
"summary": "string",
"themes": [
{
"label": "string",
"sentiment": "positive",
"reviewCount": 0
}
],
"language": "string",
"reviewCount": 0,
"newestReviewAt": "2025-01-15T09:30:00Z",
"modelId": "string",
"promptVersion": 0,
"generatedAt": "2025-01-15T09:30:00Z",
"suppressedAt": "2025-01-15T09:30:00Z",
"previous": {
"summary": "string",
"themes": [
{
"label": "...",
"sentiment": "...",
"reviewCount": "..."
}
],
"language": "string",
"reviewCount": 0,
"newestReviewAt": "2025-01-15T09:30:00Z",
"modelId": "string",
"promptVersion": 0,
"generatedAt": "2025-01-15T09:30:00Z"
}
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"productId": {
"type": "string",
"description": "The product this summary describes.",
"idCodec": "sqid"
},
"summary": {
"type": "string",
"description": "Two or three sentences on what reviewers say, generated from the reviews."
},
"themes": {
"type": "array",
"items": {
"type": "object",
"properties": {
"label": {
"type": "string",
"description": "Short name for the theme, such as `Fit runs small`, in a few words at most."
},
"sentiment": {
"type": "string",
"enum": ["positive", "negative", "mixed"],
"description": "Whether reviewers raise this theme approvingly, as a complaint, or both."
},
"reviewCount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "Roughly how many of the published reviews mention this theme."
}
},
"required": ["label", "sentiment", "reviewCount"],
"additionalProperties": false
},
"description": "Recurring topics reviewers raise, most mentioned first."
},
"language": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The language the summary is written in, which is the one most reviews are in."
},
"reviewCount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How many published reviews the summary was generated from."
},
"newestReviewAt": {
"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 newest review it covers was written."
},
"modelId": {
"type": "string",
"description": "The model that produced it."
},
"promptVersion": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "Which version of the instructions produced it."
},
"generatedAt": {
"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 it was generated."
},
"suppressedAt": {
"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 it was hidden from the storefront, or null if it is showing."
},
"previous": {
"anyOf": [
{
"type": "object",
"properties": {
"summary": {
"type": "string"
},
"themes": {
"type": "array",
"items": {
"type": "object",
"properties": {
"label": {
"type": "string",
"description": "Short name for the theme, such as `Fit runs small`, in a few words at most."
},
"sentiment": {
"type": "string",
"enum": ["positive", "negative", "mixed"],
"description": "Whether reviewers raise this theme approvingly, as a complaint, or both."
},
"reviewCount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "Roughly how many of the published reviews mention this theme."
}
},
"required": ["label", "sentiment", "reviewCount"],
"additionalProperties": false
},
"description": "Recurring topics reviewers raise, most mentioned first."
},
"language": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"reviewCount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"newestReviewAt": {
"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"
}
]
},
"modelId": {
"type": "string"
},
"promptVersion": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"generatedAt": {
"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))$"
}
},
"required": [
"summary",
"themes",
"language",
"reviewCount",
"newestReviewAt",
"modelId",
"promptVersion",
"generatedAt"
],
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "The summary this one replaced, kept until it is accepted or reverted to. Null once there is nothing to go back to."
}
},
"required": [
"productId",
"summary",
"themes",
"language",
"reviewCount",
"newestReviewAt",
"modelId",
"promptVersion",
"generatedAt",
"suppressedAt",
"previous"
],
"additionalProperties": false,
"description": "The restored review summary."
}GET /products
List the organization's products, newest first by default. Supports search, filtering and cursor pagination.
Requires the products: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.
sorttitle | -title | createdAt | -createdAt | wishlistsCount | -wishlistsCount | 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, slug, title, metadata, averageRating, ratingCount, createdAt, tags.name, variants.id, variants.title, variants.slug, variants.metadata, externalIds.externalId. 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, reviewsCount, wishlistsCount, variantsCount, tags, variants.
curl \
"https://your-store.meisei.app/api/products" \
-H "Authorization: Bearer sk_YOUR_SECRET_KEY"import { client } from "@meisei/api/admin/client"
import { getProducts } from "@meisei/api/admin"
client.setConfig({
baseUrl: "https://your-store.meisei.app/api",
headers: { Authorization: "Bearer sk_YOUR_SECRET_KEY" },
})
const { data, error } = await getProducts()require "meisei"
meisei = Meisei::Client.new(
secret_key: "sk_YOUR_SECRET_KEY",
base_url: "https://your-store.meisei.app/api",
)
page = meisei.products.listfrom meisei import Client
meisei = Client(
secret_key="sk_YOUR_SECRET_KEY",
base_url="https://your-store.meisei.app/api",
)
page = meisei.products.list(){
"data": [
{
"id": "A4Ws1igz",
"slug": "string",
"title": "string",
"description": "string",
"imageUrl": "string",
"url": "string",
"brand": "string",
"metadata": "...",
"notes": "string",
"averageRating": 0,
"ratingCount": 0,
"createdAt": "2025-01-15T09:30:00Z",
"updatedAt": "2025-01-15T09:30:00Z",
"importJob": {
"id": "...",
"type": "...",
"action": "...",
"row": "...",
"createdAt": "..."
},
"externalId": "string",
"externalIds": ["..."],
"reviewsCount": 0,
"wishlistsCount": 0,
"variantsCount": 0,
"tags": ["..."],
"variants": ["..."]
}
],
"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 product.",
"idCodec": "sqid"
},
"slug": {
"type": "string",
"description": "The product's URL handle, unique within the organization. A Shopify store's sync sets it from the product's handle and follows a rename while no other product holds the new one. The product's Shopify ID is in `externalIds`."
},
"title": {
"type": "string",
"description": "The product's display name."
},
"description": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The product's long-form description."
},
"imageUrl": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "URL of the product's main image."
},
"url": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "URL of the product's page on the storefront, or null if it is not known."
},
"brand": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The product's brand, or null if it is not known. Shopping channels such as Google match reviews to a product by its brand together with a variant's `mpn`. A Shopify store's sync sets it from the product's vendor."
},
"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": {
"type": "string",
"description": "Internal notes about the product. Never shown to customers."
},
"averageRating": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"description": "The average star rating of the product's published reviews, out of 5 and rounded to two decimal places, or null if none of them has a rating."
},
"ratingCount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How many published reviews of the product have a star rating."
},
"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 product 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 product 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."
},
"reviewsCount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How many reviews the product has."
},
"wishlistsCount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How many wishlists the product is on. Counts wishlists, not items."
},
"variantsCount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How many variants the product has."
},
"tags": {
"type": "array",
"items": {
"type": "string"
},
"description": "Names of the tags applied to the product."
},
"variants": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Unique identifier for the variant.",
"idCodec": "sqid"
},
"slug": {
"type": "string",
"description": "The variant's handle, unique within its product."
},
"title": {
"type": "string",
"description": "The variant's display name, such as its size or color."
},
"imageUrl": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "URL of the variant's image."
},
"priceCents": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "The variant's stored price in cents, or null if it is not known."
},
"currencyCode": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The currency of `priceCents` as an ISO 4217 code, or null if it is not known."
},
"available": {
"anyOf": [
{
"type": "boolean"
},
{
"type": "null"
}
],
"description": "Whether the variant can be bought right now, or null if no source has said. A change from false to true is a restock, which an automation can announce; null never counts as one."
},
"gtin": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The variant's GTIN, the number under its barcode (a UPC, EAN or ISBN), or null if it is not known. It is what shopping channels such as Google match reviews to a product by. A Shopify store's sync sets it from the variant's barcode when that is a valid GTIN."
},
"mpn": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The variant's manufacturer part number, or null if it is not known. With the product's `brand`, it identifies a product that has no GTIN."
},
"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`."
},
"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."
}
},
"required": [
"id",
"slug",
"title",
"imageUrl",
"priceCents",
"currencyCode",
"available",
"gtin",
"mpn",
"metadata",
"externalId",
"externalIds"
],
"additionalProperties": false
},
"description": "The product's variants, sorted by title."
}
},
"required": [
"id",
"slug",
"title",
"description",
"imageUrl",
"url",
"brand",
"metadata",
"notes",
"averageRating",
"ratingCount",
"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 /products/bulk-update
Add or remove tags and metadata across every product matching a filter, or an exact list of ids, in one request. Sending neither applies the changes to every product in the organization. Each product whose tags or metadata actually change triggers the product.updated webhook.
Requires the products:write permission.
Costs 5 rate limit tokens instead of the usual one. See Rate limits.
Query parameters
filterstringA JSON-encoded filter document selecting the products to update. Omit it and every product 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 products 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 product already has.
removeMetadataKeysstring[]Metadata keys to delete.
curl \
-X PATCH \
"https://your-store.meisei.app/api/products/bulk-update" \
-H "Authorization: Bearer sk_YOUR_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"ids": [
"string"
],
"addTags": [
"string"
],
"removeTags": [
"string"
],
"addMetadata": "...",
"removeMetadataKeys": [
"string"
]
}'import { client } from "@meisei/api/admin/client"
import { patchProductsBulkUpdate } from "@meisei/api/admin"
client.setConfig({
baseUrl: "https://your-store.meisei.app/api",
headers: { Authorization: "Bearer sk_YOUR_SECRET_KEY" },
})
const { data, error } = await patchProductsBulkUpdate({
body: {
ids: ["string"],
addTags: ["string"],
removeTags: ["string"],
addMetadata: "...",
removeMetadataKeys: ["string"],
},
})require "meisei"
meisei = Meisei::Client.new(
secret_key: "sk_YOUR_SECRET_KEY",
base_url: "https://your-store.meisei.app/api",
)
result = meisei.products.bulk_update(
ids: ["string"],
add_tags: ["string"],
remove_tags: ["string"],
add_metadata: "...",
remove_metadata_keys: ["string"],
)from meisei import Client
meisei = Client(
secret_key="sk_YOUR_SECRET_KEY",
base_url="https://your-store.meisei.app/api",
)
result = meisei.products.bulk_update(
ids=["string"],
add_tags=["string"],
remove_tags=["string"],
add_metadata={},
remove_metadata_keys=["string"],
){
"count": 0
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"count": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How many products the filter matched and updated."
}
},
"required": ["count"],
"additionalProperties": false
}POST /products/import
Create or update products in bulk, matching on externalIds first and then on slug. Variants and site links are replaced with exactly what you send, so include the full set for each product. 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 product triggers the product.created webhook and each existing one product.updated.
Requires the products:write permission.
Costs 10 rate limit tokens instead of the usual one. See Rate limits.
Body parameters
dataobject[]requiredThe products 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.
mergeMetadatabooleanWhen true, each row's metadata keys (and its variants' metadata keys) are merged onto whatever is already stored instead of replacing it.
followSlugChangesbooleanWhen a row's external ID is already linked to a product with a different slug, whether that product takes the row's slug (when no other product holds it). On by default; off keeps the product's slug.
curl \
-X POST \
"https://your-store.meisei.app/api/products/import" \
-H "Authorization: Bearer sk_YOUR_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"data": [
{
"slug": "string",
"title": "string",
"description": "string",
"imageUrl": "string",
"url": "string",
"brand": "string",
"metadata": "...",
"tags": [
"..."
],
"variants": [
"..."
],
"externalId": "string",
"externalIds": [
"..."
]
}
],
"strict": true,
"mergeMetadata": true,
"followSlugChanges": true
}'import { client } from "@meisei/api/admin/client"
import { postProductsImport } from "@meisei/api/admin"
client.setConfig({
baseUrl: "https://your-store.meisei.app/api",
headers: { Authorization: "Bearer sk_YOUR_SECRET_KEY" },
})
const { data, error } = await postProductsImport({
body: {
data: [
{
slug: "string",
title: "string",
description: "string",
imageUrl: "string",
url: "string",
brand: "string",
metadata: "...",
tags: ["..."],
variants: ["..."],
externalId: "string",
externalIds: ["..."],
},
],
strict: true,
mergeMetadata: true,
followSlugChanges: true,
},
})require "meisei"
meisei = Meisei::Client.new(
secret_key: "sk_YOUR_SECRET_KEY",
base_url: "https://your-store.meisei.app/api",
)
result = meisei.products.import(
data: [
{
slug: "string",
title: "string",
description: "string",
image_url: "string",
url: "string",
brand: "string",
metadata: "...",
tags: ["..."],
variants: ["..."],
external_id: "string",
external_ids: ["..."],
},
],
strict: true,
merge_metadata: true,
follow_slug_changes: true,
)from meisei import Client
meisei = Client(
secret_key="sk_YOUR_SECRET_KEY",
base_url="https://your-store.meisei.app/api",
)
result = meisei.products.import_(
data=[
{
"slug": "string",
"title": "string",
"description": "string",
"image_url": "string",
"url": "string",
"brand": "string",
"metadata": {},
"tags": ["..."],
"variants": [],
"external_id": "string",
"external_ids": [],
},
],
strict=True,
merge_metadata=True,
follow_slug_changes=True,
){
"data": [
{
"id": "A4Ws1igz",
"slug": "string",
"title": "string",
"description": "string",
"imageUrl": "string",
"url": "string",
"brand": "string",
"metadata": "...",
"notes": "string",
"averageRating": 0,
"ratingCount": 0,
"createdAt": "2025-01-15T09:30:00Z",
"updatedAt": "2025-01-15T09:30:00Z",
"importJob": {
"id": "...",
"type": "...",
"action": "...",
"row": "...",
"createdAt": "..."
},
"externalId": "string",
"externalIds": ["..."],
"reviewsCount": 0,
"wishlistsCount": 0,
"variantsCount": 0,
"tags": ["..."],
"variants": ["..."]
}
],
"skipped": [
{
"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 product.",
"idCodec": "sqid"
},
"slug": {
"type": "string",
"description": "The product's URL handle, unique within the organization. A Shopify store's sync sets it from the product's handle and follows a rename while no other product holds the new one. The product's Shopify ID is in `externalIds`."
},
"title": {
"type": "string",
"description": "The product's display name."
},
"description": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The product's long-form description."
},
"imageUrl": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "URL of the product's main image."
},
"url": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "URL of the product's page on the storefront, or null if it is not known."
},
"brand": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The product's brand, or null if it is not known. Shopping channels such as Google match reviews to a product by its brand together with a variant's `mpn`. A Shopify store's sync sets it from the product's vendor."
},
"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": {
"type": "string",
"description": "Internal notes about the product. Never shown to customers."
},
"averageRating": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"description": "The average star rating of the product's published reviews, out of 5 and rounded to two decimal places, or null if none of them has a rating."
},
"ratingCount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How many published reviews of the product have a star rating."
},
"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 product 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 product 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."
},
"reviewsCount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How many reviews the product has."
},
"wishlistsCount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How many wishlists the product is on. Counts wishlists, not items."
},
"variantsCount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How many variants the product has."
},
"tags": {
"type": "array",
"items": {
"type": "string"
},
"description": "Names of the tags applied to the product."
},
"variants": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Unique identifier for the variant.",
"idCodec": "sqid"
},
"slug": {
"type": "string",
"description": "The variant's handle, unique within its product."
},
"title": {
"type": "string",
"description": "The variant's display name, such as its size or color."
},
"imageUrl": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "URL of the variant's image."
},
"priceCents": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "The variant's stored price in cents, or null if it is not known."
},
"currencyCode": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The currency of `priceCents` as an ISO 4217 code, or null if it is not known."
},
"available": {
"anyOf": [
{
"type": "boolean"
},
{
"type": "null"
}
],
"description": "Whether the variant can be bought right now, or null if no source has said. A change from false to true is a restock, which an automation can announce; null never counts as one."
},
"gtin": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The variant's GTIN, the number under its barcode (a UPC, EAN or ISBN), or null if it is not known. It is what shopping channels such as Google match reviews to a product by. A Shopify store's sync sets it from the variant's barcode when that is a valid GTIN."
},
"mpn": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The variant's manufacturer part number, or null if it is not known. With the product's `brand`, it identifies a product that has no GTIN."
},
"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`."
},
"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."
}
},
"required": [
"id",
"slug",
"title",
"imageUrl",
"priceCents",
"currencyCode",
"available",
"gtin",
"mpn",
"metadata",
"externalId",
"externalIds"
],
"additionalProperties": false
},
"description": "The product's variants, sorted by title."
}
},
"required": [
"id",
"slug",
"title",
"description",
"imageUrl",
"url",
"brand",
"metadata",
"notes",
"averageRating",
"ratingCount",
"createdAt",
"updatedAt"
],
"additionalProperties": false
},
"description": "The imported products, 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."
}
},
"required": ["data", "skipped"],
"additionalProperties": false
}GET /products/{id}
Get a single product. The product 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 products:read permission.
Path parameters
idstringrequiredThe product'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, reviewsCount, wishlistsCount, variantsCount, tags, variants.
curl \
"https://your-store.meisei.app/api/products/A4Ws1igz" \
-H "Authorization: Bearer sk_YOUR_SECRET_KEY"import { client } from "@meisei/api/admin/client"
import { getProductsById } from "@meisei/api/admin"
client.setConfig({
baseUrl: "https://your-store.meisei.app/api",
headers: { Authorization: "Bearer sk_YOUR_SECRET_KEY" },
})
const { data, error } = await getProductsById({
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.products.retrieve("A4Ws1igz")from meisei import Client
meisei = Client(
secret_key="sk_YOUR_SECRET_KEY",
base_url="https://your-store.meisei.app/api",
)
result = meisei.products.retrieve("A4Ws1igz"){
"id": "A4Ws1igz",
"slug": "string",
"title": "string",
"description": "string",
"imageUrl": "string",
"url": "string",
"brand": "string",
"metadata": "...",
"notes": "string",
"averageRating": 0,
"ratingCount": 0,
"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"
}
],
"reviewsCount": 0,
"wishlistsCount": 0,
"variantsCount": 0,
"tags": ["string"],
"variants": [
{
"id": "A4Ws1igz",
"slug": "string",
"title": "string",
"imageUrl": "string",
"priceCents": 0,
"currencyCode": "string",
"available": true,
"gtin": "string",
"mpn": "string",
"metadata": "...",
"externalId": "string",
"externalIds": ["..."]
}
]
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Unique identifier for the product.",
"idCodec": "sqid"
},
"slug": {
"type": "string",
"description": "The product's URL handle, unique within the organization. A Shopify store's sync sets it from the product's handle and follows a rename while no other product holds the new one. The product's Shopify ID is in `externalIds`."
},
"title": {
"type": "string",
"description": "The product's display name."
},
"description": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The product's long-form description."
},
"imageUrl": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "URL of the product's main image."
},
"url": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "URL of the product's page on the storefront, or null if it is not known."
},
"brand": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The product's brand, or null if it is not known. Shopping channels such as Google match reviews to a product by its brand together with a variant's `mpn`. A Shopify store's sync sets it from the product's vendor."
},
"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": {
"type": "string",
"description": "Internal notes about the product. Never shown to customers."
},
"averageRating": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"description": "The average star rating of the product's published reviews, out of 5 and rounded to two decimal places, or null if none of them has a rating."
},
"ratingCount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How many published reviews of the product have a star rating."
},
"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 product 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 product 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."
},
"reviewsCount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How many reviews the product has."
},
"wishlistsCount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How many wishlists the product is on. Counts wishlists, not items."
},
"variantsCount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How many variants the product has."
},
"tags": {
"type": "array",
"items": {
"type": "string"
},
"description": "Names of the tags applied to the product."
},
"variants": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Unique identifier for the variant.",
"idCodec": "sqid"
},
"slug": {
"type": "string",
"description": "The variant's handle, unique within its product."
},
"title": {
"type": "string",
"description": "The variant's display name, such as its size or color."
},
"imageUrl": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "URL of the variant's image."
},
"priceCents": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "The variant's stored price in cents, or null if it is not known."
},
"currencyCode": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The currency of `priceCents` as an ISO 4217 code, or null if it is not known."
},
"available": {
"anyOf": [
{
"type": "boolean"
},
{
"type": "null"
}
],
"description": "Whether the variant can be bought right now, or null if no source has said. A change from false to true is a restock, which an automation can announce; null never counts as one."
},
"gtin": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The variant's GTIN, the number under its barcode (a UPC, EAN or ISBN), or null if it is not known. It is what shopping channels such as Google match reviews to a product by. A Shopify store's sync sets it from the variant's barcode when that is a valid GTIN."
},
"mpn": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The variant's manufacturer part number, or null if it is not known. With the product's `brand`, it identifies a product that has no GTIN."
},
"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`."
},
"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."
}
},
"required": [
"id",
"slug",
"title",
"imageUrl",
"priceCents",
"currencyCode",
"available",
"gtin",
"mpn",
"metadata",
"externalId",
"externalIds"
],
"additionalProperties": false
},
"description": "The product's variants, sorted by title."
}
},
"required": [
"id",
"slug",
"title",
"description",
"imageUrl",
"url",
"brand",
"metadata",
"notes",
"averageRating",
"ratingCount",
"createdAt",
"updatedAt"
],
"additionalProperties": false,
"description": "The requested product."
}PATCH /products/{id}
Update a product's metadata, notes, tags or brand. Catalog fields such as the title and slug come from the store and cannot be edited here. Triggers the product.updated webhook. The product 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 products:write permission.
Path parameters
idstringrequiredThe product'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 product's metadata wholesale. Omit to leave it alone.
notesstringInternal notes about the product.
tagsstring[]The product's full set of tag names. Omit to leave the tags alone.
brandstring | nullThe product's brand. Send null to clear it, or omit it to leave it alone.
curl \
-X PATCH \
"https://your-store.meisei.app/api/products/A4Ws1igz" \
-H "Authorization: Bearer sk_YOUR_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"metadata": "...",
"notes": "string",
"tags": [
"string"
],
"brand": "string"
}'import { client } from "@meisei/api/admin/client"
import { patchProductsById } from "@meisei/api/admin"
client.setConfig({
baseUrl: "https://your-store.meisei.app/api",
headers: { Authorization: "Bearer sk_YOUR_SECRET_KEY" },
})
const { data, error } = await patchProductsById({
path: { id: "A4Ws1igz" },
body: {
metadata: "...",
notes: "string",
tags: ["string"],
brand: "string",
},
})require "meisei"
meisei = Meisei::Client.new(
secret_key: "sk_YOUR_SECRET_KEY",
base_url: "https://your-store.meisei.app/api",
)
result = meisei.products.update(
"A4Ws1igz",
metadata: "...",
notes: "string",
tags: ["string"],
brand: "string",
)from meisei import Client
meisei = Client(
secret_key="sk_YOUR_SECRET_KEY",
base_url="https://your-store.meisei.app/api",
)
result = meisei.products.update(
"A4Ws1igz",
metadata={},
notes="string",
tags=["string"],
brand="string",
){
"id": "A4Ws1igz",
"slug": "string",
"title": "string",
"description": "string",
"imageUrl": "string",
"url": "string",
"brand": "string",
"metadata": "...",
"notes": "string",
"averageRating": 0,
"ratingCount": 0,
"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"
}
],
"reviewsCount": 0,
"wishlistsCount": 0,
"variantsCount": 0,
"tags": ["string"],
"variants": [
{
"id": "A4Ws1igz",
"slug": "string",
"title": "string",
"imageUrl": "string",
"priceCents": 0,
"currencyCode": "string",
"available": true,
"gtin": "string",
"mpn": "string",
"metadata": "...",
"externalId": "string",
"externalIds": ["..."]
}
]
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Unique identifier for the product.",
"idCodec": "sqid"
},
"slug": {
"type": "string",
"description": "The product's URL handle, unique within the organization. A Shopify store's sync sets it from the product's handle and follows a rename while no other product holds the new one. The product's Shopify ID is in `externalIds`."
},
"title": {
"type": "string",
"description": "The product's display name."
},
"description": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The product's long-form description."
},
"imageUrl": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "URL of the product's main image."
},
"url": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "URL of the product's page on the storefront, or null if it is not known."
},
"brand": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The product's brand, or null if it is not known. Shopping channels such as Google match reviews to a product by its brand together with a variant's `mpn`. A Shopify store's sync sets it from the product's vendor."
},
"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": {
"type": "string",
"description": "Internal notes about the product. Never shown to customers."
},
"averageRating": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"description": "The average star rating of the product's published reviews, out of 5 and rounded to two decimal places, or null if none of them has a rating."
},
"ratingCount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How many published reviews of the product have a star rating."
},
"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 product 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 product 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."
},
"reviewsCount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How many reviews the product has."
},
"wishlistsCount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How many wishlists the product is on. Counts wishlists, not items."
},
"variantsCount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How many variants the product has."
},
"tags": {
"type": "array",
"items": {
"type": "string"
},
"description": "Names of the tags applied to the product."
},
"variants": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Unique identifier for the variant.",
"idCodec": "sqid"
},
"slug": {
"type": "string",
"description": "The variant's handle, unique within its product."
},
"title": {
"type": "string",
"description": "The variant's display name, such as its size or color."
},
"imageUrl": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "URL of the variant's image."
},
"priceCents": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "The variant's stored price in cents, or null if it is not known."
},
"currencyCode": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The currency of `priceCents` as an ISO 4217 code, or null if it is not known."
},
"available": {
"anyOf": [
{
"type": "boolean"
},
{
"type": "null"
}
],
"description": "Whether the variant can be bought right now, or null if no source has said. A change from false to true is a restock, which an automation can announce; null never counts as one."
},
"gtin": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The variant's GTIN, the number under its barcode (a UPC, EAN or ISBN), or null if it is not known. It is what shopping channels such as Google match reviews to a product by. A Shopify store's sync sets it from the variant's barcode when that is a valid GTIN."
},
"mpn": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The variant's manufacturer part number, or null if it is not known. With the product's `brand`, it identifies a product that has no GTIN."
},
"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`."
},
"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."
}
},
"required": [
"id",
"slug",
"title",
"imageUrl",
"priceCents",
"currencyCode",
"available",
"gtin",
"mpn",
"metadata",
"externalId",
"externalIds"
],
"additionalProperties": false
},
"description": "The product's variants, sorted by title."
}
},
"required": [
"id",
"slug",
"title",
"description",
"imageUrl",
"url",
"brand",
"metadata",
"notes",
"averageRating",
"ratingCount",
"createdAt",
"updatedAt"
],
"additionalProperties": false,
"description": "The updated product."
}DELETE /products/{id}
Delete a product along with its variants, tags and site links. Its reviews are kept but unlinked and unpublished, unless the organization has cascadeDeleteReviews turned on. Triggers the product.deleted webhook. The product 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 products:write permission.
Path parameters
idstringrequiredThe product'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/products/A4Ws1igz" \
-H "Authorization: Bearer sk_YOUR_SECRET_KEY"import { client } from "@meisei/api/admin/client"
import { deleteProductsById } from "@meisei/api/admin"
client.setConfig({
baseUrl: "https://your-store.meisei.app/api",
headers: { Authorization: "Bearer sk_YOUR_SECRET_KEY" },
})
const { data, error } = await deleteProductsById({
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.products.delete("A4Ws1igz")from meisei import Client
meisei = Client(
secret_key="sk_YOUR_SECRET_KEY",
base_url="https://your-store.meisei.app/api",
)
result = meisei.products.delete("A4Ws1igz"){
"id": "A4Ws1igz",
"slug": "string",
"title": "string",
"description": "string",
"imageUrl": "string",
"url": "string",
"brand": "string",
"metadata": "...",
"notes": "string",
"averageRating": 0,
"ratingCount": 0,
"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"
}
],
"reviewsCount": 0,
"wishlistsCount": 0,
"variantsCount": 0,
"tags": ["string"],
"variants": [
{
"id": "A4Ws1igz",
"slug": "string",
"title": "string",
"imageUrl": "string",
"priceCents": 0,
"currencyCode": "string",
"available": true,
"gtin": "string",
"mpn": "string",
"metadata": "...",
"externalId": "string",
"externalIds": ["..."]
}
]
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Unique identifier for the product.",
"idCodec": "sqid"
},
"slug": {
"type": "string",
"description": "The product's URL handle, unique within the organization. A Shopify store's sync sets it from the product's handle and follows a rename while no other product holds the new one. The product's Shopify ID is in `externalIds`."
},
"title": {
"type": "string",
"description": "The product's display name."
},
"description": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The product's long-form description."
},
"imageUrl": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "URL of the product's main image."
},
"url": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "URL of the product's page on the storefront, or null if it is not known."
},
"brand": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The product's brand, or null if it is not known. Shopping channels such as Google match reviews to a product by its brand together with a variant's `mpn`. A Shopify store's sync sets it from the product's vendor."
},
"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": {
"type": "string",
"description": "Internal notes about the product. Never shown to customers."
},
"averageRating": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"description": "The average star rating of the product's published reviews, out of 5 and rounded to two decimal places, or null if none of them has a rating."
},
"ratingCount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How many published reviews of the product have a star rating."
},
"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 product 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 product 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."
},
"reviewsCount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How many reviews the product has."
},
"wishlistsCount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How many wishlists the product is on. Counts wishlists, not items."
},
"variantsCount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "How many variants the product has."
},
"tags": {
"type": "array",
"items": {
"type": "string"
},
"description": "Names of the tags applied to the product."
},
"variants": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Unique identifier for the variant.",
"idCodec": "sqid"
},
"slug": {
"type": "string",
"description": "The variant's handle, unique within its product."
},
"title": {
"type": "string",
"description": "The variant's display name, such as its size or color."
},
"imageUrl": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "URL of the variant's image."
},
"priceCents": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "The variant's stored price in cents, or null if it is not known."
},
"currencyCode": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The currency of `priceCents` as an ISO 4217 code, or null if it is not known."
},
"available": {
"anyOf": [
{
"type": "boolean"
},
{
"type": "null"
}
],
"description": "Whether the variant can be bought right now, or null if no source has said. A change from false to true is a restock, which an automation can announce; null never counts as one."
},
"gtin": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The variant's GTIN, the number under its barcode (a UPC, EAN or ISBN), or null if it is not known. It is what shopping channels such as Google match reviews to a product by. A Shopify store's sync sets it from the variant's barcode when that is a valid GTIN."
},
"mpn": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The variant's manufacturer part number, or null if it is not known. With the product's `brand`, it identifies a product that has no GTIN."
},
"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`."
},
"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."
}
},
"required": [
"id",
"slug",
"title",
"imageUrl",
"priceCents",
"currencyCode",
"available",
"gtin",
"mpn",
"metadata",
"externalId",
"externalIds"
],
"additionalProperties": false
},
"description": "The product's variants, sorted by title."
}
},
"required": [
"id",
"slug",
"title",
"description",
"imageUrl",
"url",
"brand",
"metadata",
"notes",
"averageRating",
"ratingCount",
"createdAt",
"updatedAt"
],
"additionalProperties": false,
"description": "The deleted product."
}PATCH /products/{productId}/variants/{variantId}
Update a variant's metadata, GTIN or manufacturer part number. Triggers the productVariant.updated webhook.
Requires the products:write permission.
Path parameters
productIdstringrequiredThe ID of the product the variant belongs to.
variantIdstringrequiredThe ID of the variant to update.
Body parameters
metadataobjectReplaces the variant's metadata wholesale. Omit to leave it alone.
gtinstring | nullThe variant's GTIN: the 8, 12, 13 or 14 digits under its barcode. Send null to clear it, or omit it to leave it alone.
mpnstring | nullThe variant's manufacturer part number. Send null to clear it, or omit it to leave it alone.
curl \
-X PATCH \
"https://your-store.meisei.app/api/products/A4Ws1igz/variants/A4Ws1igz" \
-H "Authorization: Bearer sk_YOUR_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"metadata": "...",
"gtin": "string",
"mpn": "string"
}'import { client } from "@meisei/api/admin/client"
import { patchProductsByProductIdVariantsByVariantId } from "@meisei/api/admin"
client.setConfig({
baseUrl: "https://your-store.meisei.app/api",
headers: { Authorization: "Bearer sk_YOUR_SECRET_KEY" },
})
const { data, error } = await patchProductsByProductIdVariantsByVariantId({
path: { productId: "A4Ws1igz", variantId: "A4Ws1igz" },
body: {
metadata: "...",
gtin: "string",
mpn: "string",
},
})require "meisei"
meisei = Meisei::Client.new(
secret_key: "sk_YOUR_SECRET_KEY",
base_url: "https://your-store.meisei.app/api",
)
result = meisei.products.variants.update(
"A4Ws1igz",
"A4Ws1igz",
metadata: "...",
gtin: "string",
mpn: "string",
)from meisei import Client
meisei = Client(
secret_key="sk_YOUR_SECRET_KEY",
base_url="https://your-store.meisei.app/api",
)
result = meisei.products.variants.update(
"A4Ws1igz",
"A4Ws1igz",
metadata={},
gtin="string",
mpn="string",
){
"id": "A4Ws1igz",
"slug": "string",
"title": "string",
"imageUrl": "string",
"priceCents": 0,
"currencyCode": "string",
"available": true,
"gtin": "string",
"mpn": "string",
"metadata": "...",
"externalId": "string",
"externalIds": [
{
"site": "string",
"id": "string"
}
],
"productId": "A4Ws1igz"
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Unique identifier for the variant.",
"idCodec": "sqid"
},
"slug": {
"type": "string",
"description": "The variant's handle, unique within its product."
},
"title": {
"type": "string",
"description": "The variant's display name, such as its size or color."
},
"imageUrl": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "URL of the variant's image."
},
"priceCents": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
],
"description": "The variant's stored price in cents, or null if it is not known."
},
"currencyCode": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The currency of `priceCents` as an ISO 4217 code, or null if it is not known."
},
"available": {
"anyOf": [
{
"type": "boolean"
},
{
"type": "null"
}
],
"description": "Whether the variant can be bought right now, or null if no source has said. A change from false to true is a restock, which an automation can announce; null never counts as one."
},
"gtin": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The variant's GTIN, the number under its barcode (a UPC, EAN or ISBN), or null if it is not known. It is what shopping channels such as Google match reviews to a product by. A Shopify store's sync sets it from the variant's barcode when that is a valid GTIN."
},
"mpn": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The variant's manufacturer part number, or null if it is not known. With the product's `brand`, it identifies a product that has no GTIN."
},
"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`."
},
"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."
},
"productId": {
"type": "string",
"description": "The product the variant belongs to.",
"idCodec": "sqid"
}
},
"required": [
"id",
"slug",
"title",
"imageUrl",
"priceCents",
"currencyCode",
"available",
"gtin",
"mpn",
"metadata",
"externalId",
"externalIds",
"productId"
],
"additionalProperties": false,
"description": "The updated variant."
}