Products Guide - Styles, Variants & SKUs
How to create, read, update, and archive products through the Retraced public API. Everything goes through one unified resource - /styles - which serves all three levels of the product hierarchy. If you only read one section, read The Mental Model: once you understand how the API detects the product level and how field inheritance works, every endpoint behaves predictably.
Setup
BASE_URL="https://publicapi.retraced.com/api/v2"
API_KEY="your-api-key" # provided by Retraced; wrap in single quotes if it contains $
Requests are authenticated with the API key header:
curl "$BASE_URL/styles?level=style" \
-H "companyapikey: $API_KEY"
Important: If your API key contains
$characters, use single quotes around its value to prevent the shell from interpreting it as a variable:-H 'companyapikey: $2b$10$...' # correct - single quotes -H "companyapikey: $2b$10$..." # wrong - shell expands $2b to empty string → 401
All responses use the standard envelope:
{
"metadata": { "success": true },
"data": { }
}
Errors come back in the same envelope with metadata.success: false plus a machine-readable code:
{
"metadata": { "success": false },
"statusCode": 400,
"code": "mixed_levels",
"message": "All IDs must be the same product level. Found mixed levels: style, variant"
}
The Mental Model
One endpoint, three levels
Products form a 3-level hierarchy:
Style "Summer Dress" ── the product definition
└── Variant "Summer Dress - Blue" ── a colorway
└── SKU "Summer Dress - Blue - M" ── a sellable size/dimension
There are no separate /variants or /skus endpoints. All three levels live under /styles, and the API determines the level for you:
| Operation | How the level is determined |
|---|---|
POST /styles |
By parentId: omitted → style; a style ID → variant; a variant ID → SKU; a SKU ID → 400 invalid_parent_level |
GET /styles/:id, PUT /styles/:id |
Looked up from the ID - pass any level's ID, the right handler is chosen automatically |
GET /styles (list) |
By the level query parameter (style is the default) |
POST /styles/archive, /styles/unarchive |
From the IDs in the body - all IDs must be the same level (400 mixed_levels otherwise) |
Field inheritance: null means "inherit"
For these fields, variants and SKUs inherit the parent's value whenever their own value is null:
description, gender, brands, departments, seasons, collections,
customProperties, suppliers, mainSupplier, vendors, claimIntentions
Inheritance is resolved at read time - there is no copying. Consequences:
- A variant created without
descriptionreturns its style's description, and keeps tracking it: update the style and the variant's reads change immediately. - Setting a value on a variant/SKU overrides the parent from then on.
- Setting the field back to
nullin aPUTre-enables inheritance - this is the only way to "un-override". - Variant and SKU responses include a read-only
overriddenFlagarray naming the fields that are currently overridden at that level.
Materials and BOMs follow a different inheritance model (shared links with copy-on-write) - see the Materials Guide and BOM Guide.
Codes identify products across systems
Every product carries one or more codes - external identifiers like ERP numbers or EAN barcodes (codes: [{ "codeId": "ERP_ID", "codeValue": "DRS-001" }]). At least one code is required at every level, and code values must be unique per code type within your catalog. See Code Types.
externalId: address products by your own ID
Codes describe a product; externalId addresses it. Set externalId when you create a product and you can then use your own identifier anywhere this API expects a product ID - no mapping table on your side:
# Create with your own ID
curl -X POST "$BASE/styles" -H "companyapikey: $KEY" -H 'Content-Type: application/json' -d '{
"name": "Summer Dress 2024",
"externalId": "DRS-2024-001",
"styleTypeId": "DRESSES",
"isComponent": false,
"codes": [{ "codeId": "ERP_ID", "codeValue": "DRS-2024-001" }]
}'
# Then read, update, archive or reference it by that ID
curl "$BASE/styles/DRS-2024-001" -H "companyapikey: $KEY"
# ...including as a parent
curl -X POST "$BASE/styles" -H "companyapikey: $KEY" -H 'Content-Type: application/json' -d '{
"name": "Summer Dress 2024 - Blue",
"parentId": "DRS-2024-001",
"externalId": "DRS-2024-001-BLUE",
"color": "Blue",
"codes": [{ "codeId": "ERP_ID", "codeValue": "DRS-2024-001-BLUE" }]
}'
Rules:
- Unique within your company across all three levels - a style and a SKU cannot share one. Duplicates fail with
409 external_id_already_exists. - 1–255 characters,
A-Z a-z 0-9 _ . ~ : @ -, at least one letter or digit, and it may not start withstyle_,variant_orsku_(reserved for Retraced IDs). - Changeable on
PUT /styles/:id.PUTreplaces the product, butexternalIdis the one field exempt from that: omitting it keeps the stored value, and it is only cleared by sending"externalId": null. - Responses return
externalIdnext toid. Every ID that points at another record (parentId,styleId,variantId, …) is always a Retraced ID. - Works on
/styles,/bom-headers,/bom-lines,/material-headers,/material-lines,/orders(lines[].styleId) and/supply-chains/:id. - Not set by style bulk import - bulk-imported products have
externalId: null. - If a value happens to match both one of your
externalIds and some other company's product ID, your own product always wins.
Endpoint Reference
| Endpoint | Purpose |
|---|---|
GET /styles?level=... |
List products at one hierarchy level |
GET /styles/:id |
Get one product (any level, auto-detected) |
POST /styles |
Create a style, variant, or SKU (level from parentId) |
PUT /styles/:id |
Update a product (any level, auto-detected) |
POST /styles/archive |
Archive products (cascades to children) |
POST /styles/unarchive |
Unarchive products (cascade optional) |
GET /style-types |
Look up styleTypeId values (product categorization) |
GET /style-properties?category=... |
Look up brand / department / season / collection / custom property IDs |
GET /facility-processes |
Look up facility process IDs (used on orders and supply chains) |
GET /production-processes |
Look up production process IDs (the parent stage of a facility process) |
Before You Create: Look Up Reference IDs
Creating a style requires a styleTypeId, and fields like brands take IDs - not names. Fetch the valid IDs first. If you have mapped your own product-type codes to Retraced style types (in the Retraced dashboard under Company settings → API → Constants Mapping → Product Types, or with POST /api/v2/constant-mappings), styleTypeId also accepts such a code; a Retraced ID always wins over a code with the same text, and an unmapped code fails like an unknown ID.
# Product type hierarchy → styleTypeId
curl "$BASE_URL/style-types" \
-H "companyapikey: $API_KEY"
# Brands, departments, seasons, collections, custom properties → property IDs
curl "$BASE_URL/style-properties?category=brands" \
-H "companyapikey: $API_KEY"
suppliers, mainSupplier, and vendors take company IDs from your Retraced network.
Two more ID lists are shared platform-wide rather than owned by your account - you'll need them when working with orders and supply chains:
# Facility process IDs → buyerFacilityProcess / supplierFacilityProcess / receiverFacilityProcess on orders
curl "$BASE_URL/facility-processes?limit=100" \
-H "companyapikey: $API_KEY"
# Production process IDs → the parent stage of a facility process
curl "$BASE_URL/production-processes" \
-H "companyapikey: $API_KEY"
See Reference data for the full list of ID lookup endpoints.
Custom properties: labels you define yourself
brands, departments, seasons and collections have a fixed meaning. customProperties is a fifth list with no meaning in Retraced at all - it holds whatever labels your company wants to tag products with (a capsule name, an internal range, a sourcing programme). The list is yours alone: values are scoped to your company and unique by exact value.
Create a value once, then reference its ID on products:
# 1. Create the label (409 style_property_already_exists if the value already exists)
curl -X POST "$BASE_URL/style-properties" \
-H "companyapikey: $API_KEY" \
-H "Content-Type: application/json" \
-d '{ "value": "Capsule 2027", "category": "customProperties" }'
# 2. Look the IDs up later
curl "$BASE_URL/style-properties?category=customProperties" \
-H "companyapikey: $API_KEY"
# 3. Attach to a product (create or update) - a single ID or an array
curl -X PUT "$BASE_URL/styles/$STYLE_ID" \
-H "companyapikey: $API_KEY" \
-H "Content-Type: application/json" \
-d '{ "customProperties": ["YOUR_CUSTOM_PROPERTY_ID"] }'
The field behaves like brands from here on: the response always returns an array, an unknown ID is a 404, variants and SKUs inherit the style's values until they set their own, and include=styleProperties returns the resolved labels under styleProperties.customProperties.
Style properties by your own ID
Give a style property an externalId when you create it, and you can use that value wherever a property ID is expected, with no lookup needed:
# 1. Create the property with your own ID (409 external_id_already_exists if another property already holds it)
curl -X POST "$BASE_URL/style-properties" \
-H "companyapikey: $API_KEY" \
-H "Content-Type: application/json" \
-d '{ "value": "Acme", "category": "brands", "externalId": "ERP-BRAND-ACME" }'
# 2. Use it in brands / departments / seasons / collections / customProperties on POST and PUT /styles
curl -X PUT "$BASE_URL/styles/$STYLE_ID" \
-H "companyapikey: $API_KEY" \
-H "Content-Type: application/json" \
-d '{ "brands": ["ERP-BRAND-ACME"], "...": "..." }'
# 3. Find the property again, or list the products that carry it
curl "$BASE_URL/style-properties?externalId=ERP-BRAND-ACME" \
-H "companyapikey: $API_KEY"
curl "$BASE_URL/styles?propertyExternalIds=ERP-BRAND-ACME" \
-H "companyapikey: $API_KEY"
- Uniqueness: an
externalIdis unique across all your style properties, whatever their category. - Create only: it can only be set on create. Style properties have no update endpoint.
- Stored as IDs: products store the Retraced ID, so responses return
stp_…IDs inbrandsand the other property fields.include=stylePropertiesadds each property'sexternalIdnext to itsid. - Category still checked: a value that is neither an ID nor an
externalIdof the right category is still a404.
Creating Products
Create a style (top level)
Required: name, codes, styleTypeId, isComponent. Omit parentId.
curl -X POST "$BASE_URL/styles" \
-H "companyapikey: $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Summer Dress",
"externalId": "DRS-001",
"styleTypeId": "YOUR_STYLE_TYPE_ID",
"isComponent": false,
"codes": [
{ "codeId": "ERP_ID", "codeValue": "DRS-001" }
],
"description": "Lightweight summer dress in organic cotton",
"gender": "FEMALE",
"brands": ["YOUR_BRAND_ID"],
"seasons": ["YOUR_SEASON_ID"],
"customProperties": ["YOUR_CUSTOM_PROPERTY_ID"],
"suppliers": ["SUPPLIER_COMPANY_ID"],
"mainSupplier": "SUPPLIER_COMPANY_ID",
"weight": { "value": 250, "unit": "GRAM" }
}'
Response (201): the created product, including its generated id:
{
"metadata": { "success": true },
"data": {
"id": "style_01KMZMMMW8JA3S1RVX1PS8E19S",
"externalId": "DRS-001",
"name": "Summer Dress",
"codes": [{ "codeId": "ERP_ID", "codeValue": "DRS-001" }],
"isComponent": false,
"isArchived": false,
"createdAt": "2026-06-01T08:00:00.000Z"
}
}
STYLE_ID="style_01KMZMMMW8JA3S1RVX1PS8E19S"
Because this style was created with "externalId": "DRS-001", every request below could equally use DRS-001 in place of $STYLE_ID - see externalId.
Field notes:
| Field | Notes |
|---|---|
isComponent |
true marks a component / raw material (e.g. a button) rather than a finished good - components are what you reference in BOM lines |
gender |
Free text (≤ 50 chars); convention: MALE, FEMALE, UNISEX |
weight |
{ "value": number, "unit": "GRAM" | "KILOGRAM" | ... } - both parts required; sending either as null stores null |
customProperties |
Style property IDs of category customProperties (a single ID string or an array; always returned as an array) - labels your company defines for itself, created via POST /style-properties with "category": "customProperties". Inherited by variants and SKUs like brands |
imagesFileIds |
File IDs from the Files API - see the File Upload Guide |
ecoFootPrint |
Recyclability and hazardous-substance data (hazardousMaterials, recyclabilityRate, …) |
externalId |
Your own identifier for this product, so you can address it later without storing our ID. Unique per company across all three levels; kept on a PUT that omits it, cleared only by "externalId": null |
forCompanyId |
Create the product on behalf of another company in your network - either its Retraced ID or the internal company code you assigned to it. The same applies to suppliers[], mainSupplier and vendors[] |
isStoryActive |
Enables the public traceability page - requires a supply chain attached to the product |
Create a variant
Set parentId to the style ID. Required: name, codes, color.
curl -X POST "$BASE_URL/styles" \
-H "companyapikey: $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Summer Dress - Blue",
"parentId": "'"$STYLE_ID"'",
"externalId": "DRS-001-BLU",
"codes": [{ "codeId": "ERP_ID", "codeValue": "DRS-001-BLU" }],
"color": "Blue"
}'
parentId also accepts the parent's externalId, so with the style above you can skip $STYLE_ID entirely and never store a Retraced ID:
curl -X POST "$BASE_URL/styles" \
-H "companyapikey: $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Summer Dress - Red",
"parentId": "DRS-001",
"externalId": "DRS-001-RED",
"codes": [{ "codeId": "ERP_ID", "codeValue": "DRS-001-RED" }],
"color": "Red"
}'
Fields you omit (or set null) - description, gender, brands, … - are inherited from the style at read time. Provide values only where the variant genuinely differs.
VARIANT_ID="variant_01KN6YZNVKNBN1TJ4MR9T7FFD0" # from the response
Create a SKU
Set parentId to the variant ID. Required: name, codes, dimension.
curl -X POST "$BASE_URL/styles" \
-H "companyapikey: $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Summer Dress - Blue - M",
"parentId": "'"$VARIANT_ID"'",
"externalId": "DRS-001-BLU-M",
"codes": [{ "codeId": "EAN_GTIN_13", "codeValue": "4006381333931" }],
"dimension": "M"
}'
Set externalId at every level and your whole catalogue stays addressable by your own IDs - GET /styles/DRS-001-BLU-M, GET /bom-headers?styleId=DRS-001, POST /orders with lines[].styleId = "DRS-001-BLU-M".
Required fields by level
| Level | parentId |
Required fields | Level-specific field |
|---|---|---|---|
| Style | (omitted) | name, codes, styleTypeId, isComponent |
- |
| Variant | a style ID or its externalId |
name, codes, color |
color |
| SKU | a variant ID or its externalId |
name, codes, dimension |
dimension |
externalId is optional at every level. Reusing one that another of your products already holds fails with 409 external_id_already_exists.
Missing styleTypeId or isComponent on a style fails with 400 missing_style_type_id / 400 missing_is_component. An unknown parentId fails with 404 parent_not_found; a SKU parentId with 400 invalid_parent_level.
Reading Products
Get by ID (any level)
curl "$BASE_URL/styles/$VARIANT_ID" \
-H "companyapikey: $API_KEY"
The level is auto-detected; inherited fields come back resolved (the variant's description shows the style's text if not overridden). Unknown IDs return 404 not_found.
List by level
# All styles (level=style is the default)
curl "$BASE_URL/styles?level=style&page=1&limit=50" \
-H "companyapikey: $API_KEY"
# Variants of one style
curl "$BASE_URL/styles?level=variant&styleId=$STYLE_ID" \
-H "companyapikey: $API_KEY"
# SKUs of one variant
curl "$BASE_URL/styles?level=sku&variantId=$VARIANT_ID" \
-H "companyapikey: $API_KEY"
List responses are paginated:
{
"metadata": {
"success": true,
"pagination": { "page": 1, "limit": 50, "total": 7, "totalPages": 1 }
},
"data": [ { "id": "style_01KMZ...", "name": "Summer Dress", "...": "..." } ]
}
Query parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
level |
string | style |
style, variant, sku |
page |
number | 1 | 1-based |
limit |
number | 20 | Max 100 |
sort |
string | createdAt |
createdAt, updatedAt, name, gender, typeName, color, styleId, dimension, variantId |
order |
string | desc |
asc, desc |
name |
string | - | Substring match on name |
nameOrCode |
string | - | Substring match (case-insensitive) on the name or any code value - ABC also matches ABCD. Works at every level, e.g. with level=sku to look up a SKU by its code |
codeId |
string | - | Filter by code type/ID, e.g. codeId=ERP_ID |
codeValue |
string | - | Filter by code value, e.g. codeValue=DRS-001. Combine: ?codeId=ERP_ID&codeValue=DRS-001 |
description |
string | - | Substring match |
gender |
string | - | e.g. FEMALE |
styleTypeId, styleLevelId |
string | - | Exact match |
brands, departments, seasons, collections, suppliers, vendors |
string | - | Comma-separated ID lists |
propertyExternalIds |
string | - | Comma-separated style property externalIds. Each one joins the brands, departments, seasons or collections filter of its category. Values that match nothing are ignored; if none match, the result is empty. A customProperties value is rejected with 400 unsupported_property_filter |
mainSupplier, companyId |
string | - | Exact match |
isComponent, isStoryActive, createdForMe |
boolean | - | true or false - see note below |
isArchived |
boolean | false |
true returns archived products only; false (the default) non-archived only |
updatedAfterUnixMs, updatedBeforeUnixMs |
number | - | Updated-time window (Unix ms) - useful for incremental syncs |
styleId |
string | - | Parent filter when level=variant |
color |
string | - | When level=variant |
variantId |
string | - | Parent filter when level=sku |
dimension |
string | - | When level=sku |
include |
string | - | Comma-separated related data - see below |
Boolean filters accept
trueorfalse. PassisComponent=false/isStoryActive=falseto filter for thefalsevalue, or omit the parameter to skip the filter. Any other value (e.g.1,yes) is rejected with a400.
Including related data
include pulls related records into each list result, and into a single product on GET /styles/{id}. Availability differs by level:
| Value | style | variant | sku | What you get |
|---|---|---|---|---|
type |
✓ | ✓ | ✓ | The style type (categorization) |
material |
✓ | ✓ | ✓ | Material composition headers |
bom |
✓ | ✓ | ✓ | BOM headers |
styleProperties |
✓ | ✓ | ✓ | Resolved brand / department / season / collection / custom property objects |
tracingStory |
✓ | ✓ | ✓ | The active supply-chain story attached to the product |
images |
- | ✓ | ✓ | Product images on list queries (not available at style level - see note below) |
style |
- | ✓ | ✓ | The parent style |
variant |
- | - | ✓ | The parent variant |
skuCount |
- | ✓ | - | Number of SKUs under the variant |
styleLink |
✓ | - | - | Linked styles |
Images:
include=imagesonly affects list queries, and only at variant/SKU level - style lists silently ignore it. You still get images for styles two ways: every list result carriesfeatureImageUrl(the product's first image), andGET /styles/{id}returns the fullimagesarray at every level without anyinclude.
curl "$BASE_URL/styles?level=sku&variantId=$VARIANT_ID&include=images,style,tracingStory" \
-H "companyapikey: $API_KEY"
On a single product (
GET /styles/{id})includeacceptsmaterial,bom,styleProperties,type,styleLink(styles) andstyle(variants and SKUs).imagesandtracingStoryare always returned there, so they need noinclude. A value the single-product read does not support is rejected with a400, unlike list queries, which ignore it.
curl "$BASE_URL/styles/$STYLE_ID?include=material,styleProperties" \
-H "companyapikey: $API_KEY"
Updating Products
PUT /styles/:id works on any level (auto-detected). Two rules matter:
codesis replaced wholesale. The codes array you send becomes the product's complete set of codes - send the full desired list every time, not just additions.nullre-enables inheritance on inheritable fields (variants/SKUs); omitting a field leaves it unchanged.
# Update the style's description and codes
curl -X PUT "$BASE_URL/styles/$STYLE_ID" \
-H "companyapikey: $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Summer Dress",
"description": "Updated description",
"codes": [
{ "codeId": "ERP_ID", "codeValue": "DRS-001" },
{ "codeId": "PLM_ID", "codeValue": "PLM-9999" }
]
}'
Override and re-inherit
# Variant: override brands (stop inheriting from the style)
curl -X PUT "$BASE_URL/styles/$VARIANT_ID" \
-H "companyapikey: $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Summer Dress - Blue",
"brands": ["DIFFERENT_BRAND_ID"],
"codes": [{ "codeId": "ERP_ID", "codeValue": "DRS-001-BLU" }]
}'
# Variant: reset to inherit brands from the style again
curl -X PUT "$BASE_URL/styles/$VARIANT_ID" \
-H "companyapikey: $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Summer Dress - Blue",
"brands": null,
"codes": [{ "codeId": "ERP_ID", "codeValue": "DRS-001-BLU" }]
}'
parentId and isComponent cannot be changed after creation (they are not part of the update body). styleLevelId can be set on update.
Archiving & Unarchiving
Both endpoints take up to 100 IDs, all of the same level, and return 204 No Content.
# Archive a style - ALWAYS cascades to its variants and SKUs
curl -X POST "$BASE_URL/styles/archive" \
-H "companyapikey: $API_KEY" \
-H "Content-Type: application/json" \
-d '{ "ids": ["'"$STYLE_ID"'"] }'
# Unarchive the style and all its children (default)
curl -X POST "$BASE_URL/styles/unarchive" \
-H "companyapikey: $API_KEY" \
-H "Content-Type: application/json" \
-d '{ "ids": ["'"$STYLE_ID"'"], "shouldUnarchiveChildren": true }'
# Unarchive only the style - children stay archived
curl -X POST "$BASE_URL/styles/unarchive" \
-H "companyapikey: $API_KEY" \
-H "Content-Type: application/json" \
-d '{ "ids": ["'"$STYLE_ID"'"], "shouldUnarchiveChildren": false }'
- Archive always cascades downward - archiving a style archives its variants and SKUs.
shouldUnarchiveChildrenhas no effect on archive. - Unarchive cascades by default; pass
shouldUnarchiveChildren: falseto restore only the listed products. - Archived products disappear from lists unless you pass
isArchived=true.
Full Lifecycle Script
#!/usr/bin/env bash
set -euo pipefail
BASE_URL="https://publicapi.retraced.com/api/v2"
API_KEY="your-api-key"
STYLE_TYPE_ID="your-style-type-id" # from GET /style-types
api_post() { curl -s -X POST "$BASE_URL$1" -H "companyapikey: $API_KEY" -H "Content-Type: application/json" -d "$2"; }
api_put() { curl -s -X PUT "$BASE_URL$1" -H "companyapikey: $API_KEY" -H "Content-Type: application/json" -d "$2"; }
api_get() { curl -s "$BASE_URL$1" -H "companyapikey: $API_KEY"; }
extract_id() { echo "$1" | grep -o '"id":"[^"]*"' | head -1 | cut -d'"' -f4; }
echo "=== Step 1: Create Style ==="
STYLE_RESP=$(api_post "/styles" '{
"name": "Demo Jacket",
"styleTypeId": "'"$STYLE_TYPE_ID"'",
"isComponent": false,
"codes": [{"codeId": "ERP_ID", "codeValue": "JKT-001"}],
"gender": "UNISEX",
"weight": {"value": 500, "unit": "GRAM"}
}')
STYLE_ID=$(extract_id "$STYLE_RESP")
echo "Created style: $STYLE_ID"
echo "=== Step 2: Create Variant (Red) ==="
VARIANT_RESP=$(api_post "/styles" '{
"name": "Demo Jacket - Red",
"parentId": "'"$STYLE_ID"'",
"codes": [{"codeId": "ERP_ID", "codeValue": "JKT-001-RED"}],
"color": "Red"
}')
VARIANT_ID=$(extract_id "$VARIANT_RESP")
echo "Created variant: $VARIANT_ID"
echo "=== Step 3: Create SKUs (S, M, L) ==="
for SIZE in S M L; do
SKU_RESP=$(api_post "/styles" '{
"name": "Demo Jacket - Red - '"$SIZE"'",
"parentId": "'"$VARIANT_ID"'",
"codes": [{"codeId": "ERP_ID", "codeValue": "JKT-001-RED-'"$SIZE"'"}],
"dimension": "'"$SIZE"'"
}')
echo "Created SKU ($SIZE): $(extract_id "$SKU_RESP")"
done
echo "=== Step 4: Update Style ==="
api_put "/styles/$STYLE_ID" '{
"name": "Demo Jacket (Updated)",
"description": "Premium demo jacket for integration testing",
"codes": [{"codeId": "ERP_ID", "codeValue": "JKT-001"}]
}' > /dev/null
echo "=== Step 5: Read back ==="
api_get "/styles/$STYLE_ID" | python3 -m json.tool
api_get "/styles?level=variant&styleId=$STYLE_ID" | python3 -m json.tool
api_get "/styles?level=sku&variantId=$VARIANT_ID" | python3 -m json.tool
echo "=== Step 6: Archive (cascades to all children) ==="
api_post "/styles/archive" '{"ids": ["'"$STYLE_ID"'"]}' > /dev/null
echo "=== Step 7: Unarchive everything ==="
api_post "/styles/unarchive" '{"ids": ["'"$STYLE_ID"'"], "shouldUnarchiveChildren": true}' > /dev/null
echo "=== Done ==="
Code Types
Single-entry (max one per product):
| Code ID | Meaning |
|---|---|
ERP_ID |
ERP system identifier |
PLM_ID |
Product Lifecycle Management ID |
MAIN_SUPPLIERS_ID |
Main supplier's internal ID |
PIM_ID |
Product Information Management ID |
UPC_GTIN_12 |
12-digit barcode |
EAN_GTIN_13 |
13-digit barcode |
ZALANDO_ID |
Zalando marketplace ID |
FASHIONCLOUD_ID |
FashionCloud ID |
Multi-entry (several per product, with an explicit numeric suffix you assign):
| Code ID | Example |
|---|---|
ALTERNATIVE_SUPPLIERS_ID<N> |
ALTERNATIVE_SUPPLIERS_ID1, ALTERNATIVE_SUPPLIERS_ID2, … |
OTHER_ID<N> |
OTHER_ID1, OTHER_ID2, … |
Rules:
- Unknown code IDs (including a multi-entry ID without its numeric suffix) fail with
400 invalid_code_ids. - Code values must be unique per code type across your catalog - duplicates fail with
400 codes_not_unique. PUTreplaces the full codes array (see Updating Products).
Error Reference
| Status / code | Where | Meaning |
|---|---|---|
401 token_or_api_key_invalid |
All endpoints | Missing or invalid companyapikey |
404 parent_not_found |
POST /styles |
parentId doesn't exist in your catalog |
400 invalid_parent_level |
POST /styles |
parentId is a SKU - SKUs cannot have children |
400 missing_style_type_id |
POST /styles |
Creating a style without styleTypeId |
400 missing_is_component |
POST /styles |
Creating a style without isComponent |
400 invalid_code_ids |
Create & update | Unknown code ID (check spelling and multi-entry suffix) |
400 codes_not_unique |
Create & update | A code value is already used by another product |
404 not_found |
GET/PUT /styles/:id |
Product ID doesn't exist |
404 not_found |
Archive / unarchive | None of the provided IDs were found |
400 mixed_levels |
Archive / unarchive | IDs span multiple product levels |
409 external_id_already_exists |
POST/PUT /styles |
externalId is already used by another of your products |
409 external_id_already_exists |
POST /style-properties |
externalId is already used by another of your style properties |
400 unsupported_property_filter |
GET /styles |
propertyExternalIds names a customProperties property |
Field limits: name ≤ 255 characters, description ≤ 4000, gender ≤ 50, color / dimension ≤ 255.
Gotchas Checklist
PUTreplacescodeswholesale. Always send the complete desired codes array - sending only the new code deletes the others.null≠ omitted in updates. Omitting a field keeps its value; sendingnullclears it - and on variants/SKUs that means re-inherit from the parent.- Inheritance is live. A variant that never overrode
descriptionreflects style edits immediately. CheckoverriddenFlagon variant/SKU responses to see what is overridden. - Archive always cascades; unarchive is optional.
shouldUnarchiveChildrenonly affects/styles/unarchive. - Boolean query filters accept only
trueorfalse(isComponent,isStoryActive,isArchived,createdForMe) - any other value is rejected with a400. Omit the parameter to skip the filter. styleTypeIdandisComponentare required for styles only - andisComponentcannot be changed later.- Multi-entry code IDs need a numeric suffix (
OTHER_ID1, notOTHER_ID). weightis all-or-nothing -valuewithoutunit(or vice versa) is stored asnull.include=imagesworks on variant/SKU lists only - style lists silently ignore it. UsefeatureImageUrl(present on every list result) orGET /styles/{id}, which returns the fullimagesarray at every level without anyinclude.- List
limitis capped at 100; archive/unarchive bodies at 100 IDs. - Use
updatedAfterUnixMsfor incremental syncs instead of re-pulling the whole catalog. externalIdsurvives aPUTthat omits it - unlike every other field on that endpoint. Clear it deliberately with"externalId": null.
Related Reading
- BOM Guide - Bills of Materials: shared links, ownership, copy-on-write
- Materials Guide - material composition trees, percentages, countries of origin
- File Upload Guide - obtaining file IDs for
imagesFileIds - API Reference - interactive endpoint and schema documentation
- All integration guides