Skip to main content

Browsing: retail or trade

GET /products requires a priceType query parameter — exactly one of retail or trade. trade is only available to trade accounts and staff; anyone else gets 403. Omitting priceType is a 400.

Home page in one call

GET /recommendations returns every product rail on the storefront home page — justForYou, everydayItems, lowPrices, bestSellers, newArrivals, flashSales and recentlyViewed — instead of one call per rail. Each rail has exactly the shape its own /recommendations/* endpoint returns, with the same pricing visibility for the caller.
  • limit — products per rail (1–100, default 10).
  • include — comma-separated subset of rails, e.g. include=bestSellers,flashSales.
  • recentlyViewed (and the personal part of justForYou) need a signed-in caller; for guests recentlyViewed is null.
  • A rail that fails comes back null with its message under errors; the others still load.

Several products in one call

POST /products/lookup with { "ids": [ … ] } (up to 100) returns each product exactly as GET /products/:id would for the same caller — trade pricing included only if the caller may see it. Ids that don’t exist, are deleted, or are hidden from the caller (trade-only products for a retail caller or guest) are simply left out rather than failing the call.

Product shape

Category and brand are embedded objects, null when unset:
Variants and sizes. A product has one or more variants (e.g. “Original”, “Wholewheat”), and each variant has one or more sizes. The size is what’s sold: it has its own id, SKU, prices, sale window and stock. Products without real choices have a single “Default” variant with one size.
  • priceRange is the lowest–highest retail price across the sizes (“from £5.99”); tradePriceRange is the same for trade prices and is null unless the caller can see wholesale pricing.
  • Customers see only active sizes they can buy: trade-only sizes are hidden from retail callers, and exact stock is replaced by available and stockStatus (in_stock, low_stock, out_of_stock).
  • Browse price filters and price sorts use each product’s lowest size price for the requested priceType; ?packSize=5kg matches size labels.
  • For one release the product still carries retailPrice, tradePrice, sku, weight and the sale fields from its default size, and stockLevel as the total across its sizes.
Cart and orders. POST /cart/items takes { productId, sizeId, quantity }; sizeId may be left out only when the product has exactly one size. Each size is its own cart line. Cart and order lines include sizeId, variantName and sizeLabel (size holds the same label for existing clients), and order lines also record the size’s sku. The wishlist saves the product, not a size. Customer-facing product responses don’t include source; admin responses do.

Deleted products

Deleting a product hides it everywhere a customer or admin browses — storefront, search, recommendations, the admin product list and admin product counts — and GET /products/:id returns 404. Existing orders, carts and wishlists keep their own copy of the product’s name, image and price, so order history is unaffected. Check whether products can still be bought with:
available is false for a product that is deleted, deactivated, or unknown. A deleted product keeps its SKU and slug reserved, so creating a product with either returns 409 with the deleted product’s productId. Bring it back instead: admins find deleted products with GET /admin/products?status=deleted and restore them with POST /admin/products/bulk-restore ({ "ids": [ … ] }). A restored product comes back inactive; activate it to put it back on sale. Leave slug out when creating a product and a unique one is generated from the name (rice-5kg-2 if rice-5kg is taken).

Cart and wishlist availability

Every cart and wishlist item has an available flag. Unavailable cart lines stay visible so the customer can see what happened, but they’re excluded from the cart subtotal. Prompt the customer to remove them — checkout refuses the cart until they do.

Checkout re-validation

The cart stores a snapshot of each product taken when it was added. POST /orders/checkout re-checks every line against the live product, at the caller’s own price (trade or retail), before creating the order: A 409 is safe to retry immediately — the next attempt goes through at the updated prices.