Reviews

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

To call them from your own server instead, send a secret key as a bearer token and leave apiPublishableKey out. See Calling the widget API from a server.

GET /widgets/reviews

Get paginated published reviews, either for one product or, with no product slug, from across the whole store. Send the customer trio for a signed-in shopper and reviewForm says whether they can write a review here.

Query parameters

apiPublishableKeystring

Your organization's publishable key, from Settings, API keys. Leave it out when the request carries a secret key as a bearer token instead, which is how a server calls these endpoints.

productSlugstring

Slug of the product whose reviews to list. Leave it out to list published reviews from across the whole store, which is what a store-wide carousel shows.

productExternalIdstring

The product's ID on one of your sites, such as its Shopify product ID, sent instead of productSlug. Either Shopify form works.

sitestring

The slug of the site productExternalId belongs to. Only needed when the same ID is on more than one of your sites.

limitnumber

How many reviews to return.

cursorstring

The nextCursor from the previous page.

sortnewest | highest | lowest | helpful

Order of the review list: newest first, highest or lowest rating first, or most helpful votes first. Defaults to newest, for one product and across the whole store alike.

ratinginteger

Show only reviews with this star rating. The rating breakdown still covers them all.

minRatinginteger

Show only reviews rated this many stars or higher. The rating breakdown covers them all.

voterTokenstring

The visitor's vote token, used to mark the reviews they have already voted on.

customerEmailstring (email)

The signed-in shopper's address. Leave it out for a shopper who is not signed in.

timestampstring

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

signaturestring

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

Request
curl \
  "https://your-store.meisei.app/api/widgets/reviews?apiPublishableKey=pk_YOUR_PUBLISHABLE_KEY"
Response
{
  "product": {
    "slug": "string",
    "title": "string",
    "imageUrl": "string",
    "averageRating": 0,
    "totalReviews": 0,
    "ratingBreakdown": "..."
  },
  "summary": {
    "text": "string",
    "themes": [
      {
        "label": "...",
        "sentiment": "...",
        "reviewCount": "..."
      }
    ],
    "language": "string",
    "reviewCount": 0,
    "generatedAt": "string"
  },
  "reviewAnswers": {
    "enabled": true,
    "suggestedQuestions": ["string"]
  },
  "reviewForm": {
    "enabled": true,
    "reason": "signInRequired"
  },
  "questions": [
    {
      "id": "A4Ws1igz",
      "label": "string",
      "type": "scale",
      "required": true,
      "steps": 0,
      "lowLabel": "string",
      "highLabel": "string",
      "middleLabel": "string",
      "multiple": true,
      "options": ["..."]
    }
  ],
  "questionSummary": [
    {
      "id": "A4Ws1igz",
      "label": "string",
      "type": "scale",
      "answered": 0,
      "averageStep": 0,
      "steps": 0,
      "lowLabel": "string",
      "highLabel": "string",
      "middleLabel": "string",
      "options": ["..."]
    }
  ],
  "reviewStatement": {
    "text": "string",
    "url": "string"
  },
  "reviews": [
    {
      "id": "A4Ws1igz",
      "rating": 0,
      "title": "string",
      "body": "string",
      "createdAt": "string",
      "verifiedBuyer": true,
      "incentivized": true,
      "connectedReviewer": true,
      "files": ["..."],
      "helpfulCount": 0,
      "notHelpfulCount": 0,
      "viewerVote": true,
      "response": {
        "body": "...",
        "responderName": "...",
        "createdAt": "..."
      },
      "answers": ["..."],
      "customer": {
        "firstName": "...",
        "lastInitial": "...",
        "country": "..."
      },
      "product": {
        "slug": "...",
        "title": "...",
        "url": "..."
      }
    }
  ],
  "nextCursor": "string"
}

POST /widgets/reviews

Submit a review of a product from the storefront. The review goes into moderation like any other: published in the answer says whether it is live already, which only happens when the store auto-publishes reviews at that rating. A review from a signed-in shopper (the customer trio) with an order for the product that was not canceled is marked as a verified buyer; a guest's review, identified by the email in the body, never is. When the store only accepts reviews from verified buyers, anyone else gets a 403 whose message says why, and reviewForm on GET /widgets/reviews tells the widget beforehand. It is rate limited per IP address.

Query parameters

apiPublishableKeystring

Your organization's publishable key, from Settings, API keys. Leave it out when the request carries a secret key as a bearer token instead, which is how a server calls these endpoints.

customerEmailstring (email)

The signed-in shopper's address. Leave it out for a shopper who is not signed in.

timestampstring

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

signaturestring

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

Body parameters

productSlugstring

Slug of the product being reviewed. Send this or productExternalId.

productExternalIdstring

The product's ID on one of your sites, such as its Shopify product ID, sent instead of productSlug. Either Shopify form works.

sitestring

The slug of the site productExternalId belongs to. Only needed when the same ID is on more than one of your sites.

ratingintegerrequired

The star rating, from 1 to 5.

titlestring | null

The review's headline.

bodystring | null

The review itself.

reviewerNamestring | null

The reviewer's name. Only the first name and the last initial are ever shown, the same as every other review.

emailstring (email)

The reviewer's email address. It is never shown on the storefront: it identifies the reviewer for moderation, and stops the same person reviewing one product twice. Ignored when the shopper is signed in, because the signed email is used instead.

fileKeysstring[]

Keys from POST /widgets/reviews/upload-url for the photos and videos to attach, up to five.

renderedAtnumber

Unix time in milliseconds when the form was opened, used to measure time to submit. A review that comes back instantly is flagged for a person to look at.

honeypotFilledboolean

True when the form's hidden trap field was filled in, which only a bot does.

