Skip to content

SRS — Reviews & Ratings

Module owner: Domain 8 — Experience. Reviews are Yam3at's trust engine: only customers with a Completed booking can review, which makes every star on a vendor profile a verified transaction — the platform's core differentiation over Instagram vendor-shopping.

Related: Booking · Admin & CMS · Vendor profiles (Listings module)

Purpose

Convert completed bookings into credible, fraud-resistant social proof that powers vendor ranking, customer decisions and the compare view — while giving vendors a fair right of reply and admins a workable moderation pipeline.

Actors

Actor Role
Customer Writes/edits one review per completed booking; reports abusive replies
Vendor Reads reviews, posts one public reply per review, reports abusive reviews
Admin (support/ops) Moderates the queue, resolves reports, hides/restores reviews
System Opens review windows, recomputes rating aggregates, auto-screens content

User stories

  • As a customer, I want to rate my photographer on quality, punctuality and value with photos, so that the next bride knows what she's actually getting.
  • As a customer, I want to fix a typo in my review within a few days, so that my honest words stay accurate.
  • As a vendor, I want to reply publicly to a review, so that my side of a bad experience is visible.
  • As a vendor, I want reviews only from real completed bookings, so that a competitor can't torch my rating with fakes.
  • As an admin, I want a moderation queue with report reasons and booking context, so that I resolve disputes about reviews quickly and consistently.

Functional requirements

Creation & editing — MVP

ID Requirement Phase
SRS-REV-001 Review window opens when a booking reaches completed (SRS-BKG-017) and stays open 90 days. Exactly one review per booking. Invitation via review.invite notification + persistent CTA on the booking and event dashboard. MVP
SRS-REV-002 Review composition: overall_rating 1–5 (required, integer stars), optional criteria ratings 1–5 each — quality, punctuality, value for money — optional title, body, and up to 5 photos. MVP
SRS-REV-003 Reviews publish immediately (post-moderation model) unless auto-screening flags them (blocked words, contact info, links) — flagged reviews go to the moderation queue in pending_moderation and show to the author only as "under review". MVP
SRS-REV-004 Edit window: author may edit or delete their review within 7 days of publishing (edits re-run auto-screening; "edited" label shown). After 7 days the review is locked; deletion after the window requires a support request. MVP
SRS-REV-005 Vendor public reply: one reply per review (text only, ≤ 1,000 chars), editable by the vendor within 7 days of posting the reply, displayed beneath the review. Replies pass the same auto-screening. MVP

Moderation — MVP

ID Requirement Phase
SRS-REV-010 Report-abuse: vendors can report reviews, customers can report replies (reason enum: fake, offensive, personal info, off-topic, blackmail/extortion + note). Reports create moderation cases; a review with an open report stays visible unless the reason is personal info or offensive auto-classified (then temporarily hidden pending decision). MVP
SRS-REV-011 Moderation decisions: approve (visible, report closed), hide (removed from public, author notified with reason, counts removed from aggregates), edit-redact (admin redacts personal info only — logged, original preserved), escalate (to dispute handling). All decisions audit-logged with actor and reason. MVP
SRS-REV-012 A hidden review can be appealed once by its author within 14 days; appeal goes to a different moderator than the original decision-maker where staffing allows. MVP

Aggregation — MVP

ID Requirement Phase
SRS-REV-020 Vendor profile aggregates, recomputed transactionally on every review publish/hide/delete: rating_avg = mean of visible reviews' overall_rating, stored decimal(3,2), displayed to 1 dp; rating_count; star distribution (count per 1–5); per-criteria averages (over reviews that rated that criterion). MVP
SRS-REV-021 Display rules: vendors with < 3 reviews show "New — {n} review(s)" instead of an average (prevents a single 1-star from defining a new vendor). Search/compare ranking uses a Bayesian smoothed score: score = (v·R + m·C) / (v + m) where v = review count, R = vendor mean, C = platform-wide mean, m = 5 (damping constant, admin-tunable) — documented so ranking is explainable. MVP
SRS-REV-022 Review listing on vendor profile: sort by newest (default), highest, lowest; filter by star level and by criteria; shows reviewer first name + initial, event type, and month/year of the event (never full names or dates that identify the event precisely). MVP

