4.8 KiB
reviews — ratings, tags, moderation
Client seam
client/src/services/reviews/·USE_REVIEWS_MOCK = false(real) · 8 server ops Last verified: 2026-07-30 against commitd3ec723and swagger.v1.json (2026-07-29).
Customer reviews of a completed booking, pre-screened and then human-moderated before they are public. Clinical care records are a different domain — patient-records.md.
Endpoints
| Method | Path | Auth | Verdict |
|---|---|---|---|
| GET | /api/v1/bookings/{bookingId}/review_eligibility |
[Authorize] |
wired |
| GET | /api/v1/bookings/{bookingId}/my_review |
[Authorize] |
wired |
| POST | /api/v1/bookings/{bookingId}/review |
[Authorize] |
wired |
| GET | /api/v1/nurses/{nurseProfileId}/reviews |
anonymous | wired · paginated (page/pageSize) |
| GET | /api/v1/nurses/{nurseProfileId}/review_tags |
anonymous | unwired — the client reads tags off the review rows |
| PATCH | /api/v1/reviews/{reviewId}/status |
[Authorize] |
wired — the moderation decision |
| POST | /api/v1/reviews/{reviewId}/tags |
[Authorize] |
unwired — no console screen edits tags |
| GET | /api/v1/admin/reviews/moderation_queue |
admin | wired · paginated |
No phantoms. Note PATCH — one of only two PATCH verbs in the whole API (the other is on
partner-center.md); everything else mutates with POST. Note also the hardcoded
nested admin/reviews/ route segment — see index.md.
Aggregates are recomputed, never incremented
averageRating, totalReviews and totalCompletedBookings on the nurse profile are recomputed from
source whenever a review's moderation status changes. Hiding a published review must lower the average;
an incrementing counter cannot do that correctly. Never += 1 a review aggregate. See
profiles.md.
The moderation gate
submit ──AI pre-screen──▸ pending_moderation ──admin──▸ published
│ │
banned word hit unpublish ──▸ hidden
▼
rejected
- A review is not public on submit.
IReviewModerationServicepre-screens; by default clean text returns a human-review flag, keeping the gate on.Seams:ReviewModeration:AutoApproveCleanmakes clean text auto-publish;BannedWords(defaultscam,fraud,کلاهبردار) forcesreject. Both are mock knobs — a real classifier ignores them. - A low rating raises a support alert (
low_rating), linked aslowRatingAlertIdon the queue item. See admin.md. unpublishis a distinct action fromhidein the client'sModerationActionunion even though both land onhidden— the audit trail records which was chosen.
Shape rules the JSON does not express
ReviewEligibilityDtois{ canReview, reason }and the reason is a code, not a message:not_completedalready_reviewednot_ownernot_found. The client maps it to an i18n key. Eligibility is server-decided — the client must not infer it from booking status.- The author is masked (REQ-026, confirmed): a public review carries
authorMasked, never a full name. This is a privacy decision, not a display choice. tagCodesis onModerationQueueItemDto(REQ-037, delivered) so the queue shows what the reviewer tagged without a second fetch.- Review tags are codes; labels are i18n keys. Never render a tag code, and never build a display string from one.
moderationReasonis admin-facing free text and is not returned on the public review read.- The public reviews list is anonymous and paginated with
page/pageSize(camelCase — unlikesearch/nurses, which usespage_size). See ../api-contract.md.
Enums
| Vocabulary | Values |
|---|---|
ModerationStatus |
pending_moderation published hidden rejected |
ModerationAction |
publish hide reject unpublish |
ReviewIneligibilityReason |
not_completed already_reviewed not_owner not_found |
ModerationStatus and ModerationAction are both defined in
Entities/Reviews/ReviewModerationStatus.cs and verified identical to the client's unions — the file
carries the four states and the four actions together.
Open REQs
| REQ | Status | Effect |
|---|---|---|
| REQ-026 | delivered | Eligibility + my-review-for-booking reads, and masked-author confirmation |
| REQ-037 | delivered | tagCodes on the moderation queue item |
| REQ-040 | open | No topReviewTag on the search index row — the C2 card renders without the tag chip. Owned by search.md |