Supercommerce API Docs
Vendor API

Reviews Module — Vendor surface

Vendor-facing HTTP surface for moderating reviews on the vendor's own products — listing, editing content, approving/rejecting, marking/unmarking spam, and soft-deleting. List is…

Vendor-facing HTTP surface for moderating reviews on the vendor's own products — listing, editing content, approving/rejecting, marking/unmarking spam, and soft-deleting. List is always allowed; every write action is gated by a corresponding admin.reviews.allow_vendor_* platform setting. Stars and author identity are intentionally not editable by vendors (rating-integrity / honest-attribution policy).

Source: api-modules/reviews/src/controllers/vendor-reviews.controller.ts.


Conventions

Authentication

All endpoints require a Better-Auth bearer session with an active vendor.

Authorization: Bearer <session-token>

The active vendor is resolved via resolveActiveVendorId(session). Vendor scope is always read from the session — never accepted from the client.

Tenant scoping

Every read and write scopes to reviews whose product.vendorId matches the active vendor. Cross-vendor review ids return 404 Not Found (no leak of foreign ids). Soft-deleted reviews are hidden from the vendor list entirely (no includeDeleted toggle — that's an admin concern).

Spam reviews are also hidden from the vendor list unless the platform has set admin.reviews.allow_vendor_show_spam. The rationale: an admin's spam classification shouldn't re-surface in the vendor's UI by default — abusive content the platform decided to suppress stays suppressed for the vendor too.

Platform-gated writes

Every mutating endpoint is gated by a setting in the admin.reviews.* namespace (consult settings.md). When the relevant flag is off the endpoint returns 403 Forbidden with the message "Vendor <action> disabled by platform configuration". The flags are enforced inside ReviewsService — the controller stays declarative.

EndpointSetting
PATCH /vendor/reviews/:idadmin.reviews.allow_vendor_edit
POST /vendor/reviews/:id/approveadmin.reviews.allow_vendor_approve
POST /vendor/reviews/:id/rejectadmin.reviews.allow_vendor_reject
POST /vendor/reviews/:id/mark-spam, /unmark-spamadmin.reviews.allow_vendor_mark_spam
DELETE /vendor/reviews/:idadmin.reviews.allow_vendor_delete

Response envelope

{
  "data": <payload>,
  "message": "Success",
  "statusCode": 200,
  "metadata": { /* optional, e.g. pagination */ }
}

Error envelope

statusCodeerrorCode examples
400BAD_REQUEST, VALIDATION_ERROR
401UNAUTHORIZED
403FORBIDDEN (vendor action disabled by platform)
404NOT_FOUND
500INTERNAL_SERVER_ERROR

Domain types

ReviewStatus

type ReviewStatus = "pending" | "approved" | "rejected";

ReviewResponse

type ReviewImageResponse = {
  id: string;
  url: string;
  sortOrder: number;
};

type ReviewResponse = {
  id: string;
  productId: string;
  userId: string | null;
  authorFirstName: string | null;
  authorLastName: string | null;
  title: string | null;
  content: string;
  stars: number;                          // 1..5not editable by vendors
  recommended: boolean | null;
  isVerifiedPurchase: boolean;
  isSpam: boolean;
  status: ReviewStatus;
  approvedAt: string | null;              // ISO
  approvedBy: string | null;
  rejectedAt: string | null;
  rejectedBy: string | null;
  createdBy: string | null;
  createdAt: string;
  updatedAt: string;
  deletedAt: string | null;
  images: ReviewImageResponse[];
};

Reviews

Base path: /vendor/reviews. :id is review.id.

GET /vendor/reviews — List reviews on my products

Returns reviews whose product.vendorId matches the active vendor. Soft-deleted rows are excluded; spam is excluded unless admin.reviews.allow_vendor_show_spam is on.

Query

NameTypeDefaultNotes
productIdstring?Filter to one product
status"pending" | "approved" | "rejected"?Filter by moderation status
pageint1>= 1
limitintplatform default>= 1, capped by MAX_REVIEW_PAGE_SIZE
orderBy"newest" | "oldest" | "stars-desc" | "stars-asc"?newestSort order

Response 200 — paginated envelope of ReviewResponse.

{
  "data": [ /* ReviewResponse[] */ ],
  "metadata": {
    "total": 124,
    "items": 20,
    "perPage": 20,
    "currentPage": 1,
    "lastPage": 7
  }
}

PATCH /vendor/reviews/:id — Edit content fields

Editable fields are intentionally narrow: title, content, recommended. Stars and author identity are not editable by vendors.

Body

{
  "title": "Updated title",                // nullable; 1..200 chars trimmed
  "content": "Updated content body",       // optional; 1..5000 chars trimmed
  "recommended": true                      // nullable
}
FieldTypeConstraints
titlestring | null?Trimmed; 1..200 chars when set
contentstring?Trimmed; 1..5000 chars
recommendedboolean | null?

Response 200 — updated ReviewResponse.

Errors

StatusCodeWhen
404NOT_FOUNDReview not on a product owned by active vendor
403FORBIDDENadmin.reviews.allow_vendor_edit is off
400VALIDATION_ERRORBody fails zod

POST /vendor/reviews/:id/approve — Approve

Transitions status → "approved" and stamps approvedAt / approvedBy.

Body — empty.

Response 200 — updated ReviewResponse.

Errors

StatusCodeWhen
404NOT_FOUNDReview not on a product owned by active vendor
403FORBIDDENadmin.reviews.allow_vendor_approve is off

POST /vendor/reviews/:id/reject — Reject

Transitions status → "rejected" and stamps rejectedAt / rejectedBy.

Body — empty.

Response 200 — updated ReviewResponse.

Errors

StatusCodeWhen
404NOT_FOUNDReview not on a product owned by active vendor
403FORBIDDENadmin.reviews.allow_vendor_reject is off

POST /vendor/reviews/:id/mark-spam — Flag as spam

Sets isSpam = true. The review remains in the underlying table but is hidden from the storefront and (by default) from the vendor list.

Body — empty.

Response 200 — updated ReviewResponse with isSpam: true.

Errors

StatusCodeWhen
404NOT_FOUNDReview not on a product owned by active vendor
403FORBIDDENadmin.reviews.allow_vendor_mark_spam is off

POST /vendor/reviews/:id/unmark-spam — Clear spam flag

Sets isSpam = false.

Body — empty.

Response 200 — updated ReviewResponse with isSpam: false.

Errors

StatusCodeWhen
404NOT_FOUNDReview not on a product owned by active vendor
403FORBIDDENadmin.reviews.allow_vendor_mark_spam is off

DELETE /vendor/reviews/:id — Soft delete

Sets deletedAt. Soft-deleted reviews disappear from the vendor list and the storefront. Admins retain visibility (and may restore via the admin surface).

Response 200 — updated ReviewResponse (now carrying deletedAt).

Errors

StatusCodeWhen
404NOT_FOUNDReview not on a product owned by active vendor
403FORBIDDENadmin.reviews.allow_vendor_delete is off

  • settingsadmin.reviews.allow_vendor_* toggles live here. See settings.md.
  • catalog — review-to-product ownership goes through product.vendorId.
  • storage — review images are stored in object storage; only the public URL surfaces on the API.

On this page