Custom attributes
Brands define their own typed attributes — volume, material, fragrance family, care instructions — and every product carries their values in attributes.
Types and values
| Type | Value in JSON | Example |
|---|---|---|
text | string | "Eau de parfum" |
textarea | string (multi-line) | "Top: bergamot\nHeart: rose" |
richtext | string (sanitized HTML) | "<p>Hand-poured</p>" |
integer | number (whole) | 100 |
decimal | number | 12.5 |
boolean | true / false | true |
select | option code | "woody-oriental" |
multiselect | list of option codes | ["cotton", "linen"] |
date | "yyyy-mm-dd" | "2026-10-01" |
url | http(s) URL | "https://example.com/sds.pdf" |
color | "#rrggbb" | "#8A5A2B" |
On write, values are coerced when unambiguous: "100" becomes 100 for integers, "yes"/"true"/"1" become true, a select accepts the option label or code and stores the code, and a multiselect accepts "a|b" or an array. Invalid values answer 422 with a field error such as attributes.volume_ml. Set a value to null to remove it.
Create an attribute
curl -X POST "https://library.retailcommerceai.com/api/v1/attributes" \
-H "Authorization: Bearer cl_live_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"code": "fragrance_family",
"label": "Fragrance family",
"type": "select",
"scope": "category",
"groupCode": "specifications",
"options": [
{
"code": "woody-oriental",
"label": "Woody oriental",
"translations": {
"ar": "خشبي شرقي"
}
},
{
"code": "floral",
"label": "Floral",
"translations": {
"ar": "زهري"
}
}
],
"translations": {
"ar": {
"label": "عائلة العطر"
}
}
}'codeis lowercase letters, digits and_; it is the key inproduct.attributesand in file columns (attr.fragrance_family). It cannot change once products use it.scope: "global"applies to every product;"category"only to products in the categories it is assigned to (and their subcategories).requiredcounts toward completeness;isVariantAxislets it distinguish variants (select, text, integer).validationaddsminLength,maxLength,pattern,min,maxordecimals.
Attributes per category
Assign category-scoped attributes to a category — optionally as required there — and every product in it or its subcategories gets them:
curl -X PUT "https://library.retailcommerceai.com/api/v1/categories/fragrance/attributes" \
-H "Authorization: Bearer cl_live_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"attributes": [
{
"attribute": "fragrance_family",
"required": true
},
{
"attribute": "volume_ml",
"required": true
}
]
}'GET /categories/{code}/attributes answers with the effective list (global plus the category chain) and each attribute's effectiveRequired flag — exactly what a product in that category must fill.
Localized values
Text attributes marked localizable have a value per language: the default one in attributes, others in translations.<locale>.attributes. Select option labels are translated on the attribute definition (options[].translations) while products store the language-neutral option code.
curl -X PATCH "https://library.retailcommerceai.com/api/v1/products/FR-OUD-100" \
-H "Authorization: Bearer cl_live_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"attributes": {
"scent_notes": "Oud, amber, saffron"
},
"translations": {
"ar": {
"attributes": {
"scent_notes": "عود، عنبر، زعفران"
}
}
}
}'Deleting is permanent