Anti-fraud — MVP

ID Requirement Phase
SRS-REV-030 Hard rules: one review per booking (DB unique constraint); reviewer must be the booking's customer; no self-review — a vendor account whose owner identity (phone/email/civil-linked user id) matches the customer account is blocked at write time and flagged; reviews impossible on cancelled/refund_requested bookings. MVP
SRS-REV-031 Signals feeding a fraud score for moderator attention (not auto-removal): burst of reviews for one vendor from accounts created < 7 days ago; identical/near-duplicate bodies across accounts (shingling similarity > 0.85); reviewer and vendor sharing device fingerprint or IP ranges repeatedly; ratio of 5-star reviews from bookings with the minimum deposit that were never messaged. Score ≥ threshold → queue with "suspected fraud" tag. MVP
SRS-REV-032 Incentivized-review policy: vendors offering discounts for positive reviews (detected via messaging flags or reports) receive a strike; reviews obtained that way are hidden. Three strikes → account review by operations. MVP

Scale / Vision

ID Requirement Phase
SRS-REV-040 Review helpfulness votes ("Was this helpful?") influencing sort. Scale
SRS-REV-041 AI review summarization on vendor profiles ("What customers consistently praise/criticize"), AR/EN. Scale
SRS-REV-042 Video reviews; verified event-photo watermarking. Vision

Business rules

  1. Verified-only: no review path exists outside a completed booking — no imported reviews, no vendor-solicited external testimonials shown as reviews.
  2. Ratings are immutable history: hiding removes from aggregates and public view but never physically deletes (moderation/audit needs); author deletion within the window is a soft delete.
  3. Vendor replies never affect ratings and cannot be replied to further (no threads).
  4. Aggregates count only published (visible) reviews; pending_moderation and hidden are excluded.
  5. A refund after completion does not auto-remove a review; an admin may hide it only through the standard moderation reasons.
  6. Reviewer anonymity is partial by design (first name + initial); full anonymity is not offered because accountability deters abuse.
  7. Photos must originate from the reviewer's upload (no URL imports); EXIF stripped; faces are the reviewer's responsibility per ToS.

Validation rules

Field Rule
overall_rating required, integer 1–5
Criteria ratings optional, each integer 1–5, only defined criteria keys (quality, punctuality, value)
title optional, 3–80 chars
body optional but required if any criteria rating < 3 (context for low scores), 10–2,000 chars, no URLs, blocked-words screened
Photos ≤ 5; jpg/png/webp; ≤ 10 MB each; min 400×400 px; virus-scanned, EXIF-stripped
Vendor reply 10–1,000 chars, no URLs, no contact info
Report reason enum required, note ≤ 1,000 chars
Window checks create: booking completed and ≤ 90 days; edit: ≤ 7 days since publish; reply edit: ≤ 7 days since reply

Permissions matrix

Action Customer (booking owner) Other customer Vendor (reviewed) Other vendor Admin support Admin ops Admin super
Create review ✔ (window)
Edit/delete own review (≤7d)
Read published reviews
Post/edit public reply
Report review/reply ✔ (replies) ✔ (reviews)
Moderate (approve/hide/redact) ✔ (logged) ✔ (logged) ✔ (logged)
Tune damping constant / criteria set

State machine

