Supercommerce API Docs
Admin API

Frequently Bought Together Module — Admin

HTTP surface for operating the Frequently-Bought-Together (FBT) recommendation pipeline — an offline batch that mines product co-purchase pairs from confirmed orders and stores a…

HTTP surface for operating the Frequently-Bought-Together (FBT) recommendation pipeline — an offline batch that mines product co-purchase pairs from confirmed orders and stores a ranked related-product set per anchor. This admin surface triggers on-demand rebuilds and reads the rebuild audit log; the recommendations themselves are served by the storefront surface (see ../store/fbt.md).

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

The pipeline is normally driven by a monthly BullMQ cron (0 3 1 * * by default, overridable via the fbt settings group). This controller exposes the manual trigger and the audit log. A kill switch (fbt.enabled setting) makes both the rebuild job and the retrieval endpoints no-op when off.


Conventions

Authentication

All endpoints require a Better-Auth admin session and a role granting the matching fbt:* permission.

Endpoint groupPermission
POST /admin/fbt/rebuildfbt: rebuild
GET /admin/fbt/runs, GET /admin/fbt/runs/:idfbt: view

Response envelope

Successful responses are wrapped by ResponseInterceptor:

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

Error envelope

statusCodeerrorCode examples
400BAD_REQUEST, VALIDATION_ERROR
401UNAUTHORIZED
403FORBIDDEN
404NOT_FOUND
500INTERNAL_SERVER_ERROR, DATABASE_ERROR

How the pipeline works

A rebuild run mines pairs from orders confirmed within a rolling window and writes the top-N related products per anchor. Each run is recorded as an fbt_rebuild_run row (status, counts, timings, error) that this surface exposes as an audit log.

Defaults (overridable per-run by the cron via the fbt settings group; the manual trigger always uses the module defaults):

KnobDefaultMeaning
windowDays180Orders confirmed within this many days are eligible
minSupport10Minimum co-occurrence count for a pair to qualify (below = noise)
topN20Max related-product rows stored per anchor (retrieval can request fewer)

Concurrency is guarded by a Postgres advisory lock inside the worker — two rebuilds never race on the same data. Stale queued/running runs older than 2h are marked failed on worker boot.


Domain types

RebuildRunResponse

type FbtRebuildTrigger = "cron" | "admin";
type FbtRebuildStatus  = "queued" | "running" | "succeeded" | "failed";

type RebuildRunResponse = {
  id: string;
  trigger: FbtRebuildTrigger;
  triggeredByUserId: string | null;     // null for cron runs
  status: FbtRebuildStatus;
  windowDays: number;
  minSupport: number;
  topN: number;
  pairCount: number | null;             // null until the run finishes
  anchorCount: number | null;
  eligibleLineCount: number | null;
  startedAt: string | null;             // ISO; null while queued
  finishedAt: string | null;            // ISO; null until done
  /** Wall-clock ms (finishedAt - startedAt). Null while in flight. */
  durationMs: number | null;
  error: string | null;                 // populated on failed runs
  createdAt: string;                    // ISO
};

TriggerRebuildResponse

type TriggerRebuildResponse = {
  runId: string;
  status: FbtRebuildStatus;             // typically "queued"
  /** True when an in-flight run already existed (no new run enqueued). */
  alreadyRunning: boolean;
};

Endpoints

Base path: /admin/fbt.

POST /admin/fbt/rebuild — Enqueue an on-demand rebuild

Required permission: fbt: rebuild. Idempotent under concurrency: if a rebuild is already queued or running, the existing run is returned with alreadyRunning: true rather than spawning a duplicate. (The advisory lock inside the worker is the real concurrency guard; this short-circuit is a UX nicety.) The run is enqueued with trigger: "admin" and the module-default window/support/topN.

Response 202 AcceptedTriggerRebuildResponse.

{
  "data": { "runId": "01J9...", "status": "queued", "alreadyRunning": false },
  "message": "Success",
  "statusCode": 202
}

GET /admin/fbt/runs — Paginated rebuild audit log

Required permission: fbt: view. Newest first.

Query

NameTypeDefaultConstraints
pageint1>= 1
limitint201..100
statusFbtRebuildStatus?Filter by lifecycle status (e.g. "show me the latest failure")
triggerFbtRebuildTrigger?Filter by source (cron / admin)

Response 200 — paginated envelope of RebuildRunResponse[] with metadata pagination.


GET /admin/fbt/runs/:id — Single rebuild run detail

Required permission: fbt: view.

Path params

NameNotes
idRebuild run id

Response 200RebuildRunResponse.

Errors

StatusCodeWhen
404NOT_FOUNDUnknown run id

  • admin-rbac — gates both surfaces via fbt:* permissions. See admin-rbac.md.
  • order — confirmed orders are the input signal; the rebuild mines order_line co-occurrence within the window.
  • settings — the fbt settings group holds the kill switch (enabled), cron override, and window/support/top-N overrides used by the scheduled rebuild.
  • catalog — anchors and related products resolve to catalog products at retrieval time. See the storefront surface in ../store/fbt.md.

On this page