Vendors Module — Storefront
Public seller-page surface: a paginated seller directory, a vendor's public profile with its cover-image carousel and rating summary, and a paginated, filterable/sortable list of the vendor's reviews.
Public, unauthenticated surface that powers the storefront seller page: a browsable seller directory, a vendor's public profile (logo, banner, cover-image carousel, rating summary), and a paginated list of every approved review across that vendor's products. Only approved vendors — those that have a vendor_profile row — are visible, and only customer-safe fields are returned (business email/phone, tax id and bank details are never exposed).
Source:
api-modules/vendor/src/controllers/store-vendor.controller.ts(directory + profile),api-modules/vendor/src/controllers/store-vendor-reviews.controller.ts(reviews).Reviews are read through the optional
VENDOR_REVIEWS_PORT(@sc/core/ports), implemented by the reviews module. If that module is unmounted the seller page still serves: the reviews list comes back empty and the rating aggregate is zeroed.
Conventions
Authentication
| Endpoint group | Auth |
|---|---|
GET /store/vendors/** | none (public) |
Vendors are addressed by their profile slug, the SEO-friendly identifier used in the public seller-page URL.
Pagination
The directory uses the canonical offset/limit envelope; the reviews list takes a page query param and is converted to the same envelope. Both return:
{ "data": [...], "metadata": { "total": 50, "limit": 24, "offset": 0, "hasMore": true } }GET /store/vendors
Paginated seller directory.
| Query param | Type | Default | Notes |
|---|---|---|---|
limit | int 1–100 | 24 | Page size. |
offset | int ≥ 0 | 0 | |
search | string | — | Case-insensitive match on the business name. |
sort | newest | name | newest | newest = most recently created first; name = A→Z by business name. |
Each row (StoreVendorSummary):
{
"id": "vnd_123",
"name": "Acme Cosmetics",
"slug": "acme-cosmetics",
"logo": "https://cdn/.../logo.png",
"banner": "https://cdn/.../banner.png",
"businessDescription": "Clean beauty, made in small batches.",
"city": "Pune",
"country": "IN",
"rating": { "avg": 4.6, "count": 128, "distribution": { "1": 2, "2": 3, "3": 10, "4": 40, "5": 73 } }
}Rating summaries for the whole page are fetched in a single batched query — there is no per-vendor lookup.
GET /store/vendors/:slug
A vendor's public seller-page profile (StoreVendorProfile). Returns 404 if no approved vendor matches the slug.
{
"id": "vnd_123",
"name": "Acme Cosmetics",
"slug": "acme-cosmetics",
"logo": "https://cdn/.../logo.png",
"banner": "https://cdn/.../banner.png",
"businessDescription": "Clean beauty, made in small batches.",
"city": "Pune",
"state": "MH",
"country": "IN",
"coverImages": [
{ "id": "cov_1", "url": "https://cdn/.../cover-1.jpg", "sortOrder": 0 },
{ "id": "cov_2", "url": "https://cdn/.../cover-2.jpg", "sortOrder": 1 }
],
"rating": { "avg": 4.6, "count": 128, "distribution": { "1": 2, "2": 3, "3": 10, "4": 40, "5": 73 } },
"deliveredOrderCount": 842,
"approvedAt": "2022-03-04T05:06:07.000Z"
}coverImages is the seller-page carousel, in display order. It (along with logo, banner and businessDescription) is managed by the vendor (see the Vendor API → Profile Customization doc).
rating is the review summary across all of the vendor's products: count is the total approved review count, avg the mean stars (two decimals), and distribution the per-star tallies. deliveredOrderCount is the number of this seller's sub-orders that reached the delivered status — a fulfilment trust signal. Both are read through optional ports (reviews / orders); if either module is unmounted that figure is 0. approvedAt (ISO 8601) is when the seller was approved onto the platform — render the "member since" / time-on-platform from it. The assembled profile is cached for ~60s and busted immediately on vendor edits.
Vendor's products
To render the vendor's products on the seller page, call GET /store/product-search?vendors=<vendorId> (pass the id from this profile response). The Typesense product collection indexes vendorId, so this reuses the storefront's existing search sorting, pagination, and facets. See the Search doc.
GET /store/vendors/:slug/reviews
Paginated, approved reviews across all of the vendor's products. The service hard-filters to approved + non-spam + non-deleted regardless of input. Returns 404 if the slug is unknown.
| Query param | Type | Default | Notes |
|---|---|---|---|
page | int ≥ 1 | 1 | |
limit | int 1–100 | 20 | Page size. |
orderBy | newest | oldest | stars-desc | stars-asc | newest | |
stars | int 1–5 | — | Show only reviews with this exact star rating. |
Each row (StoreVendorReview) — reviewer display name only (no email/account id), no moderation columns:
{
"id": "rev_1",
"productId": "prod_9",
"reviewerName": "Jordan Lee",
"authorFirstName": "Jordan",
"authorLastName": "Lee",
"title": "Lovely texture",
"content": "Absorbs fast, no residue.",
"stars": 5,
"recommended": true,
"isVerifiedPurchase": false,
"createdAt": "2026-06-01T10:00:00.000Z",
"images": [{ "id": "img_1", "url": "https://cdn/.../r1.jpg", "sortOrder": 0 }]
}reviewerName is the reviewer's display name to render on the seller page: the customer account name for customer-submitted reviews, or the assembled author name parts for admin-entered reviews. It is null only when no name source exists. Only the name is exposed — never the reviewer's email or account id. authorFirstName/authorLastName remain populated only for admin-entered reviews; customer submissions leave those two null.
GET /store/vendors/:slug/reviews/aggregate
Rating summary across the vendor's approved reviews (StoreVendorReviewAggregate). Returns 404 if the slug is unknown.
{ "avg": 4.6, "count": 128, "distribution": { "1": 2, "2": 3, "3": 10, "4": 40, "5": 73 } }avg is rounded to two decimals; the per-star distribution counts only approved, non-spam, non-deleted reviews. Cached for ~60s.
Shipping Module — Storefront
HTTP surface for customer-side shipment tracking — the timeline of provider-emitted events for a sub-order the customer placed. Read-only.
Wishlist Module — Storefront
HTTP surface for the customer wishlist — one wishlist per customer ("My Wishlist"), keyed at the variant level. The default wishlist is lazy-materialized on first access so the…