Ratings & Reviews
Trust is everything in a peer-to-peer marketplace. Our rating system helps buyers find reliable sellers and helps sellers identify trustworthy buyers. We use a “simultaneous reveal” approach inspired by Airbnb — neither party sees the other’s review until both have submitted (or the deadline passes).
Why Simultaneous Reveal?
Traditional review systems have a problem: if you can see the other person’s review before writing yours, you might:
- Retaliate with a bad review because they gave you one
- Reciprocate with a good review hoping they’ll do the same
- Game the timing to see their review first
Our simultaneous reveal system prevents all of this. Reviews are hidden until both are submitted or the deadline passes, encouraging honest feedback.
Review Window Lifecycle
Every completed order gets a 7-day review window. Here’s how it works:
Order Completes
Order reaches a terminal status (completed, resolved_*, etc.)
Review Window Opens
Both parties have 7 days to submit their review
Reviews Submitted
Each party writes their review — it’s stored but not visible
Reveal Triggers
Either both submit (instant reveal) or deadline passes (auto-fill missing reviews with 5-star defaults, then reveal)
Reviews Visible
Both parties can now see each other’s reviews
Reveal Timing
| Scenario | When reviews become visible |
|---|---|
| Both submit before deadline | Immediately after the second submission |
| Deadline passes, both submitted | At the deadline |
| Deadline passes, one submitted | At the deadline — missing review auto-filled with 5-star default |
| Deadline passes, neither submitted | At the deadline — both reviews auto-filled with 5-star defaults |
Auto-submitted reviews: When the deadline passes and a party hasn’t submitted their review, we automatically create a 5-star review on their behalf. This ensures both parties always receive feedback and prevents gaming by withholding reviews. Auto-generated reviews are marked with isAutoSubmitted: true in the database so they can be distinguished from genuine submissions.
Auto-Submission Defaults
When a review is auto-generated, we use these default values:
| Party | Auto-generated values |
|---|---|
| Buyer (reviewing seller) | Accuracy: 5, Packaging: 5, Communication: 5 (no comment) |
| Seller (reviewing buyer) | Rating: 5 (no comment) |
The 5-star default assumes good faith — if neither party complained, the transaction presumably went well.
Background Processing
Expired review windows are processed by a background job:
- EventBridge Scheduler triggers an hourly message to the
process-expired-reviewsSQS queue - The handler queries for reviews where
deadline < nowandrevealedAt = null - For each expired review,
autoCompleteAndReveal()is called - Missing reviews are filled with defaults using an atomic MongoDB
$ifNulloperation - Reviews are revealed and rating stats are updated
This ensures reviews are processed within an hour of their deadline, even under high load.
What Gets Reviewed
Buyers and sellers rate different things, reflecting their different roles in the transaction.
Buyer Reviews Seller
Buyers rate sellers on three dimensions:
| Category | What it measures | Scale |
|---|---|---|
| Accuracy | Did the item match the listing? | 1-5 stars |
| Packaging | Was it packaged well? | 1-5 stars |
| Communication | Was the seller responsive? | 1-5 stars |
Plus an optional text comment (max 1,000 characters).
The seller’s overall rating is the average of these three categories.
The Review Flow
Both parties follow the same basic flow — rate, optionally comment, submit, then wait for the other party (or the deadline):
The key difference is what each party rates:
- Buyers rate three categories (accuracy, packaging, communication) + optional comment
- Sellers rate one overall score + optional comment
Once both submit (or the 7-day deadline passes with missing reviews auto-filled), reviews are revealed simultaneously.
Rating Aggregation
When reviews are revealed, we update the user’s aggregated rating stats incrementally:
- Seller stats: We add the new accuracy, packaging, and communication ratings to their running sums and recalculate averages. The overall score is the average of the three category averages.
- Buyer stats: We add the new rating to the running sum and recalculate the average.
The UserRatingStats Model
Here’s what we store for each user:
interface UserRatingStats {
userId: string
seller?: {
accuracy: { sum: number; count: number; avg: number }
packaging: { sum: number; count: number; avg: number }
communication: { sum: number; count: number; avg: number }
overall: { sum: number; count: number; avg: number }
}
buyer?: {
sum: number
count: number
avg: number
}
lastUpdatedAt: Date
}We store both sum and count so we can incrementally update the average without needing to recalculate from all historical reviews.
Which Orders Get Review Windows?
Not all orders can be reviewed. Review windows are created only for orders that actually completed (items changed hands):
Can Be Reviewed
| Status | Why reviewable |
|---|---|
completed | Normal successful order |
resolved_full_refund | Dispute ended, but items were involved |
resolved_partial_refund | Dispute ended with partial resolution |
resolved_no_refund | Dispute ended, seller kept funds |
resolved_return | Item was returned |
Displaying Ratings
On Seller Profiles
When someone views a seller’s profile, they see:
| Display | Calculation |
|---|---|
| Overall Rating | Average of the three category averages |
| Total Reviews | Number of reviews received |
| Breakdown | Individual averages for accuracy, packaging, communication |
Example: “4.8 stars (234 reviews) — Accuracy: 4.9, Packaging: 4.7, Communication: 4.8”
On Buyer Profiles
Buyers have simpler stats:
| Display | Calculation |
|---|---|
| Buyer Rating | Single average rating |
| Total Reviews | Number of reviews from sellers |
Example: “4.9 stars (42 reviews)“
Edge Cases
| Scenario | What happens |
|---|---|
| User submits twice | Error: “Already submitted” |
| Submit after deadline | Error: “Review window closed” |
| Edit after submission | Not allowed — reviews are final |
| Order disputed after review submitted | Reviews still valid (separate concerns) |
| User account deleted | Reviews remain (anonymized as “Deleted User”) |
Reviews are final. Once submitted, a review cannot be edited or deleted. This prevents people from changing their review in response to seeing the other party’s feedback.
API Endpoints
| Endpoint | Purpose |
|---|---|
rating.getOrderReview | Get review window status (submitted? revealed? deadline?) |
rating.submitBuyerReview | Submit buyer’s 3-category review |
rating.submitSellerReview | Submit seller’s single rating |
rating.getUserRatingStats | Get aggregated ratings for a user |
rating.getUserReviews | List revealed reviews for a user |
Code Locations
| Component | Location |
|---|---|
| OrderReview Model | packages/core/db/src/mongo/marketplace/order-review.model.ts |
| UserRatingStats Model | packages/core/db/src/mongo/marketplace/user-rating-stats.model.ts |
| Rating Service | packages/backend/domains/rating/src/rating.service.ts |
| Aggregation Service | packages/backend/domains/rating/src/rating-aggregation.service.ts |
| Expired Reviews Handler | packages/backend/domains/rating/src/process-expired-reviews.handler.ts |
| Rating Subscriber | packages/backend/domains/rating/src/rating.subscriber.ts |
| Queue Config | packages/backend/queue/src/config.ts (process-expired-reviews) |
| Rating Contracts | packages/core/api-dtos/src/lib/marketplace/rating/ |