# reviews — ratings, tags, moderation > Client seam `client/src/services/reviews/` · `USE_REVIEWS_MOCK = false` (**real**) · 8 server ops > Last verified: 2026-07-30 against commit `d3ec723` and 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](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](partner-center.md)); everything else mutates with `POST`. Note also the hardcoded nested `admin/reviews/` route segment — see [index.md](index.md#route-shape-exceptions). ## 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](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.** `IReviewModerationService` pre-screens; by default clean text returns a human-review **flag**, keeping the gate on. `Seams:ReviewModeration:AutoApproveClean` makes clean text auto-publish; `BannedWords` (default `scam`, `fraud`, `کلاهبردار`) forces `reject`. Both are mock knobs — a real classifier ignores them. - **A low rating raises a support alert** (`low_rating`), linked as `lowRatingAlertId` on the queue item. See [admin.md](admin.md). - **`unpublish` is a distinct action from `hide`** in the client's `ModerationAction` union even though both land on `hidden` — the audit trail records which was chosen. ## Shape rules the JSON does not express - **`ReviewEligibilityDto` is `{ canReview, reason }`** and the reason is a **code**, not a message: `not_completed` `already_reviewed` `not_owner` `not_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. - **`tagCodes` is on `ModerationQueueItemDto`** (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. - `moderationReason` is 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 — unlike `search/nurses`, which uses `page_size`). See [../api-contract.md](../api-contract.md#pagination). ## 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](search.md) |