Supercommerce API Docs
Store API

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 groupAuth
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 paramTypeDefaultNotes
limitint 1–10024Page size.
offsetint ≥ 00
searchstringCase-insensitive match on the business name.
sortnewest | namenewestnewest = 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 paramTypeDefaultNotes
pageint ≥ 11
limitint 1–10020Page size.
orderBynewest | oldest | stars-desc | stars-ascnewest
starsint 1–5Show 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.

On this page