Supercommerce API Docs
Store API

Frequently Bought Together Module — Storefront

Public, anonymous storefront surface for Frequently-Bought-Together (FBT) recommendations — the "Customers also bought" carousel on the product detail page (PDP) and the cart.…

Public, anonymous storefront surface for Frequently-Bought-Together (FBT) recommendations — the "Customers also bought" carousel on the product detail page (PDP) and the cart. Both endpoints read a precomputed pair set built offline by the admin/cron rebuild pipeline (see ../admin/fbt.md).

Source: api-modules/fbt/src/controllers/storefront-fbt.controller.ts.

No auth guards: the recommendation set is identical for every visitor and the kill switch (fbt.enabled setting) sits inside the service. When FBT is disabled, or when an anchor has no mined signal (long-tail / brand-new product), the endpoint returns an empty products array — there is no fallback to similar-products or top-sellers, by design. The frontend hides the section when the array is empty.


Conventions

Authentication

Endpoint groupAuth
GET /store/products/:productId/fbt, POST /store/cart/fbtnone (public)

Response envelope

{
  "data": { "products": [ /* FbtProduct[] */ ] },
  "message": "Success",
  "statusCode": 200
}

Error envelope

statusCodeerrorCode examples
400VALIDATION_ERROR (bad limit, empty/oversized productIds)
500INTERNAL_SERVER_ERROR, DATABASE_ERROR

Currency

priceStart / priceEnd on FbtProduct are integer subunits (paise / cents). The frontend formats.


Domain types

FbtProduct

A slim product summary tuned for recommendation tiles — intentionally not the full product-search shape (no variant matrix, attributes, or facets; just enough to render a card and link to the PDP).

type FbtProduct = {
  id: string;
  slug: string;
  title: string;
  thumbnail: string | null;
  priceStart: number | null;            // integer subunits
  priceEnd: number | null;              // integer subunits (range for multi-variant)
  inStock: boolean;
  hasActiveSpecial: boolean;
};

FbtResponse

type FbtResponse = { products: FbtProduct[] };   // empty ⇒ hide the section

Endpoints

Base path: /store.

GET /store/products/:productId/fbt — PDP recommendations (single anchor)

Returns up to limit related products for one anchor, ranked by stored confidence. Empty array when there's no FBT signal for this anchor.

Path params

NameNotes
productIdThe anchor product id (the PDP being viewed)

Query

NameTypeDefaultConstraints
limitint41..10

Response 200FbtResponse.


POST /store/cart/fbt — Cart recommendations (multi-anchor)

Multi-anchor lookup over the customer's cart contents. Returns products co-purchased with the cart items, ranked by summed confidence across anchors and deduped against the input ids.

Body

{ "productIds": ["01J9...", "01J9..."], "limit": 4 }
FieldTypeDefaultConstraints
productIdsstring[]1..50 ids (bounded server-side to cap query fan-out)
limitint?41..10

Response 200FbtResponse (input product ids excluded from results).

Errors

StatusCodeWhen
400VALIDATION_ERROREmpty productIds, more than 50 ids, or limit out of range

  • fbt (admin) — builds and refreshes the pair set this surface reads. See ../admin/fbt.md.
  • catalogFbtProduct fields (title, slug, thumbnail, price, special) are hydrated from catalog products/variants at retrieval time.
  • inventory — drives the inStock flag.
  • settings — the fbt.enabled kill switch makes both endpoints return an empty list when off.

On this page