Products
The Products menu is the catalog's product-management surface — list, search, create, edit, copy, and delete products, plus manage each product's images, per-source inventory, and customer-group prices. It mirrors the admin Catalog → Products screen.
Product types
Every product has a type, fixed at creation. There are seven:
| Type | Notes |
|---|---|
simple | A standalone product with its own price and stock. |
virtual | Like simple but non-shippable (no weight/dimensions) — services, memberships. |
downloadable | Sells downloadable links (paid files) and samples (free previews); non-stockable. |
grouped | A storefront grouping of other simple products (linked products); has no own price. |
bundle | A configurable kit built from bundle options; its price is calculated from the chosen items. |
configurable | A parent with variants generated from variant-defining attributes (super_attributes, e.g. colour × size). Each variant is its own SKU with its own price/stock. |
booking | A bookable product (default / appointment / event / rental / table sub-types) with time slots; non-stockable. |
Composite types own no price or stock of their own. For configurable, bundle, grouped, and booking, the price and inventory live on the children — the variants, bundle items, linked products, or slots. Their top-level price / quantity are derived or empty.
Creating a product is two steps
Creation is deliberately minimal: a POST creates the shell from just sku + attribute_family_id + type (plus super_attributes for configurable). Everything else — name, description, price, images, categories, channels, inventory — is filled in afterwards via the update endpoint. This mirrors the admin's create-then-edit wizard.
status vs visibleIndividually
Two independent flags control storefront presence:
status—1enabled /0disabled. A disabled product is fully hidden from the storefront.visibleIndividually— whether the product appears in category/search listings. Variant products and grouped-component products are usually set to0(reachable only through their parent), while still beingstatus = 1.
Both must effectively be on for a product to be browsable on its own. (status is stored on the flattened product table, resolved per channel + locale.)
Per-product sub-resources
The product edit screen's tabs map to their own endpoint groups, each scoped to one product:
- Images — upload, reorder, and delete a product's images.
- Inventories — read and bulk-update the per-inventory-source stock quantities.
- Customer-group prices — tiered prices that apply to specific customer groups.
These are not returned in full on the listing (they're detail-only); the single-product endpoint embeds them inline.
The product listing
GET /api/admin/catalog/products is the datagrid — a paginated { data, meta } envelope.
Listing filters (AND-combined)
All filters are combined with AND — every filter you add narrows the result set further. Pass them as query parameters:
| Filter | Type | Description |
|---|---|---|
channel | string | Channel code used to resolve per-channel values. |
name | string | Partial product-name match. |
sku | string | Partial SKU match. |
attribute_family | integer | Attribute-family ID. |
price_from / price_to | number | Price band (inclusive). price=50,200 is shorthand for price_from=50&price_to=200. |
product_id | string | A single ID, or a comma-separated list (e.g. 1,22,2705). |
status | integer | 0 (disabled) or 1 (active). |
type | string | One of the seven product types. |
locale | string | Locale code used to resolve translated values. |
Plus pagination and sort:
| Parameter | Type | Description |
|---|---|---|
page | integer | 1-based page number (default 1). |
per_page | integer | Page size (default 10, max 50). |
sort | string | product_id (default), sku, name, type, status, price, quantity, attribute_family, channel. |
order | string | asc or desc (default desc). |
Listing columns
Every row carries these scalar columns. Heavy fields are null on the listing (fetch them from the detail endpoint):
| Field | Type | Notes |
|---|---|---|
id | integer | Product ID. |
sku | string | SKU. |
name | string|null | Resolved for the active locale/channel. null for draft products with no name yet. |
type | string | Product type. |
status | integer | 1 active / 0 disabled. |
price | string|null | Base price (composite types carry no own price → null). |
formattedPrice | string|null | Locale-formatted base price. |
specialPrice | string|null | Discounted price, when a special price is set. |
formattedSpecialPrice | string|null | Locale-formatted special price. |
specialPriceFrom | string|null | Start date of the special-price window (null = always on). |
specialPriceTo | string|null | End date of the special-price window (null = no end). |
quantity | integer | Total stock across inventory sources. |
baseImageUrl | string|null | Medium-cache base image URL. |
imagesCount | integer | Number of images. |
categoryId | integer|null | Primary category ID. |
categoryName | string|null | Primary category name. |
channel | string | Resolved channel code. |
locale | string | Resolved locale code. |
attributeFamilyId | integer | Attribute-family ID. |
attributeFamilyName | string | Attribute-family name. |
urlKey | string|null | Storefront URL slug. |
visibleIndividually | boolean | Appears in category/search listings. |
shortDescription / description | string|null | Resolved content. |
metaTitle / metaDescription / metaKeywords | string|null | SEO fields. |
weight | number|null | Product weight. |
featured | boolean | Featured flag. |
new | boolean | "New" flag. |
createdAt / updatedAt | string | Timestamps. |
taxCategoryId | integer|null | Detail-only — null on the listing. |
manageStock | boolean|null | Detail-only — null on the listing. |
inStock | boolean|null | Detail-only — null on the listing. |
translations, images, categories, inventories, customerGroupPrices, superAttributes, variants, bundleOptions, linkedProducts, downloadableLinks, downloadableSamples | array|null | Relation blocks — all null on the listing; populated only on the detail endpoint. |
Actions
| Action | What it does |
|---|---|
| Copy | Duplicates an existing product into a new draft product (a fresh SKU is generated). |
| Mass delete | Deletes several products at once — body { "indices": [1, 22] }. Missing IDs are skipped. |
| Mass update status | Bulk enable/disable — body { "indices": [1, 22], "value": 0 } (0 = disable, 1 = active). |
| Export (CSV) | Downloads the listing as a CSV file, honouring the current filters; exports every matching row, not just the page. REST only. |
Endpoints in this menu
| Action | Endpoint |
|---|---|
| List products | GET /api/admin/catalog/products |
| Product detail | GET /api/admin/catalog/products/{id} |
| Create product | POST /api/admin/catalog/products |
| Update product | PUT /api/admin/catalog/products/{id} |
| Delete product | DELETE /api/admin/catalog/products/{id} |
| Copy product | POST /api/admin/catalog/products/{id}/copy |
| Mass delete | POST /api/admin/catalog/products/mass-delete |
| Mass update status | POST /api/admin/catalog/products/mass-update-status |
| Export products (CSV) | GET /api/admin/catalog/products/export |
| Upload images | POST /api/admin/catalog/products/{id}/images |
| Reorder images | PUT /api/admin/catalog/products/{id}/images/reorder |
| Delete image | DELETE /api/admin/catalog/products/{id}/images/{imageId} |
| List inventories | GET /api/admin/catalog/products/{id}/inventories |
| Update inventories | PUT /api/admin/catalog/products/{id}/inventories |
| List customer-group prices | GET /api/admin/catalog/products/{id}/customer-group-prices |
| Add customer-group price | POST /api/admin/catalog/products/{id}/customer-group-prices |
| Update customer-group price | PUT /api/admin/catalog/products/{id}/customer-group-prices/{priceId} |
| Delete customer-group price | DELETE /api/admin/catalog/products/{id}/customer-group-prices/{priceId} |
The canonical product listing is List products (GET /api/admin/catalog/products) above. There is also a separate slim Add-Product Search (GET /api/admin/products) used only by the Create-Order "Add Product" modal — not the product listing.
All Products endpoints require an admin Bearer token — see Authentication. Reads require catalog.products.view; writes require the matching catalog.products.create / .edit / .delete permission.

