Products
6 API calls in this section.
Create Product
/storefront/productsCreates a new product with flexible variant option configuration.
The has_variant flag controls whether the product is modeled as:
Multi-variant product (
has_variant: true): product defines variant options; warehouse fields are optional at product level; variants handle their own warehouse linking/creation.Simple single-variant product (
has_variant: false): backend will auto-create a single primary variant.
Exactly one scenario must be provided
Scenario A (link existing):
warehouse_item_idScenario B (create new):
warehouse_id+location_id+inventory_quantity(and nowarehouse_item_id)
Rules
Sending Scenario A and Scenario B fields together returns 400
If
has_variantis false and neither scenario is provided, returns 400Scenario B requires permission
manage:operations:tenantRequired: Valid Bearer token with tenant admin permissions
Required Fields
name: Multi-language product name objectdescription: Multi-language product description objectprice: Product base price (required, >= 0). Used as fallback when variant has no price.
Optional Fields (common)
category_ids: Array of category IDs the product belongs to (product can be linked to multiple categories)brand_id: Brand ID for the productvariant_options: Array of variant option type names that variants must provide values for (only meaningful whenhas_variantis true)is_active: Boolean to enable/disable the product (default: true)tags: Array of searchable tagsimages: Product-level image URLs used as fallback when variants have no imagesmedia: Optional rich media list. Each item:{ "type": "image"|"video", "url": "...", "sortOrder": 0 }. When provided, the legacyimagesarray is auto-derived from image-type media sorted by sortOrder.has_variant: Boolean flag (default: false)
The backend will automatically create a primary variant with
warehouse_item_id: from Scenario A or Scenario Bname:variant_nameif provided, otherwisenamedescription:variant_descriptionif provided, otherwisedescriptionimages:variant_imagesif provided, otherwise productimagesattributes: fromvariant_attributesif provided
Variant Options System (for has_variant: true)
System Options: colors, size, volume, model, weight, material, brand
Custom Options: Tenant-specific options like 'ram', 'storage', 'warranty'
Validation: All variants must provide values for selected options
Aggregation: Available option values are automatically aggregated from variants
Example Variant Options
Electronics: ["colors", "size", "model", "ram", "storage"]
Clothing: ["colors", "size", "material", "
fit_type"]Food: ["flavor", "size", "packaging", "organic"]
Features
Multi-language support (en, ar, ku)
Flexible variant option selection
Automatic validation of variant attributes
Option value aggregation for filtering
System and custom option support
Slug uniqueness (DB enforced)
slug must be unique per market (trimmed, case-insensitive). Duplicate slug returns 409 Conflict with a localized message to choose a different slug. Variant slugs are unique per product the same way.
Headers
| Parameter | Type | Description |
|---|---|---|
Authorization | string | Example: |
Body parameters
| Parameter | Type | Description |
|---|---|---|
name | object | — |
name.en | string | — |
name.ar | string | — |
name.ku | string | — |
description | object | — |
description.en | string | — |
description.ar | string | — |
description.ku | string | — |
slug | string | — |
price | number | — |
category_ids | array<string> | — |
brand_id | string | — |
Show 9 more body fields
Additional body parameters
| Parameter | Type | Description |
|---|---|---|
variant_options | array<string> | — |
is_active | boolean | — |
tags | array<string> | — |
has_variant | boolean | — |
use_product_defaults | boolean | — |
media | array<object> | — |
media.type | string | — |
media.url | string | — |
media.sortOrder | number | — |
1curl --globoff --request POST "https://staging-api.ondi.io/functions/v1/storefront/products" \2 --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}" \3 --header "Content-Type: application/json" \4 --data-raw '{5 "name": {6 "en": "MacBook Pro Laptop",7 "ar": "لابتوب ماك بوك برو",8 "ku": "لابتۆپی ماک بوک پرۆ"9 },10 "description": {11 "en": "High-performance laptop for professionals",12 "ar": "لابتوب عالي الأداء للمحترفين",13 "ku": "لابتۆپی بەرزی کارایی بۆ پیشەییەکان"14 },15 "slug": "macbook-pro-laptop",16 "price": 1999.99,17 "category_ids": [18 "electronics-category-id"19 ],20 "brand_id": "brand-uuid",21 "variant_options": [22 "colors",23 "size",24 "model",25 "ram"26 ],27 "is_active": true,28 "tags": [29 "laptop",30 "professional",31 "apple"32 ],33 "has_variant": true,34 "use_product_defaults": false,35 "media": [36 {37 "type": "image",38 "url": "https://cdn.example.com/products/macbook-pro/main.jpg",39 "sortOrder": 040 },41 {42 "type": "video",43 "url": "https://cdn.example.com/products/macbook-pro/demo.mp4",44 "sortOrder": 145 },46 {47 "type": "image",48 "url": "https://cdn.example.com/products/macbook-pro/alt-1.jpg",49 "sortOrder": 250 }51 ]52}'Response examples
1{2 "success": true,3 "message": "Product created successfully",4 "product": {5 "id": "product-uuid",6 "market_id": "market-uuid",7 "brand_id": "brand-uuid",8 "slug": "macbook-pro-laptop",9 "price": 1999.99,10 "name": {11 "en": "MacBook Pro Laptop",12 "ar": "لابتوب ماك بوك برو",13 "ku": "لابتۆپی ماک بوک پرۆ"14 },15 "description": {16 "en": "High-performance laptop for professionals",17 "ar": "لابتوب عالي الأداء للمحترفين",18 "ku": "لابتۆپی بەرزی کارایی بۆ پیشەییەکان"19 },20 "is_active": true,21 "tags": [22 "laptop",23 "professional",24 "apple"25 ],26 "has_variant": true,27 "use_product_defaults": false,28 "images": [29 "https://cdn.example.com/products/macbook-pro/main.jpg",30 "https://cdn.example.com/products/macbook-pro/alt-1.jpg"31 ],32 "media": [33 {34 "type": "image",35 "url": "https://cdn.example.com/products/macbook-pro/main.jpg",36 "sortOrder": 037 },38 {39 "type": "video",40 "url": "https://cdn.example.com/products/macbook-pro/demo.mp4",41 "sortOrder": 142 },43 {44 "type": "image",45 "url": "https://cdn.example.com/products/macbook-pro/alt-1.jpg",46 "sortOrder": 247 }48 ],49 "variant_options": [50 "colors",51 "size",52 "model",53 "ram"54 ],55 "available_option_values": {},56 "categories": [57 {58 "id": "electronics-category-id",59 "name": {60 "en": "Electronics",61 "ar": "إلكترونيات",62 "ku": "ئەلیکترۆنیات"63 }64 }65 ],66 "stock_quantity": 0,67 "created_at": "2024-07-20T10:00:00Z",68 "updated_at": "2024-07-20T10:00:00Z"69 }70}Get Products
/storefront/productsRetrieves products for the market with KPI stats, server-side sorting, additive legacy filters, and AdvancedDataGrid column filters.
Existing params remain supported:
search,category_id,brand_id,is_active,include_variants,limit,offsetNumeric range filters use
{ "min": number, "max": number }Date range filters use
{ "from": "YYYY-MM-DD", "to": "YYYY-MM-DD" }Boolean filters use string values
"true"or"false"
Stats
totalProducts: total products in marketactiveProducts: products whereis_active = truelowStockItems: products wherestock_quantity < 10topSelling: English name of the top-selling product in this market
Notes
column_filtersare AND-combined with each other and with top-level filters.Legacy numeric string ranges like
"5000,20000"are still accepted for backward compatibility.Legacy single-date
created_atstrings are still accepted and treated as one-day ranges.ratingandtotal_reviewscome from actual approved review data.statsare market-global and are not affected by filters, sorting, or pagination.
Query parameters
| Parameter | Type | Description |
|---|---|---|
search | string | Search products by name or description in any language (en/ar/ku) Example: |
category_id | string | Filter products by category ID Example: |
brand_id | string | Filter products by brand ID Example: |
is_active | string | Filter by active status (true/false). Default: true Example: |
include_variants | string | Include product variants in response (true/false). Default: true Example: |
limit | string | Number of products to return (default: 20) Example: |
offset | string | Number of products to skip for pagination (default: 0) Example: |
sort_by | string | Sort field. Allowed: price, Example: |
sort_order | string | Sort order: asc or desc. Default: desc Example: |
column_filters | string | Advanced column filters JSON. Numeric ranges use min/max. Date ranges use from/to. Boolean filters use string true/false. Supported keys: name, slug, price, Example: |
Headers
| Parameter | Type | Description |
|---|---|---|
Authorization | string | Example: |
1curl --globoff --request GET "https://staging-api.ondi.io/functions/v1/storefront/products?search=t-shirt&category_id=category-uuid&brand_id=brand-uuid&is_active=true&include_variants=true&limit=20&offset=0&sort_by=created_at&sort_order=desc&column_filters=%7B%22name%22%3A%22samsung%22%2C%22price%22%3A%7B%22min%22%3A5000%2C%22max%22%3A20000%7D%2C%22has_variant%22%3A%22true%22%2C%22created_at%22%3A%7B%22from%22%3A%222026-04-01%22%2C%22to%22%3A%222026-04-30%22%7D%2C%22is_active%22%3A%22true%22%7D" \2 --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}"Response examples
1{2 "success": true,3 "message": "Products retrieved successfully",4 "products": [5 {6 "id": "product-uuid-1",7 "market_id": "market-uuid",8 "brand_id": "brand-uuid",9 "slug": "aero-pure-3000",10 "price": 12500,11 "name": {12 "en": "Aero-Pure 3000"13 },14 "is_active": true,15 "stock_quantity": 8,16 "rating": 4.6,17 "total_reviews": 23,18 "created_at": "2026-04-01T10:00:00Z",19 "updated_at": "2026-04-02T10:00:00Z"20 }21 ],22 "pagination": {23 "limit": 20,24 "offset": 0,25 "total": 458226 },27 "stats": {28 "totalProducts": 4582,29 "activeProducts": 4210,30 "lowStockItems": 24,31 "topSelling": "Aero-Pure 3000"32 }33}Get Product
/storefront/products/:productIdRead a storefront product’s catalog details before displaying or editing it.
Path parameters
| Parameter | Type | Description |
|---|---|---|
productIdRequired | string | Dynamic path value from the request URL. |
Headers
| Parameter | Type | Description |
|---|---|---|
Authorization | string | Example: |
1curl --globoff --request GET "https://staging-api.ondi.io/functions/v1/storefront/products/${PRODUCTID}" \2 --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}"Response examples
No saved response example. Use Try it to inspect a staging response.
Get Product Analytics
/storefront/products/:productId/analyticsReturns product detail analytics for the tenant market using the storefront insights read model.
Required: Valid Bearer token with tenant admin permissions
Read Model
Current totals/trends come from
storefront_product_insights_currentMonthly chart data comes from
storefront_product_insights_dailyEngagement events can be fed by
storefront_product_events
Response Metrics
total_sales: Total sold quantity for the producttotal_views: Current product view counttotal_likes: Current like / wishlist countadd_to_basket: Add-to-basket totalconversion_rate:(total_sales / total_views) * 100when views exist, otherwise0sales_trend,views_trend,likes_trend,basket_trend: Percentage change vs previous 30-day windowmonthly_sales: Sold quantity for the last 12 calendar months in chronological ordermonth_labels: Month labels aligned withmonthly_salestotal_revenue,monthly_revenue,avg_rating,total_reviews,stats_last_updated: extra fields currently available
Notes
If the new insight tables are not deployed yet, the API falls back to the previous live calculation path.
Path parameters
| Parameter | Type | Description |
|---|---|---|
productIdRequired | string | UUID of the product |
Headers
| Parameter | Type | Description |
|---|---|---|
Authorization | string | Example: |
1curl --globoff --request GET "https://staging-api.ondi.io/functions/v1/storefront/products/${PRODUCTID}/analytics" \2 --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}"Response examples
1{2 "success": true,3 "analytics": {4 "total_sales": 1248,5 "total_views": 0,6 "total_likes": 312,7 "add_to_basket": 0,8 "conversion_rate": 0,9 "sales_trend": 12.5,10 "views_trend": 0,11 "likes_trend": 0,12 "basket_trend": 0,13 "monthly_sales": [14 65,15 85,16 72,17 95,18 88,19 110,20 102,21 125,22 118,23 135,24 128,25 14826 ],27 "month_labels": [28 "Jan",29 "Feb",30 "Mar",31 "Apr",32 "May",33 "Jun",34 "Jul",35 "Aug",36 "Sep",37 "Oct",38 "Nov",39 "Dec"40 ],41 "total_revenue": 52400,42 "monthly_revenue": [43 2700,44 3400,45 2950,46 4200,47 3980,48 4700,49 4520,50 5300,51 5010,52 5900,53 5650,54 609055 ],56 "avg_rating": 4.6,57 "total_reviews": 23,58 "stats_last_updated": "2026-04-23T08:00:00Z"59 }60}Update Product
/storefront/products/:productIdChange a storefront product’s content, branding, media, pricing or inventory linkage.
Path parameters
| Parameter | Type | Description |
|---|---|---|
productIdRequired | string | Dynamic path value from the request URL. |
Headers
| Parameter | Type | Description |
|---|---|---|
Authorization | string | Example: |
Body parameters
| Parameter | Type | Description |
|---|---|---|
name | object | — |
name.en | string | — |
name.ar | string | — |
name.ku | string | — |
description | object | — |
description.en | string | — |
slug | string | — |
brand_id | string | — |
variant_options | array<string> | — |
is_active | boolean | — |
has_variant | boolean | — |
media | array<object> | — |
Show 13 more body fields
Additional body parameters
| Parameter | Type | Description |
|---|---|---|
media.type | string | — |
media.url | string | — |
media.sortOrder | number | — |
warehouse_item_id | string | — |
price | number | — |
sku | string | — |
variant_name | object | — |
variant_name.en | string | — |
variant_description | object | — |
variant_description.en | string | — |
variant_images | array<string> | — |
variant_attributes | object | — |
variant_attributes.colors | string | — |
1curl --globoff --request PUT "https://staging-api.ondi.io/functions/v1/storefront/products/${PRODUCTID}" \2 --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}" \3 --header "Content-Type: application/json" \4 --data-raw '{5 "name": {6 "en": "Updated MacBook Pro",7 "ar": "ماك بوك برو محدث",8 "ku": "ماک بوک پرۆی نوێکراوە"9 },10 "description": {11 "en": "Latest generation MacBook Pro with enhanced performance"12 },13 "slug": "updated-macbook-pro",14 "brand_id": "brand-uuid",15 "variant_options": [16 "colors",17 "size",18 "model",19 "ram",20 "storage"21 ],22 "is_active": true,23 "has_variant": false,24 "media": [25 {26 "type": "image",27 "url": "https://cdn.example.com/products/macbook-pro/main-updated.jpg",28 "sortOrder": 029 },30 {31 "type": "video",32 "url": "https://cdn.example.com/products/macbook-pro/promo.mp4",33 "sortOrder": 134 }35 ],36 "warehouse_item_id": "warehouse-item-simple-macbook",37 "price": 1999.99,38 "sku": "MBP-SIMPLE-BASE",39 "variant_name": {40 "en": "MacBook Pro Base Model"41 },42 "variant_description": {43 "en": "Base configuration automatically managed as a single variant"44 },45 "variant_images": [46 "https://cdn.example.com/products/macbook-pro/variant-base.jpg"47 ],48 "variant_attributes": {49 "colors": "Space Gray"50 }51}'Response examples
No saved response example. Use Try it to inspect a staging response.
Delete Product
/storefront/products/:productIdRemove a product from the storefront catalog.
Path parameters
| Parameter | Type | Description |
|---|---|---|
productIdRequired | string | Dynamic path value from the request URL. |
Headers
| Parameter | Type | Description |
|---|---|---|
Authorization | string | Example: |
1curl --globoff --request DELETE "https://staging-api.ondi.io/functions/v1/storefront/products/${PRODUCTID}" \2 --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}"Response examples
No saved response example. Use Try it to inspect a staging response.