Supercommerce API Docs
Store API

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…

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 storefront never has to call a "create my wishlist" endpoint. The wishlist itself is invisible on the wire: there is no list id in the URL or response — leaves room for a future multi-list surface at a sibling path without breaking this contract.

Source: api-modules/wishlist/src/controllers/store-wishlist.controller.ts.

This is a customer-only surface — there is no admin or vendor controller. The wishlist module hooks into the cart module for the "add to cart" action; cart errors propagate unchanged.


Conventions

Authentication

Endpoint groupAuth
GET/POST/DELETE /store/wishlist/**required (customer session)

Every read and write is scoped to session.user.id. There is no guest wishlist.

Headers

The add-to-cart endpoint accepts the cart module's headers (forwarded verbatim to the cart layer):

HeaderDirectionNotes
x-cart-tokenrequest (optional) + responseCart handle. Mint by omitting — response sets it.
x-platformrequest (optional)WEB / APP (case-insensitive). Defaults to WEB.

Response envelope

{
  "data": <payload>,
  "message": "Success",
  "statusCode": 200,
  "metadata": { /* on the list endpoint */ }
}

Error envelope

statusCodeerrorCode examples
400BAD_REQUEST, VALIDATION_ERROR
401UNAUTHORIZED
404NOT_FOUND
409INSUFFICIENT_INVENTORY, BELOW_MIN_QUANTITY_PER_CART, ABOVE_MAX_QUANTITY_PER_CART (from cart-side validation during add-to-cart)
500INTERNAL_SERVER_ERROR, DATABASE_ERROR

Currency

Price fields on WishlistItemResponse (currentPrice, regularPrice, specialPrice) are integer subunits.


Domain types

WishlistItemResponse

type WishlistItemResponse = {
  variantId: string;
  productId: string;
  vendorId: string;
  productName: string;
  variantName: string | null;
  thumbnail: string | null;
  /** Effective unit price (active special if in-window, else regular). Integer subunits. */
  currentPrice: number | null;
  regularPrice: number | null;
  /** Active special unit price, or null if none / outside the window. */
  specialPrice: number | null;
  /** Best-effort live stock signal. `false` reflects present-but-OOS — soft-deleted variants
   *  are filtered out of the response entirely. */
  isInStock: boolean;
  addedAt: string;                          // ISO
};

The shape mirrors the cart-line snapshot fields so storefronts can reuse a single product-card rendering path.

WishlistContainsResponse

type WishlistContainsResponse = {
  membership: Record<string, boolean>;     // keyed by variantId; every requested id is present
};

Endpoints

Base path: /store/wishlist.

GET /store/wishlist — List my wishlist

Paginated. Newest-added first. The default wishlist is materialized on first call. Soft-deleted variants are filtered out of the response (the underlying row stays in case the variant is restored).

Query

NameTypeDefaultConstraints
pageint1>= 1
limitintDEFAULT_WISHLIST_PAGE_SIZE1..MAX_WISHLIST_PAGE_SIZE

Response 200 — paginated WishlistItemResponse[] with metadata: { total, limit, offset, hasMore }.


POST /store/wishlist/items — Add a variant

Idempotent. Adding the same variant again returns the existing entry and does not re-emit wishlist.item.added.

Body

{ "variantId": "01J9..." }
FieldConstraints
variantIdstring, min 1 char

Response 201WishlistItemResponse (same hydrated shape as GET /store/wishlist entries — no follow-up fetch needed).

Side effects — emits wishlist.item.added on first add (not on idempotent re-add).

Errors

StatusCodeWhen
400VALIDATION_ERRORBody fails zod
404NOT_FOUNDVariant does not exist

DELETE /store/wishlist/items/:variantId — Remove a variant

Idempotent — removing an absent variant still returns 204.

Response 204 No Content.

Side effects — emits wishlist.item.removed when a row was actually removed.


DELETE /store/wishlist/items — Clear my wishlist

Removes every item from the caller's wishlist.

Response 204 No Content.


POST /store/wishlist/items/:variantId/add-to-cart — Move a wishlist item into the cart

Delegates to the cart's add-line. The wishlist entry is NOT removed — the customer keeps the variant saved until they explicitly remove it (rationale: "added to cart" doesn't mean "no longer wanted later"). Cart errors (out-of-stock, min/max quantity, vendor disabled, etc.) propagate unchanged.

Path params

NameNotes
variantIdVariant id to add to cart

Query

NameTypeDefaultNotes
quantityint1>= 1. Forwarded verbatim to CartLineService.addLine

Headers

HeaderNotes
x-cart-tokenOptional. Mint on first request. Response always emits the active cart's token.
x-platformOptional. WEB / APP.

Response 200CartResponse (the cart module's full cart shape — see cart.md).

Errors

StatusCodeWhen
400VALIDATION_ERROR, BELOW_MIN_QUANTITY_PER_CART, ABOVE_MAX_QUANTITY_PER_CARTCart-side validation
404NOT_FOUNDVariant does not exist
409INSUFFICIENT_INVENTORYStock cannot cover the requested quantity

POST /store/wishlist/contains — Bulk-check membership

Bulk-only — the singular /contains/:variantId form is intentionally absent so a product-listing page rendering N cards hits the server once, not N times. Does NOT lazy-create the default wishlist; if the caller has never wished for anything, every returned key is false.

Body

{ "variantIds": ["01J9...", "01J9..."] }
FieldConstraints
variantIdsarray, 1..MAX_WISHLIST_CONTAINS_VARIANT_IDS entries; each min 1 char

Response 200

{
  "data": {
    "membership": {
      "01J9aaaa...": true,
      "01J9bbbb...": false
    }
  }
}

Every variantId from the request is present in membership — clients can index without a hasOwnProperty check.

Errors

StatusCodeWhen
400VALIDATION_ERROREmpty array, too many ids, etc.

  • cartadd-to-cart forwards to the cart's add-line; the response is the cart's CartResponse. See cart.md.
  • catalog — variants referenced by variantId come from this module.
  • inventory — drives the isInStock signal on each row.

On this page