answersobject[]

Answers to the extra questions this product's form asked, from questions on GET /widgets/reviews. Every question marked required has to be answered.

Request
curl \
  -X POST \
  "https://your-store.meisei.app/api/widgets/reviews?apiPublishableKey=pk_YOUR_PUBLISHABLE_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "productSlug": "string",
  "productExternalId": "string",
  "site": "string",
  "rating": 0,
  "title": "string",
  "body": "string",
  "reviewerName": "string",
  "email": "user@example.com",
  "fileKeys": [
    "string"
  ],
  "renderedAt": 0,
  "honeypotFilled": true,
  "answers": [
    {
      "questionId": "string",
      "value": {
        "step": "..."
      }
    }
  ]
}'
Response
{
  "id": "A4Ws1igz",
  "published": true
}

GET /widgets/reviews/media

Get paginated photos and videos from published reviews, either for one product or, with no product slug, from across the whole store

Query parameters

apiPublishableKeystring

Your organization's publishable key, from Settings, API keys. Leave it out when the request carries a secret key as a bearer token instead, which is how a server calls these endpoints.

productSlugstring

Slug of the product whose photos and videos to show. Leave it out for a wall of media from across the whole store.

productExternalIdstring

The product's ID on one of your sites, such as its Shopify product ID, sent instead of productSlug. Either Shopify form works.

sitestring

The slug of the site productExternalId belongs to. Only needed when the same ID is on more than one of your sites.

limitnumber

How many media items to return. A review's own photos and videos are never split across pages, so a page can come back a little over this.

cursorstring

The nextCursor from the previous page.

minRatinginteger

Show only media from reviews rated this many stars or higher.

Request
curl \
  "https://your-store.meisei.app/api/widgets/reviews/media?apiPublishableKey=pk_YOUR_PUBLISHABLE_KEY"
Response
{
  "media": [
    {
      "url": "string",
      "contentType": "string",
      "review": {
        "id": "...",
        "rating": "...",
        "title": "...",
        "excerpt": "...",
        "reviewerName": "...",
        "verifiedBuyer": "...",
        "incentivized": "...",
        "connectedReviewer": "...",
        "createdAt": "..."
      },
      "product": {
        "slug": "...",
        "title": "...",
        "url": "..."
      }
    }
  ],
  "nextCursor": "string"
}

POST /widgets/reviews/{id}/vote

Vote on whether a review was helpful. Voting again with the same voterToken replaces the earlier vote. It is rate limited per IP address.

Path parameters

idstringrequired

The ID of the review being voted on.

Query parameters

apiPublishableKeystring

Your organization's publishable key, from Settings, API keys. Leave it out when the request carries a secret key as a bearer token instead, which is how a server calls these endpoints.

Body parameters

helpfulbooleanrequired

True marks the review helpful, false marks it not helpful.

voterTokenstringrequired

Random id identifying the visitor's browser. Voting again with the same token replaces the earlier vote.

Request
curl \
  -X POST \
  "https://your-store.meisei.app/api/widgets/reviews/A4Ws1igz/vote?apiPublishableKey=pk_YOUR_PUBLISHABLE_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "helpful": true,
  "voterToken": "string"
}'
Response
{
  "reviewId": "A4Ws1igz",
  "helpful": true,
  "helpfulCount": 0,
  "notHelpfulCount": 0
}

POST /widgets/reviews/{id}/reports

Report a published review as fake, abusive or otherwise wrong, with one of five reasons and an optional note. The review is flagged for the store's staff to look at and stays published until a person decides. Reporting the same review again from the same IP address replaces the earlier report rather than adding another. It is rate limited per IP address.

Path parameters

idstringrequired

The ID of the review being reported.

Query parameters

apiPublishableKeystring

Your organization's publishable key, from Settings, API keys. Leave it out when the request carries a secret key as a bearer token instead, which is how a server calls these endpoints.

Body parameters

reasonfake | offensive | private_information | not_about_product | otherrequired

Why the shopper reported the review: fake (looks fake or not from a real customer), offensive (offensive or abusive), private_information (shares someone's private information), not_about_product (not about this product) or other (something else).

notestring | null

Anything the shopper wants to add in their own words, up to 1,000 characters. Shown to the store's staff beside the reason, never on the storefront.

Request
curl \
  -X POST \
  "https://your-store.meisei.app/api/widgets/reviews/A4Ws1igz/reports?apiPublishableKey=pk_YOUR_PUBLISHABLE_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "reason": "fake",
  "note": "string"
}'
Response
{
  "reviewId": "A4Ws1igz",
  "reason": "fake"
}

POST /widgets/reviews/upload-url

Get a short-lived link for uploading one photo or video to attach to a storefront review. PUT the file's bytes to uploadUrl, then send key in the review's fileKeys. It is rate limited per IP address.

Query parameters

apiPublishableKeystring

Your organization's publishable key, from Settings, API keys. Leave it out when the request carries a secret key as a bearer token instead, which is how a server calls these endpoints.

Body parameters

fileNamestringrequired

The file's name, kept for the moderation view.

contentTypeimage/jpeg | image/png | image/gif | image/webp | video/mp4 | video/quicktime | video/webmrequired

The file's MIME type. Photos and videos only.

sizeBytesnumberrequired

The file's size in bytes.

Request
curl \
  -X POST \
  "https://your-store.meisei.app/api/widgets/reviews/upload-url?apiPublishableKey=pk_YOUR_PUBLISHABLE_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "fileName": "string",
  "contentType": "image/jpeg",
  "sizeBytes": 0
}'
Response
{
  "key": "string",
  "uploadUrl": "string",
  "expiresAt": "2025-01-15T09:30:00Z"
}