stateDiagram-v2
    [*] --> Draft : customer starts (autosaved)
    Draft --> Published : passes auto-screening
    Draft --> PendingModeration : auto-screening flags
    PendingModeration --> Published : moderator approves
    PendingModeration --> Hidden : moderator hides
    Published --> Published : author edit ≤7d (re-screened)
    Published --> PendingModeration : edit flagged / report auto-hide class
    Published --> Hidden : moderation decision
    Published --> Deleted : author deletes ≤7d (soft)
    Hidden --> Published : appeal upheld / restore
    Deleted --> [*]
    Hidden --> [*]

Edge cases

  • Booking auto-completed, customer later disputes (SRS-BKG edge case): review stays writable; if a refund is granted the review persists with an internal "refunded booking" tag visible to moderators only.
  • Vendor replies, then customer edits the review: reply remains with "response to an earlier version" auto-label.
  • Customer account deleted after reviewing: review persists anonymized ("Verified customer") — aggregates unchanged.
  • Vendor suspended: reviews remain readable on the (unlisted) profile; new replies blocked.
  • Rating without body on a low score: blocked by validation (body required < 3 stars) — prevents context-free rating bombs.
  • Two moderation decisions racing: case-level lock; second decision sees resolved case.
  • 90-day window expires during a pending_moderation hold: publication proceeds normally on approval; window applies to creation, not moderation latency.
  • Same customer books the same vendor twice: two bookings → two reviews allowed (both verified transactions).

Empty / loading / error states

Surface Empty Loading Error
Vendor profile reviews tab "No reviews yet — completed bookings unlock reviews" (+ "New vendor" badge logic per SRS-REV-021) Skeleton review cards + aggregate shimmer Aggregate from cache; list retry
Review composer Photo upload progress per file Draft autosaved locally; submit failure keeps draft; per-photo retry
Moderation queue (admin) "Queue clear" Table skeleton Retry; case detail loads independently of list
My reviews (customer) "Reviews you write appear here" Skeleton rows Retry banner

Acceptance criteria

  • A review is impossible via API on a booking in any state other than completed (403/422 verified per state).
  • Unique constraint proven: concurrent double-submit on one booking yields exactly one review.
  • Aggregate math verified: seeding 4.0×10 reviews then hiding a 1.0 review updates rating_avg, count, and distribution transactionally (no window where they disagree).
  • Vendor with 2 reviews shows "New" treatment, not an average; with 3, the average appears; Bayesian score orders a 5.0×2 vendor below a 4.6×40 vendor in search.
  • Edit at day 6 succeeds and shows "edited"; edit at day 8 returns 403 with localized message.
  • Self-review attempt from a linked account is blocked and produces a fraud flag.
  • Full moderation path (report → hide → appeal → restore) works with each step in the audit log.
  • Review card, stars and reply render correctly in Arabic RTL including mixed AR/EN bodies.

Data entities touched

reviews, review_criteria_ratings, review_photos, review_replies, review_reports, review_moderation_cases, review_appeals, vendor_rating_aggregates (avg, count, distribution, criteria avgs, bayesian score), fraud_signals; reads on bookings, events (event type for display), users, vendors; writes trigger Meilisearch re-index of the vendor document.

POST   /api/v1/bookings/{bookingId}/review
GET    /api/v1/reviews/{id}
PATCH  /api/v1/reviews/{id}              # ≤ 7 days, author only
DELETE /api/v1/reviews/{id}              # ≤ 7 days, author only
GET    /api/v1/vendors/{vendorId}/reviews?sort=&stars=&page=
GET    /api/v1/vendors/{vendorId}/rating-summary
POST   /api/v1/reviews/{id}/reply        # vendor
PATCH  /api/v1/review-replies/{id}       # ≤ 7 days
POST   /api/v1/reviews/{id}/report
POST   /api/v1/review-replies/{id}/report
GET    /api/v1/me/reviews                # customer's written reviews
# admin
GET    /api/v1/admin/moderation/reviews?status=pending|reported
POST   /api/v1/admin/moderation/reviews/{caseId}/decide   # approve|hide|redact|escalate
POST   /api/v1/admin/reviews/{id}/restore