Using Catalog Data
The catalog is a simple per-project store of products and categories. Use it as a reference list for the dashboard and your own tooling. Promotion evaluation does not read the catalog. The engine sees only the fields you put on each cart item, so a promotion that targets “Outerwear” matches only items sent with category: "Outerwear" (or categories, tags, …).
Products
Section titled “Products”POST /projects/{projectId}/products
{ "name": "Wool Jacket", "description": "Medium, navy", "price": 8900, "categoryIds": ["cat_k3j2h1"], "metadata": { "externalId": "shopify-81234", "sku": "JKT-M-NV" }, "status": "active"}| Field | Notes |
|---|---|
name | Required, non-empty |
price | Required, integer ≥ 0. Use cents, as everywhere else in Storelayer |
categoryIds | Category ids (cat_…). Not checked for existence |
metadata | Free-form object. Replaced as a whole on update |
status | active (default) or inactive |
- Ids are generated (
prod_…) and cannot be chosen. Store your own system’s id inmetadatato keep a stable mapping. PUT /projects/{projectId}/products/{productId}updates only the fields you send. A missing product returns 404PRODUCT_NOT_FOUND.GET /projects/{projectId}/productsacceptslimit(1–100, default 50),offset,statusandcategoryId. ThecategoryIdfilter is applied after paging, so a filtered page can hold fewer thanlimitresults even when more exist. Keep paging until a page comes back empty.
Bulk import
Section titled “Bulk import”POST /projects/{projectId}/products/bulk with { "products": [ ... ] } takes 1–1000 products per request, each with the create shape above. The response is { "success": [products...], "errors": [{ "index", "reason" }] }. Rows are inserted one at a time, so a bad row does not block the others. Check errors and resend only those indexes. Bulk import always creates new products: re-importing the same list creates duplicates.
Categories
Section titled “Categories”POST /projects/{projectId}/categories with { "name": "Outerwear", "description": null }. Ids are generated (cat_…). Update with PUT /projects/{projectId}/categories/{categoryId}. Deleting a category fails while any product still lists it in categoryIds.
Connecting catalog data to promotions
Section titled “Connecting catalog data to promotions”Since evaluation never looks up products, your backend builds each cart line from its own source of truth:
| Cart item field | Fill from | Why |
|---|---|---|
id | Your basket line id | Discounts are reported per line |
productId | Your product id (or the catalog prod_… id; just be consistent) | targetRules on $('item').productId compare exactly |
unitPrice | The price you will charge, in cents | Not taken from catalog price |
category / categories | The names promotions use, e.g. "Outerwear" | Compared case-insensitively. Catalog categoryIds are opaque ids, so map them to names, or write rules on the ids |
tags, brand, collections, attributes | Product data | Item metadata is not visible to rules, so copy matchable values here |
Pick one vocabulary per field. If rules say "Outerwear", every cart must send "Outerwear" (any case), never cat_k3j2h1.
Verify
Section titled “Verify”- Preview a cart line for a product that should match, one that should not, and one with modifiers. Check
notApplied[].reasonfor the excluded one (computation_no_discountmeans the conditions passed but no item matchedtargetRules). - Before renaming a category, search promotions for rules that compare against the old name.
See also
Section titled “See also”- Checkout integration
- Promotion concepts
- Conditions
- Catalog in the API reference