Skip to content

Product reviews

Customers leave a star rating (and optionally a headline + body) on a product page. Submissions are moderated — created pending, published by an operator — then the published set + a rating aggregate read back for the PDP. Examples reuse the api() helper.

Submit a review (public)

POST /api/v1/public/items/:itemId/reviews — tenant header, no auth (public, rate-limited).

await api(`/public/items/${itemId}/reviews`, {
  method: "POST",
  body: JSON.stringify({
    rating: 5,                       // required, integer 1–5
    authorName: "Jordan P.",         // required, 1–80 — shown on the storefront
    authorEmail: "dana@example.com", // required, ≤320 — dedupe + verified-purchase match only
    title: "Exactly as described",   // optional, ≤120
    body: "Held up on a week-long trip.", // optional, ≤4000
  }),
});
// → { ok: true }

The review is created pending and won't appear in the list until an operator publishes it (moderation). One review per email per item — re-submitting the same authorEmail for the same item is rejected as a duplicate.

authorEmail is never returned to public callers. It's used only to de-dupe and to match the email against an order for the verified-purchase badge — published reviews expose authorName alone.

Read published reviews (public)

GET /api/v1/public/items/:itemId/reviews — returns the published reviews plus the rating aggregate, cursor-paginated.

const res = await api(`/public/items/${itemId}/reviews?limit=20`);
// → {
//   reviews: [{ id, rating, title, body, authorName, verifiedPurchase, createdAt }],
//   nextCursor: string | null,
//   ratingAverage: number | null,   // null when there are no published reviews
//   ratingCount: number,
// }

ratingAverage is null (not 0) when nothing's published yet, so the PDP can distinguish "no reviews" from "rated 0." Pass cursor (the prior nextCursor) to page.

Aggregate on catalog reads

You don't need to call this endpoint just to show stars on a listing: the public catalog item reads (and collection reads) already include ratingAverage + ratingCount per item, so a product-list or collection page can render rating summaries from a single read.