Search Documentation
Search for a documentation page...
Products API
REST API endpoints for browsing, creating, updating, and deleting products.
List Products
Returns a paginated list of products with variants, categories, and translations.
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
limit | number | 10 | Products per page (1-100) |
offset | number | 0 | Number of products to skip |
cursor | string | — | Product UUID for keyset pagination |
category | string | — | Filter by category slug |
brand | string | — | Filter by brand slug |
query | string | — | Search term for product name |
active | boolean | — | Filter by published status |
excludeBundles | boolean | false | Exclude bundle products |
includeEvents | boolean | false | Include event-products (excluded by default — see the Events API) |
orderBy | string | — | Sort field: price, name, or createdAt |
orderDirection | string | desc | Sort direction: asc or desc |
currency | string | — | Currency code for price display (e.g. EUR) |
lang | string | — | Locale code for translations (e.g. pl-PL) |
Response
Get Product
Returns a single product by UUID or URL slug, with full variant details, category, collections, and translations.
| Parameter | Type | Description |
|---|---|---|
currency | string | Currency code for variant price resolution |
lang | string | Locale code for translated slug lookup |
Supports ?lang= query parameter for translated slug lookup when translations are enabled.
Draft visibility by slug: When looking up by slug, the result depends on the authentication method. Operator credentials (OAuth tokens from "Sign in with YNS" apps, or internal tokens used by Elliot) can see draft products by slug — this lets tools read back a just-created draft without publishing it first. Public store API keys (sk- prefix) only see published products when looking up by slug. Lookups by UUID return drafts to any authenticated caller regardless of credential type.
Response
Each variant includes an omnibusPrice field — the lowest price recorded in the last 30 days (EU Omnibus Directive). The value is a string in minor units or null when the store has not enabled omnibus pricing or no historical price exists for that variant.
Bundle products additionally include a bundle object describing the configurable structure: always-included items (forced: true) plus "pick exactly N" choice groups, each with variant price/stock/images. Use it to render and price the bundle on your storefront, then submit the shopper's picks via POST /api/v1/carts.
Bundle object (bundle products only)
When the product's type is "bundle", the response includes a bundle field:
Create Product
Creates a product in one call. Image URLs are downloaded and re-uploaded to the store's CDN. Categories and collections are found or created automatically when referenced by name.
By default a single default variant is created at the given price. To create a product with multiple variants (e.g. Size × Color), pass the variants array — see the multi-variant example below.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Product display name |
slug | string | Yes | URL-friendly identifier (^[a-z0-9-]+$) |
description | string | No | Plain text product description |
content | object | No | Rich TipTap JSON document ({ "type": "doc", ... }) for the product body |
price | number | Yes | Price as a decimal (e.g. 29.99) |
images | string[] | No | Array of image URLs to upload |
categoryId | string | No | Category UUID — takes priority over category when both are provided |
category | string | No | Category name — created automatically if it doesn't exist |
brandId | string | No | Brand UUID — must reference an existing brand (pre-create via the admin) |
collectionIds | string[] | No | Collection UUIDs to attach the product to (unioned with collectionNames) |
collectionNames | string[] | No | Collection names — created automatically if missing |
taxRateId | string | No | Tax rate UUID to apply to this product (pre-create via POST /tax-rates) |
stock | number | No | Initial inventory quantity (default: 0) |
quantity | number | No | Deprecated — use stock instead |
prices | object | No | Multi-currency prices (e.g. { "EUR": 25.99, "GBP": 22.50 }) |
variants | object[] | No | Structured variants — see Multi-Variant Products below |
seo | object | No | SEO overrides — see SEO fields below |
The price (and per-variant price) must be large enough to represent at least one minor unit in the store's currency. A value like 0.001 for USD passes the schema's positivity check but rounds to 0 cents — the API returns a 400 with "Price is too small to represent in the store currency".
Simple product
Response (201)
Multi-Variant Products
Pass the variants array to create a product with real variant types, values, and combinations (e.g. Size × Color). Every variant must define the same set of option keys. The top-level price is used as the default when a variant omits its own price.
Variant object fields
| Field | Type | Required | Description |
|---|---|---|---|
options | object | Yes | Option label → value map, e.g. { "Size": "L", "Color": "Red" } |
sku | string | No | Variant SKU (required to sync stock and per-variant prices) |
barcode | string | null | No | Variant barcode (EAN, UPC, GTIN, ISBN, or internal) |
description | string | null | No | Per-variant description (markdown, max 4000 chars), shown on the product page |
price | number | No | Variant price as a decimal — defaults to the product price |
stock | number | No | Variant inventory quantity |
images | string[] | No | Variant-specific image URLs |
shippable | boolean | No | Whether the variant ships physically (default: true) |
weight | number | No | Weight for shipping calculation |
width | number | No | Width dimension |
height | number | No | Height dimension |
depth | number | No | Depth dimension |
prices | object | No | Per-variant multi-currency prices (e.g. { "EUR": 28.00 }) |
attributes | object[] | No | Per-variant key/value rows shown on the product page, e.g. [{ "key": "Nuty zapachowe", "value": "bergamotka, cedr" }]. Each entry is { key, value, checked? }. |
Example
Multi-variant response (201)
Update Product
Partially updates a product. Only the provided fields are changed.
Request Body
| Field | Type | Description |
|---|---|---|
title | string | New product name |
description | string | New plain text description |
content | object | Rich TipTap JSON document ({ "type": "doc", ... }) for the product body |
images | string[] | Replace the product's images. External URLs are uploaded to the store's CDN; URLs already on the CDN pass through. [] removes all images. |
status | string | active (published) or draft |
categoryId | string | null | Category UUID — takes priority over category; pass null to clear |
category | string | Category name — created automatically if it doesn't exist |
brandId | string | null | Brand UUID — must reference an existing brand; pass null to clear |
taxRateId | string | null | Tax rate UUID to apply to this product (create via POST /tax-rates); pass null to clear. Replaces any existing assignment. |
collectionIds | string[] | Collection UUIDs. Replaces the product's full membership set ([] removes all). Unioned with collectionNames. |
collectionNames | string[] | Collection names — created automatically if missing. Unioned with collectionIds. |
event | object | Toggle event/ticketing behaviour — see Event fields below |
seo | object | SEO overrides — see SEO fields below |
Event fields
The event object merges onto the product's existing event metadata. Setting enabled: false disables ticketing while preserving the stored details for later re-enabling. Omitted sub-fields keep their current value.
| Field | Type | Required | Description |
|---|---|---|---|
enabled | boolean | Yes | Enable or disable event/ticketing behaviour |
startsAt | string | null | No | Event start time as an ISO 8601 string |
location | string | null | No | Event location |
capacity | number | null | No | Maximum attendees (positive integer, metadata only — not wired to inventory) |
guestLabel | string | null | No | Display label for the guest line (max 100 chars, e.g. "Speakers") |
guest | string | null | No | Guest/speaker name(s) as free text (max 500 chars, comma-separated for multiple) |
SEO fields
The seo object sets meta/OG title and description overrides for the product page. On PATCH, it merges onto existing values: omitted fields keep their stored value, null clears a field back to the default (derived from the product name/summary).
| Field | Type | Description |
|---|---|---|
title | string | null | SEO meta title (null to clear) |
description | string | null | SEO meta description (null to clear) |
canonical | string | null | Canonical URL (null to clear) |
Basic update
Enable event mode
When the event field is included, the response contains the merged event metadata:
Set SEO overrides
When the seo field is included, the response contains the merged SEO metadata:
Delete Product
Deletes a product if it has no order history. Products with orders are archived (status set to hidden) instead of deleted.
Create Variant
Adds a new variant to an existing product.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
sku | string | Yes | SKU code |
barcode | string | null | No | Barcode (EAN, UPC, GTIN, ISBN, or internal) |
description | string | null | No | Per-variant description (markdown, max 4000 chars), shown on the product page |
title | string | Yes | Variant display name (e.g. Large / Red) |
priceCents | number | Yes | Price in cents (e.g. 2999 = $29.99) |
imageUrl | string | No | Variant image URL |
stock | number | No | Inventory quantity (default: 0) |
options | object | No | Option label → value map (e.g. { "Zapach": "Black Fig" }). Builds the variant types/values/combinations that drive the storefront picker. |
attributes | object[] | No | Per-variant key/value rows shown on the product page, e.g. [{ "key": "Nuty zapachowe", "value": "bergamotka, cedr" }]. Each entry is { key, value, checked? }. |
Batch Create Products
Creates up to 100 products in a single request. Each product gets a default variant. Partial failures are reported in the errors array.
Request Body
JSON array of product objects:
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Product name |
slug | string | No | URL slug (auto-generated from name) |
summary | string | No | Short description |
price | number | Yes | Price as decimal |
images | string[] | No | Image URLs |
status | string | No | draft, published, or hidden (default: draft) |
category_name | string | No | Category name |
collection_names | string[] | No | Collection names to add to |
sku | string | No | Stock keeping unit |
stock | number | No | Inventory quantity |
shippable | boolean | No | Requires shipping (default: true) |
Response
Import Products (CSV)
Import products from CSV. Supports multi-variant products (multiple rows with the same slug) and updates via product_id. Categories and collections are created or matched automatically.
Response
Product Reviews
List Reviews
Returns approved reviews with summary statistics.
| Parameter | Type | Default | Description |
|---|---|---|---|
limit | number | 10 | Reviews per page (1-100) |
offset | number | 0 | Reviews to skip |
lang | string | — | Locale code for translated slug lookup |
Create Review
Submits a review (pending approval by store owner).
| Field | Type | Required | Description |
|---|---|---|---|
author | string | Yes | Reviewer name (1-100 chars) |
email | string | Yes | Reviewer email (not returned in responses) |
content | string | Yes | Review text (1-5000 chars) |
rating | number | Yes | Star rating (1-5) |
Product Filters
Returns the facets available for building a storefront filter UI: the store's price range, its variant option types with their values, and the active categories, collections, and brands. Scoped store-wide rather than to a result set, so you can render the filter sidebar before running a search.
Only active categories, collections, and brands are returned, and variant types with no values are omitted.
Response
Prices are in cents. Feed the selected values back into List Products using category, collection, brand, vts, and the price range parameters.
Reset Catalog
Deletes the store's entire catalog and blog in a single transaction. Intended for reseeding a demo or test store, not for routine cleanup.
This is irreversible. Products cascade to their variants, prices, combinations, memberships, favorites, and translations.
Body Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
confirm | string | Yes | Must exactly match the authenticated store's UUID |
The confirm field is a safety belt: if it doesn't equal the store id behind the API key, the request is refused with 400 and nothing is deleted. Unknown fields are rejected.
Response
Orders, customers, and settings are left untouched.
Get your store's UUID from
GET /api/v1/me.