Reviews

Upload review photos through the storefront bridge, submit product reviews, and vote with merchant policy enforced.

POST /reviews/media/intents

JavaScript workflow exposed as tringify.reviews.uploadMedia(file). The image bytes upload directly to the signed storage request; they do not pass through the storefront server.

Request fields

original_filename Required

Original image filename.

mime_type Required

image/jpeg or image/png.

size_bytes Required

Image size in bytes.

Validation

The intent request requires a filename, JPEG or PNG MIME type, declared byte size, a visitor session, and the current CSRF token. The browser uploads the image to the returned signed request, then the bridge finalizes and polls it. The image is virus-scanned, decoded, dimension-checked, re-encoded as JPEG, and stripped of metadata. Only the uploader may claim the returned media id.

Limits

8 MiB input; 4096 px maximum dimension; 10 uploads/minute per visitor; 10 pending per uploader; 200 MiB pending per store; unclaimed uploads expire after 2 hours.

Response

{
  success,
  data
}

the intent endpoint returns the signed request. tringify.reviews.uploadMedia(file) completes the workflow and resolves {success: true, media_id, width, height, size_bytes, expires_at}. It never rejects: a refused or failed upload resolves {success: false, message, user_errors, status_code}, where message is the store's reason (for example STOREFRONT_REVIEW_LOGIN_REQUIRED when a signed-out visitor may not review) and is empty when there is none. Show message, or your own fallback when it is empty.

POST /reviews/submit
{% form 'review_submit' %}

Gated by review settings (guest reviews, require purchase, moderation). When ctx.forms.review.guests_allowed is false, a signed-out visitor is refused: show a sign-in link instead of the form. When ctx.forms.review.purchase_required is true, only a signed-in customer who bought the product, or a shopper opening a review request from an order, can post; signed-out visitors are refused even when guests_allowed is true. Tell visitors before they write.

Form fields

product_id Required

Product being reviewed.

variant_id Optional

Specific product variant being reviewed.

rating Required

1–5.

content Required

Review body. Length bounds are merchant review settings — read them from ctx.forms.review.content.

title Optional

Optional review heading.

reviewer_name Optional

Display name; defaults to the customer's name when logged in.

reviewer_email Optional

Optional contact email; validated when supplied.

review_media_ids Optional

Comma-separated ids from review_media_upload.

Validation

Positive product and optional variant belonging to it; rating 1–5; reviewer name and optional reviewer email. Current submission and moderation settings are enforced at save, including purchase requirements, content limits, blocked content and photo limits. A private order invitation authenticates the purchased product, including for a guest. Uploaded media must belong to the same visitor and store, be unexpired and unclaimed, and contain no duplicate IDs. Photos are saved in review_media_ids order; the review and all photo attachments save together.

Limits

Merchant content minimum 10–100 characters, content maximum 500–5000, and up to max_images photos (1–5, or none when photos are off); title 200, reviewer name 100, reviewer email 255.

Response

{
  success:true,
  response,
  review_id,
  review_status
}

review_status is approved or pending according to moderation settings. Submissions the content filters refuse are not saved and return an error instead.

POST /reviews/vote
{% form 'review_vote' %}

One vote per session per review.

Form fields

review_id Required

Review being voted on.

vote Optional

helpful or unhelpful; required unless remove is true.

remove Optional

true removes the visitor's existing vote.

Validation

Existing visible review; vote is helpful or unhelpful unless remove=true. One current vote per visitor session and review.

Limits

One vote state per visitor/review; repeated writes update or remove that state.

Response

{
  success:true,
  response,
  helpful_count,
  unhelpful_count
}

User error codes#

Code

When

STORE_REVIEW_PRODUCT_REQUIRED

product_id missing — reviews always target a product.

STORE_REVIEW_RATING_RANGE

rating outside 1–5.

STORE_REVIEW_CONTENT_TOO_SHORT / _TOO_LONG

Length limits from the store's review settings — params carry min/max.

STORE_REVIEW_TITLE_TOO_LONG

Title over the limit.

STORE_REVIEW_REVIEWER_NAME_REQUIRED

Anonymous submit without reviewer_name.

STORE_REVIEW_PURCHASE_REQUIRED

The store requires a purchase and this customer has none for the product.

STORE_REVIEW_DUPLICATE_PRODUCT

This visitor already reviewed the product.

STORE_REVIEW_IMAGES_NOT_ALLOWED / _LIMIT / _INVALID

Photos off, over the store's max, or media_ids that aren't yours / already claimed / expired.

STORE_REVIEW_VOTE_INVALID

vote is not helpful/unhelpful.

STOREFRONT_REVIEW_LOGIN_REQUIRED

A signed-out visitor on a store that does not take guest reviews, from the submit and the photo upload. Read ctx.forms.review.guests_allowed and show a sign-in link instead of the form when it is false. A visitor with a review invitation from their order can still review.