Endpoints
| Method | Endpoint | Description |
|---|---|---|
| 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-counts | Queue size per site — poll this for a moderation badge |
| GET | /reviews/:rid | Single review by id — includes criteria when per-criterion scores are stored |
| POST | /reviews | Create a review (migration seeding or custom forms) |
| PATCH | /reviews/:rid | Edit author, title, content, rating, city, slug |
| POST | /reviews/:rid/approve | Approve → goes live immediately |
| POST | /reviews/:rid/reject | Reject → stays stored, never renders |
| POST | /reviews/bulk | Bulk moderation: {"ids": ["…"], "action": "approve|reject|delete"} |
| DELETE | /reviews/:rid | Delete permanently |
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
| Field | Type | Description |
|---|---|---|
site_id required | uuid | Site the review belongs to. |
offer_id required | uuid | Reviewed offer. |
author_name required | string | Displayed publicly, e.g. "Mike T.". |
author_email | string | Never published. Used for confirmation mails when email confirmation is on. |
title | string | Headline above the review body. |
content required | string | The review text. |
rating required | int 1–5 | Star rating. Feeds the offer's rating_average once approved. |
criteria_scores | object | Optional 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. |
city | string | Shown next to the author — a small trust signal. |
status | string | pending (default) | approved | rejected. Seed with approved when migrating. |
slug | string | PATCH only — editable public slug for the individual review page. Unique per site. |
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"
}'
# 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.
# 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).
| Method | Endpoint | Description |
|---|---|---|
| POST | /reviews/:rid/replies | Operator reply — {"content": "…"}, approved immediately |
| POST | /replies | Visitor reply — {"site_id": "…", "review_id": "…", "author_name": "…", "content": "…", "status": "approved"}; approved publishes, pending queues |
| GET | /replies?site_id=…&status=pending | Visitor replies queue — filter by site and status |
| POST | /replies/:rid/approve | Approve a visitor reply |
| POST | /replies/:rid/reject | Reject a visitor reply |