API Reference

Reviews & moderation

Consumer reviews land in a moderation queue. Approve, reject, reply — or seed reviews yourself when migrating from another platform.

Endpoints

MethodEndpointDescription
GET/reviews?site_id=…&offer_id=…&status=…&q=…List reviews — filter by site, offer, and/or status (pending, approved, rejected); free-text q matches author, title, content. site/offer aliases accepted.
GET/reviews/pending-countsQueue size per site — poll this for a moderation badge
GET/reviews/:ridSingle review by id — includes criteria when per-criterion scores are stored
POST/reviewsCreate a review (migration seeding or custom forms)
PATCH/reviews/:ridEdit author, title, content, rating, city, slug
POST/reviews/:rid/approveApprove → goes live immediately
POST/reviews/:rid/rejectReject → stays stored, never renders
POST/reviews/bulkBulk moderation: {"ids": ["…"], "action": "approve|reject|delete"}
DELETE/reviews/:ridDelete permanently
Importing many reviews? Loop POST /reviews per item (or use site import, which carries reviews with their ratings and dates). /reviews/bulk is a moderation batch endpoint, not a bulk create.

The review payload

FieldTypeDescription
site_id requireduuidSite the review belongs to.
offer_id requireduuidReviewed offer.
author_name requiredstringDisplayed publicly, e.g. "Mike T.".
author_emailstringNever published. Used for confirmation mails when email confirmation is on.
titlestringHeadline above the review body.
content requiredstringThe review text.
rating requiredint 1–5Star rating. Feeds the offer's rating_average once approved.
criteria_scoresobjectOptional per-criterion scores: {"Appearance": 5, "Personality": 4, …}. Names must match the site's review.criteria exactly and, when supplied, all configured criteria are required — values are integers 1–5. Unknown names, missing criteria or out-of-range values return 422 with unknown_criteria / missing_criteria / invalid_values details (never a silent drop). Stored scores feed the per-criterion averages on the offer page, exactly like form-submitted ratings. Omit the field to store a review without criteria scores.
citystringShown next to the author — a small trust signal.
statusstringpending (default) | approved | rejected. Seed with approved when migrating.
slugstringPATCH only — editable public slug for the individual review page. Unique per site.
Create an approved review
curl -X POST $API/reviews \
  -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' \
  -d '{
    "site_id": "'$SITE_ID'",
    "offer_id": "'$OFFER_ID'",
    "author_name": "Henrik J.",
    "city": "Toronto",
    "title": "Exactly what I needed",
    "content": "Setup took ten minutes, import worked flawlessly…",
    "rating": 5,
    "status": "approved"
  }'
Seeding with per-criterion scores
# the site has review.criteria = ["Appearance", "Personality", "Value"]
# send the SAME names — ALL of them, integers 1-5
curl -X POST $API/reviews \
  -H "Authorization: Bearer ***" -H 'Content-Type: application/json' \
  -d '{
    "site_id": "'$SITE_ID'",
    "offer_id": "'$OFFER_ID'",
    "author_name": "Jane D.",
    "content": "…",
    "rating": 5,
    "status": "approved",
    "criteria_scores": {"Appearance": 5, "Personality": 4, "Value": 5}
  }'

# invalid payloads get a 422 with details, e.g.
# {"error":"criteria_scores invalid",
#  "unknown_criteria":["Swag"], "missing_criteria":["Value"],
#  "invalid_values":["Appearance"]}

Moderation workflow

Whether new reviews wait for approval is a per-site setting (settings.review.moderation — see Sites & settings). With moderation on, visitors' reviews arrive as pending and only render after approval. The same setting also controls email confirmation (review.email_confirmation) and per-criterion ratings (review.criteria list — the public form then asks for a score per criterion, and averages display on the offer page). API-seeded reviews can carry the same scores via criteria_scores on POST /reviews — they join the per-criterion averages exactly like form submissions.

Daily moderation loop
# what's waiting?
curl -s "$API/reviews?site_id=$SITE_ID&status=pending" -H "Authorization: Bearer $KEY"

# approve everything in one call
curl -X POST $API/reviews/bulk \
  -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' \
  -d '{"ids": ["uuid1", "uuid2"], "action": "approve"}'

Visitor reviews & community accounts

Every site has a built-in account system (no platform-wide login — accounts belong to one site). With review.require_account (default on), only logged-in visitors with a verified email and a confirmed 18+ date of birth can post reviews or article comments. Identity comes from the account session, so the public form drops its name/email fields — no more fake author names.

  • Reviews posted from an account carry a ✓ Registered badge wherever they render, and link back to the account's display name.
  • Unverified users can log in but see a confirm-email banner on every page until they click the link (with resend + change-email options); posting stays blocked.
  • Accounts are managed per site in the panel (Site → Community): list, ban with reason, unban. Banned users can't log in.
  • Registrations accept the Terms of Service and Privacy Policy (each site serves its own generated /terms/ and /privacy/) and consent to occasional offer emails — see Contacts.

Replies

Two kinds of replies exist: operator replies (you, answering a review — shown as an official response from the site team) and visitor replies (named guest replies, rendered with the author's name; moderated like reviews).

MethodEndpointDescription
POST/reviews/:rid/repliesOperator reply — {"content": "…"}, approved immediately
POST/repliesVisitor reply — {"site_id": "…", "review_id": "…", "author_name": "…", "content": "…", "status": "approved"}; approved publishes, pending queues
GET/replies?site_id=…&status=pendingVisitor replies queue — filter by site and status
POST/replies/:rid/approveApprove a visitor reply
POST/replies/:rid/rejectReject a visitor reply