Products¶
Manage product catalog: brands, categories, subcategories, products, variants (optional sku/barcode), and product reviews. Reviews are shared across all variants of a product. Each product has a stored avg_rating (0–5) and review_count updated automatically when reviews are added, updated, or deleted.
Cost Price (Staff-only)¶
Products and variants include a cost_price field, but it is only returned in API responses when the requester is a staff user.
- Public/non-staff:
cost_priceis omitted from responses. - Staff:
cost_priceis included for both products and nested variants.
Currency conversion (non-staff)¶
For non-staff product and variant responses, prices include display_currency and currency_symbol. Prices use the primary currency (e.g. USD) by default. Use ?currency=CODE to convert. The API uses the Currency usd_rate for conversion when ?currency= is passed.
- Default: When no ?currency= param is passed, the primary currency (typically USD) is used. Prices include display_currency and currency_symbol.
- With ?currency=CODE: Prices are converted; response includes display_currency, currency_symbol, and converted price.
- Staff: Always see stored prices; display_currency and currency_symbol are included (primary/USD).
Currencies¶
Manage currencies for price conversion. Stored prices are in the primary currency (e.g. USD). usd_rate means 1 USD = usd_rate × <currency> (e.g. KES with usd_rate 160.50 → 1 USD = 160.50 KES).
Example: 200 OK
[
{
"id": 1,
"code": "USD",
"name": "US Dollar",
"symbol": "$",
"usd_rate": "1.000000",
"is_primary": true,
"is_active": true,
"created_at": "2025-01-15T10:00:00Z",
"updated_at": "2025-01-15T10:00:00Z"
},
{
"id": 2,
"code": "KES",
"name": "Kenyan Shilling",
"symbol": "KSh",
"usd_rate": "160.500000",
"is_primary": false,
"is_active": true,
"created_at": "2025-01-16T12:00:00Z",
"updated_at": "2025-01-16T12:00:00Z"
}
]
Example: Error — List is public; no 403. Invalid request may return 400 with detail.
Example: 200 OK
{
"id": 2,
"code": "KES",
"name": "Kenyan Shilling",
"symbol": "KSh",
"usd_rate": "160.500000",
"is_primary": false,
"is_active": true,
"created_at": "2025-01-16T12:00:00Z",
"updated_at": "2025-01-16T12:00:00Z"
}
Example: 404 Not Found
{
"code": "KES",
"name": "Kenyan Shilling",
"symbol": "KSh",
"usd_rate": "160.50",
"is_primary": false,
"is_active": true
}
Example: 201 Created
{
"id": 2,
"code": "KES",
"name": "Kenyan Shilling",
"symbol": "KSh",
"usd_rate": "160.500000",
"is_primary": false,
"is_active": true,
"created_at": "2025-01-16T12:00:00Z",
"updated_at": "2025-01-16T12:00:00Z"
}
Example: 400 Bad Request (validation)
Example: 403 Forbidden (non-staff)
{
"code": "KES",
"name": "Kenyan Shilling",
"symbol": "KSh",
"usd_rate": "161.00",
"is_primary": false,
"is_active": true
}
PUTexpects the full object.PATCHcan send only the fields you want to change.- Setting
is_primary=trueon one currency clearsis_primaryon others.
Example: 200 OK
{
"id": 2,
"code": "KES",
"name": "Kenyan Shilling",
"symbol": "KSh",
"usd_rate": "161.000000",
"is_primary": false,
"is_active": true,
"created_at": "2025-01-16T12:00:00Z",
"updated_at": "2025-02-19T14:30:00Z"
}
Example: 400 Bad Request (validation)
Example: 403 Forbidden (non-staff)
Example: 404 Not Found
Example: 204 No Content — Empty response body.
Example: 403 Forbidden (non-staff)
Example: 404 Not Found
| Field | Type | Required | Description |
|---|---|---|---|
code |
string | Yes | Currency code (e.g. USD, KES). Unique. |
name |
string | No | Display name (e.g. US Dollar) |
symbol |
string | No | Symbol (e.g. $, KSh) |
usd_rate |
string | No | Conversion rate: 1 USD = usd_rate × this currency. Default 1. |
is_primary |
boolean | No | Primary currency (typically USD). Only one can be primary. |
is_active |
boolean | No | If false, currency is excluded from product conversion. Default true. |
Permissions Summary¶
| Resource | Read | Create | Update | Delete |
|---|---|---|---|---|
| Currencies | Anyone | Staff only | Staff only | Staff only |
| Brands | Anyone | Staff only | Staff only | Staff only |
| Categories | Anyone | Staff only | Staff only | Staff only |
| Subcategories | Anyone | Staff only | Staff only | Staff only |
| Products | Anyone | Staff only | Staff only | Staff only |
| Variants | Anyone | Staff only | Staff only | Staff only |
| Product Reviews | Anyone | Authenticated (must have completed order with product) | Owner or staff | Owner or staff |
| Variation Attributes | Anyone | Staff only | Staff only | Staff only |
| Variation Options | Anyone | Staff only | Staff only | Staff only |
Brands¶
Brands can have multiple images using aligned arrays:
image_urls: public image URLsimage_refs: storage keys/paths in the same order asimage_urlsimage_labels: labels in the same order asimage_urls, for examplelogoorbanner one
Example: 200 OK
[
{
"id": 1,
"name": "Nature Made",
"image_urls": [
"https://cdn.example.com/brands/nature-made-logo.png",
"https://cdn.example.com/brands/nature-made-banner.png"
],
"image_refs": [
"brands/nature-made-logo.png",
"brands/nature-made-banner.png"
],
"image_labels": ["logo", "banner"],
"description": "Trusted wellness and supplement brand."
}
]
Example: Error — List is public; no 403.
Example: 200 OK
{
"id": 1,
"name": "Nature Made",
"image_urls": [
"https://cdn.example.com/brands/nature-made-logo.png",
"https://cdn.example.com/brands/nature-made-banner.png"
],
"image_refs": [
"brands/nature-made-logo.png",
"brands/nature-made-banner.png"
],
"image_labels": ["logo", "banner"],
"description": "Trusted wellness and supplement brand."
}
Example: 404 Not Found
{
"name": "Nature Made",
"image_urls": [
"https://cdn.example.com/brands/nature-made-logo.png",
"https://cdn.example.com/brands/nature-made-banner.png"
],
"image_refs": [
"brands/nature-made-logo.png",
"brands/nature-made-banner.png"
],
"image_labels": ["logo", "banner"],
"description": "Trusted wellness and supplement brand."
}
Example: 201 Created
{
"id": 1,
"name": "Nature Made",
"image_urls": [
"https://cdn.example.com/brands/nature-made-logo.png",
"https://cdn.example.com/brands/nature-made-banner.png"
],
"image_refs": [
"brands/nature-made-logo.png",
"brands/nature-made-banner.png"
],
"image_labels": ["logo", "banner"],
"description": "Trusted wellness and supplement brand."
}
Example: 400 Bad Request (validation)
Example: 403 Forbidden (non-staff)
{
"name": "Nature Made",
"image_urls": [
"https://cdn.example.com/brands/nature-made-logo.png",
"https://cdn.example.com/brands/nature-made-banner-v2.png"
],
"image_refs": [
"brands/nature-made-logo.png",
"brands/nature-made-banner-v2.png"
],
"image_labels": ["logo", "banner"],
"description": "Updated brand description."
}
PUTexpects the full object.PATCHcan send only the fields you want to change.
Example: 200 OK
{
"id": 1,
"name": "Nature Made",
"image_urls": [
"https://cdn.example.com/brands/nature-made-logo.png",
"https://cdn.example.com/brands/nature-made-banner-v2.png"
],
"image_refs": [
"brands/nature-made-logo.png",
"brands/nature-made-banner-v2.png"
],
"image_labels": ["logo", "banner"],
"description": "Updated brand description."
}
Example: 403 Forbidden (non-staff)
Example: 404 Not Found
Example: 204 No Content — Empty response body.
Example: 403 Forbidden (non-staff)
Example: 404 Not Found
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | Brand name. |
image_urls |
array[string] | No | Public image URLs for the brand. |
image_refs |
array[string] | No | Storage keys/paths for brand images, in the same order as image_urls. |
image_labels |
array[string] | No | Labels for each brand image, in the same order as image_urls. |
description |
string | No | Brand description. |
Categories¶
Example: 200 OK
[
{
"id": 1,
"name": "Supplements",
"description": "All supplement products"
},
{
"id": 2,
"name": "Vitamins",
"description": "Vitamin products"
}
]
Example: Error — List is public; no 403.
Example: 200 OK
Example: 404 Not Found
Example: 201 Created
Example: 400 Bad Request (validation)
Example: 403 Forbidden (non-staff)
PUTexpects the full object.PATCHcan send only the fields you want to change.
Example: 200 OK
Example: 403 Forbidden (non-staff)
Example: 404 Not Found
Subcategories¶
Example: 200 OK
Example: Error — List is public; no 403.
Example: 200 OK
Example: 404 Not Found
Example: 201 Created
Example: 400 Bad Request (validation)
Example: 403 Forbidden (non-staff)
Example: 200 OK
Example: 403 Forbidden (non-staff)
Example: 404 Not Found
Products¶
Product sku and barcode are optional. When provided, barcode must be unique across all products.
Optional content fields (all strings unless noted): featured (boolean, default false), benefits, ingredients, how_to_use. Staff can set them on create/update; they are returned on list/retrieve.
Non-staff: use ?currency=CODE for converted prices; display_currency and currency_symbol are always included (default: primary/USD).
Example: 200 OK
[
{
"id": 1,
"name": "Vitamin C 1000mg",
"description": "High-strength Vitamin C",
"brand": 1,
"brand_details": {
"id": 1,
"name": "Nature Made",
"image_urls": [
"https://cdn.example.com/brands/nature-made-logo.png",
"https://cdn.example.com/brands/nature-made-banner.png"
],
"image_refs": [
"brands/nature-made-logo.png",
"brands/nature-made-banner.png"
],
"image_labels": ["logo", "banner"],
"description": "Trusted wellness and supplement brand."
},
"price": "19.99",
"display_currency": "USD",
"currency_symbol": "$",
"stock": 100,
"sku": "VIT-C-1000",
"barcode": "0123456789001",
"image_urls": ["https://cdn.example.com/p/vit-c-1.png"],
"image_refs": ["p/vit-c-1.png"],
"featured": true,
"benefits": "Supports immune health and antioxidant protection.",
"ingredients": "Ascorbic acid, microcrystalline cellulose, magnesium stearate.",
"how_to_use": "Take one tablet daily with food, or as directed by your healthcare provider.",
"subcategories": [1, 2],
"meta_title": "Vitamin C 1000mg",
"meta_description": "High-strength Vitamin C for immune support",
"slug": "vitamin-c-1000mg",
"avg_rating": "4.25",
"review_count": 12,
"created_at": "2025-01-15T10:00:00Z",
"updated_at": "2025-01-15T10:00:00Z",
"variants": [
{
"id": 1,
"product": 1,
"options": [10, 11],
"options_details": [
{ "id": 10, "attribute": 1, "attribute_name": "Color", "value": "Red" },
{ "id": 11, "attribute": 2, "attribute_name": "Size", "value": "XL" }
],
"sku": "SKU-RED-XL",
"barcode": "0123456789012",
"price": "24.99",
"stock": 10,
"image_urls": ["https://cdn.example.com/p/vit-c-red-xl.png"],
"image_refs": ["p/vit-c-red-xl.png"],
"is_active": true,
"created_at": "2025-01-15T10:00:00Z",
"updated_at": "2025-01-15T10:00:00Z",
"avg_rating": "4.25",
"review_count": 12,
"reviews": [],
"display_currency": "USD",
"currency_symbol": "$"
}
],
"reviews": [
{
"id": 1,
"product": 1,
"order": 42,
"user": 5,
"user_display": "Jane",
"rating": 5,
"body": "Great product.",
"is_anonymous": false,
"created_at": "2025-02-01T12:00:00Z",
"updated_at": "2025-02-01T12:00:00Z"
}
]
}
]
Example: Error — List is public; no 403.
Response includes a nested variants array. Non-staff: use ?currency=CODE for converted price; display_currency and currency_symbol are always included.
Example: 200 OK (non-staff, with currency)
Request: GET /api/products/products/1/?currency=KES (product stored price: 19.99 USD; KES usd_rate: 160.50)
{
"id": 1,
"name": "Vitamin C 1000mg",
"description": "High-strength Vitamin C",
"brand": 1,
"brand_details": {
"id": 1,
"name": "Nature Made",
"image_urls": [
"https://cdn.example.com/brands/nature-made-logo.png",
"https://cdn.example.com/brands/nature-made-banner.png"
],
"image_refs": [
"brands/nature-made-logo.png",
"brands/nature-made-banner.png"
],
"image_labels": ["logo", "banner"],
"description": "Trusted wellness and supplement brand."
},
"price": "3208.40",
"display_currency": "KES",
"currency_symbol": "KSh",
"stock": 100,
"sku": "VIT-C-1000",
"barcode": "0123456789001",
"image_urls": ["https://cdn.example.com/p/vit-c-1.png"],
"image_refs": ["p/vit-c-1.png"],
"featured": true,
"benefits": "Supports immune health and antioxidant protection.",
"ingredients": "Ascorbic acid, microcrystalline cellulose, magnesium stearate.",
"how_to_use": "Take one tablet daily with food, or as directed by your healthcare provider.",
"avg_rating": "4.25",
"review_count": 12,
"variants": [
{
"id": 1,
"price": "4010.50",
"display_currency": "KES",
"currency_symbol": "KSh",
"barcode": "0123456789012",
"image_urls": ["https://cdn.example.com/p/vit-c-red-xl.png"],
"image_refs": ["p/vit-c-red-xl.png"],
"avg_rating": "4.25",
"review_count": 12,
"reviews": []
}
],
"reviews": [
{
"id": 1,
"product": 1,
"order": 42,
"user": 5,
"user_display": "Jane",
"rating": 5,
"body": "Great product.",
"is_anonymous": false,
"created_at": "2025-02-01T12:00:00Z",
"updated_at": "2025-02-01T12:00:00Z"
}
]
}
Example: 404 Not Found
Request body: name, description, price, and stock are required; brand, sku, barcode, featured, benefits, ingredients, how_to_use, and other fields are optional (when provided, barcode must be unique).
{
"name": "Vitamin C 1000mg",
"description": "High-strength Vitamin C",
"brand": 1,
"price": "19.99",
"cost_price": "12.00",
"stock": 100,
"sku": "VIT-C-1000",
"barcode": "0123456789001",
"image_urls": ["https://cdn.example.com/p/vit-c-1.png"],
"image_refs": ["p/vit-c-1.png"],
"featured": true,
"benefits": "Supports immune health and antioxidant protection.",
"ingredients": "Ascorbic acid, microcrystalline cellulose, magnesium stearate.",
"how_to_use": "Take one tablet daily with food, or as directed by your healthcare provider.",
"subcategories": [1, 2],
"meta_title": "Vitamin C 1000mg",
"meta_description": "High-strength Vitamin C for immune support"
}
Example: 201 Created
{
"id": 1,
"name": "Vitamin C 1000mg",
"description": "High-strength Vitamin C",
"brand": 1,
"brand_details": {
"id": 1,
"name": "Nature Made",
"image_urls": [
"https://cdn.example.com/brands/nature-made-logo.png",
"https://cdn.example.com/brands/nature-made-banner.png"
],
"image_refs": [
"brands/nature-made-logo.png",
"brands/nature-made-banner.png"
],
"image_labels": ["logo", "banner"],
"description": "Trusted wellness and supplement brand."
},
"price": "19.99",
"cost_price": "12.00",
"stock": 100,
"sku": "VIT-C-1000",
"barcode": "0123456789001",
"image_urls": ["https://cdn.example.com/p/vit-c-1.png"],
"image_refs": ["p/vit-c-1.png"],
"featured": true,
"benefits": "Supports immune health and antioxidant protection.",
"ingredients": "Ascorbic acid, microcrystalline cellulose, magnesium stearate.",
"how_to_use": "Take one tablet daily with food, or as directed by your healthcare provider.",
"subcategories": [1, 2],
"meta_title": "Vitamin C 1000mg",
"meta_description": "High-strength Vitamin C for immune support",
"slug": "vitamin-c-1000mg",
"avg_rating": null,
"review_count": 0,
"created_at": "2025-01-15T10:00:00Z",
"updated_at": "2025-01-15T10:00:00Z",
"variants": [],
"reviews": [],
"display_currency": "USD",
"currency_symbol": "$"
}
avg_ratingandreview_countare read-only; they are updated automatically when reviews are added, updated, or deleted.
Example: 400 Bad Request (validation)
{
"name": ["This field may not be blank."],
"price": ["A valid number is required."],
"description": ["This field may not be blank."]
}
Example: 403 Forbidden (non-staff)
{
"name": "Vitamin C 1000mg",
"description": "Updated description",
"brand": 1,
"price": "21.99",
"cost_price": "13.00",
"stock": 80,
"sku": "VIT-C-1000",
"barcode": "0123456789001",
"image_urls": ["https://cdn.example.com/p/vit-c-1.png"],
"image_refs": ["p/vit-c-1.png"],
"featured": false,
"benefits": "Updated benefits copy.",
"ingredients": "Updated ingredients list.",
"how_to_use": "Updated usage instructions.",
"subcategories": [1],
"meta_title": "Vitamin C 1000mg",
"meta_description": "Updated meta description"
}
slug,avg_rating,review_count,created_at, andupdated_atare read-only.skuandbarcodeare optional.- Variants are managed via the variants endpoints (see below).
Example: 200 OK
{
"id": 1,
"name": "Vitamin C 1000mg",
"description": "Updated description",
"brand": 1,
"brand_details": {
"id": 1,
"name": "Nature Made",
"image_urls": [
"https://cdn.example.com/brands/nature-made-logo.png",
"https://cdn.example.com/brands/nature-made-banner.png"
],
"image_refs": [
"brands/nature-made-logo.png",
"brands/nature-made-banner.png"
],
"image_labels": ["logo", "banner"],
"description": "Trusted wellness and supplement brand."
},
"price": "21.99",
"cost_price": "13.00",
"stock": 80,
"sku": "VIT-C-1000",
"barcode": "0123456789001",
"image_urls": ["https://cdn.example.com/p/vit-c-1.png"],
"image_refs": ["p/vit-c-1.png"],
"featured": false,
"benefits": "Updated benefits copy.",
"ingredients": "Updated ingredients list.",
"how_to_use": "Updated usage instructions.",
"subcategories": [1],
"meta_title": "Vitamin C 1000mg",
"meta_description": "Updated meta description",
"slug": "vitamin-c-1000mg",
"avg_rating": "4.25",
"review_count": 12,
"created_at": "2025-01-15T10:00:00Z",
"updated_at": "2025-02-19T14:30:00Z",
"variants": [],
"reviews": [],
"display_currency": "USD",
"currency_symbol": "$"
}
Example: 400 Bad Request (validation)
Example: 403 Forbidden (non-staff)
Example: 404 Not Found
Example: 204 No Content — Empty response body.
Example: 403 Forbidden (non-staff)
Example: 404 Not Found
| Field | Type | Description |
|---|---|---|
brand |
integer or null | Brand ID assigned to the product. |
brand_details |
object or null | Nested brand object returned in product responses. |
featured |
boolean | When true, product can be highlighted (e.g. featured section). Default false. |
benefits |
string | Product benefits / marketing copy. |
ingredients |
string | Ingredients or composition. |
how_to_use |
string | Usage instructions. |
avg_rating |
string (decimal) or null | Average of all review ratings (0–5). Updated when reviews change. |
review_count |
integer | Number of reviews. Updated when reviews change. |
reviews |
array | Up to 10 most recent reviews (see Product Reviews below). |
Product Reviews¶
Reviews are attached to a product and shared across all its variants. They are linked to a completed order: only users who have purchased the product (via a fully paid, placed order containing that product) can submit a review. Rating is 0–5. One review per user per product. The product’s avg_rating and review_count are updated automatically on every review create/update/delete.
- Create: Requires authentication and an
orderthat belongs to you, is placed, is fully paid, and contains the product. You can submit at most one review per product. - user_display: Shown as the reviewer’s first name (or "Customer" if missing). If the review is anonymous (
is_anonymous: true),user_displayis redacted as "Anonymous".
Optional query: ?product={id} to filter by product.
Example: 200 OK
[
{
"id": 1,
"product": 1,
"order": 42,
"user": 5,
"user_display": "Jane",
"rating": 5,
"body": "Great product, fast delivery.",
"is_anonymous": false,
"created_at": "2025-02-01T12:00:00Z",
"updated_at": "2025-02-01T12:00:00Z"
},
{
"id": 2,
"product": 1,
"order": 38,
"user": 7,
"user_display": "Anonymous",
"rating": 4,
"body": "Good value.",
"is_anonymous": true,
"created_at": "2025-02-02T09:00:00Z",
"updated_at": "2025-02-02T09:00:00Z"
}
]
Example: Error — List is public; no 403.
Example: 200 OK
{
"id": 1,
"product": 1,
"order": 42,
"user": 5,
"user_display": "Jane",
"rating": 5,
"body": "Great product, fast delivery.",
"is_anonymous": false,
"created_at": "2025-02-01T12:00:00Z",
"updated_at": "2025-02-01T12:00:00Z"
}
Example: 404 Not Found
Requires authentication. You must have a completed order (status PLACED, fully paid) that contains the product. One review per user per product.
product(required): Product ID you are reviewing.order(required): Order ID that contains this product (must be your order, placed, and fully paid).rating(required): 0–5 (inclusive).body(optional): Review text.is_anonymous(optional): Iftrue,user_displayis shown as "Anonymous". Defaultfalse.
Example: 201 Created
{
"id": 1,
"product": 1,
"order": 42,
"user": 5,
"user_display": "Jane",
"rating": 5,
"body": "Great product.",
"is_anonymous": false,
"created_at": "2025-02-01T12:00:00Z",
"updated_at": "2025-02-01T12:00:00Z"
}
Example: 400 Bad Request (validation)
Example: 401 Unauthorized
Only the review owner or staff can update (e.g. rating, body, is_anonymous).
Example: 200 OK
{
"id": 1,
"product": 1,
"order": 42,
"user": 5,
"user_display": "Jane",
"rating": 4,
"body": "Updated review text.",
"is_anonymous": false,
"created_at": "2025-02-01T12:00:00Z",
"updated_at": "2025-02-19T14:30:00Z"
}
Example: 403 Forbidden (not owner and not staff)
Example: 404 Not Found
Only the review owner or staff can delete.
Example: 204 No Content — Empty response body.
Example: 403 Forbidden (not owner and not staff)
Example: 404 Not Found
| Field | Type | Required (create) | Description |
|---|---|---|---|
product |
integer | Yes | Product ID. |
order |
integer | Yes | Order ID; must be your order, status PLACED, fully paid, and contain the product. |
rating |
integer | Yes | Rating 0–5 (inclusive). |
body |
string | No | Optional review text. |
is_anonymous |
boolean | No | If true, user_display is shown as "Anonymous". Default false. |
user |
integer | No | Read-only; set from request. |
user_display |
string | — | First name of the reviewer, or "Customer", or "Anonymous" if is_anonymous. |
Variation Attributes (e.g. Color, Size)¶
Example: 200 OK
Example: Error — List is public; no 403.
Variation Options (e.g. Color=Red)¶
Example: 200 OK
[
{ "id": 10, "attribute": 1, "attribute_name": "Color", "value": "Red" },
{ "id": 11, "attribute": 2, "attribute_name": "Size", "value": "XL" }
]
Example: Error — List is public; no 403.
Example: 201 Created
Example: 400 Bad Request (validation)
Example: 403 Forbidden (non-staff)
Example: 200 OK
Example: 403 Forbidden (non-staff)
Example: 404 Not Found
Variants¶
Variant sku and barcode are optional. When provided, barcode must be unique across all variants.
Non-staff: display_currency and currency_symbol always included; use ?currency=CODE to convert.
Example: 200 OK
[
{
"id": 1,
"product": 1,
"options": [10, 11],
"options_details": [
{ "id": 10, "attribute": 1, "attribute_name": "Color", "value": "Red" },
{ "id": 11, "attribute": 2, "attribute_name": "Size", "value": "XL" }
],
"sku": "SKU-RED-XL",
"barcode": "0123456789012",
"price": "24.99",
"stock": 10,
"image_urls": ["https://cdn.example.com/p/vit-c-red-xl.png"],
"image_refs": ["p/vit-c-red-xl.png"],
"is_active": true,
"created_at": "2025-01-15T10:00:00Z",
"updated_at": "2025-01-15T10:00:00Z",
"avg_rating": "4.25",
"review_count": 12,
"reviews": [],
"display_currency": "USD",
"currency_symbol": "$"
}
]
Variants include the parent product’s avg_rating, review_count, and up to 10 reviews (same as on the product).
Example: Error — List is public; no 403.
Non-staff: display_currency and currency_symbol always included; use ?currency=CODE to convert. Response includes the product’s avg_rating, review_count, and reviews.
Example: 200 OK
{
"id": 1,
"product": 1,
"options": [10, 11],
"options_details": [
{ "id": 10, "attribute": 1, "attribute_name": "Color", "value": "Red" },
{ "id": 11, "attribute": 2, "attribute_name": "Size", "value": "XL" }
],
"sku": "SKU-RED-XL",
"barcode": "0123456789012",
"price": "24.99",
"stock": 10,
"image_urls": ["https://cdn.example.com/p/vit-c-red-xl.png"],
"image_refs": ["p/vit-c-red-xl.png"],
"is_active": true,
"created_at": "2025-01-15T10:00:00Z",
"updated_at": "2025-01-15T10:00:00Z",
"avg_rating": "4.25",
"review_count": 12,
"reviews": [
{
"id": 1,
"product": 1,
"order": 42,
"user": 5,
"user_display": "Jane",
"rating": 5,
"body": "Great product.",
"is_anonymous": false,
"created_at": "2025-02-01T12:00:00Z",
"updated_at": "2025-02-01T12:00:00Z"
}
],
"display_currency": "USD",
"currency_symbol": "$"
}
Example: 404 Not Found
Request body: product is required; options, sku, barcode, price, cost_price, stock, image_urls, image_refs, is_active are optional. sku and barcode may be omitted or null.
{
"product": 1,
"options": [10, 11],
"sku": "SKU-RED-XL",
"barcode": "0123456789012",
"price": "24.99",
"cost_price": "15.00",
"stock": 10,
"image_urls": ["https://cdn.example.com/p/vit-c-red-xl.png"],
"image_refs": ["p/vit-c-red-xl.png"],
"is_active": true
}
Example: 201 Created
{
"id": 1,
"product": 1,
"options": [10, 11],
"options_details": [
{ "id": 10, "attribute": 1, "attribute_name": "Color", "value": "Red" },
{ "id": 11, "attribute": 2, "attribute_name": "Size", "value": "XL" }
],
"sku": "SKU-RED-XL",
"barcode": "0123456789012",
"price": "24.99",
"cost_price": "15.00",
"stock": 10,
"image_urls": ["https://cdn.example.com/p/vit-c-red-xl.png"],
"image_refs": ["p/vit-c-red-xl.png"],
"is_active": true,
"created_at": "2025-01-15T10:00:00Z",
"updated_at": "2025-01-15T10:00:00Z",
"avg_rating": "4.25",
"review_count": 12,
"reviews": [],
"display_currency": "USD",
"currency_symbol": "$"
}
Example: 400 Bad Request (validation)
{
"barcode": ["product variant with this barcode already exists."],
"product": ["This field is required."]
}
Example: 403 Forbidden (non-staff)
{
"product": 1,
"options": [10, 11],
"sku": "SKU-RED-XL",
"barcode": "0123456789012",
"price": "24.99",
"cost_price": "16.00",
"stock": 12,
"image_urls": ["https://cdn.example.com/p/vit-c-red-xl.png"],
"image_refs": ["p/vit-c-red-xl.png"],
"is_active": true
}
skuandbarcodeare optional; when provided,barcodemust be unique.
Example: 200 OK
{
"id": 1,
"product": 1,
"options": [10, 11],
"options_details": [
{ "id": 10, "attribute": 1, "attribute_name": "Color", "value": "Red" },
{ "id": 11, "attribute": 2, "attribute_name": "Size", "value": "XL" }
],
"sku": "SKU-RED-XL",
"barcode": "0123456789012",
"price": "24.99",
"cost_price": "16.00",
"stock": 12,
"image_urls": ["https://cdn.example.com/p/vit-c-red-xl.png"],
"image_refs": ["p/vit-c-red-xl.png"],
"is_active": true,
"created_at": "2025-01-15T10:00:00Z",
"updated_at": "2025-02-19T14:30:00Z",
"avg_rating": "4.25",
"review_count": 12,
"reviews": [],
"display_currency": "USD",
"currency_symbol": "$"
}
Example: 400 Bad Request (validation)
Example: 403 Forbidden (non-staff)
Example: 404 Not Found
Product and variant image fields¶
image_urls: Array of image URLs (display).image_refs: Array of storage keys/paths for the same images, in the same order asimage_urls.
Brand image fields¶
image_urls: Array of brand image URLs.image_refs: Array of storage keys/paths for the same brand images, in the same order asimage_urls.image_labels: Array of labels for the same brand images, in the same order asimage_urlsandimage_refs.
Product and variant rating fields¶
avg_rating: Stored on the product; shown on product and on each variant (from the product). Decimal 0–5, ornullif there are no reviews. Updated automatically when reviews are added, updated, or deleted.review_count: Number of reviews for the product; same value on product and variants.reviews: Array of up to 10 most recent reviews on the product; same list on product detail and on each variant (reviews are shared across variants).
Notes¶
- All product endpoints are public for reads.
- All writes (create/update/delete) require a staff user.
- Non-staff:
display_currencyandcurrency_symbolincluded by default (USD); use?currency=CODEto convert. - Product responses include
featured,benefits,ingredients, andhow_to_use; staff can set them when creating or updating a product. - Reviews use a 0–5 rating; one review per user per product when the user is set.