Skip to content

Product Attributes ​

Attributes are a generic classification system for products and variants. They let you attach structured, typed metadata — Brand, Material, Weight, Waterproof Rating — to any product or variant.

Attributes vs Property Groups

Property Groups define the axes of variation for variants (Size × Color). Choosing between Size S and Size M selects a different variant.

Attributes are descriptive metadata that do not create variants. "Brand: Adidas" and "Weight: 320 g" describe a product but do not define a purchasable configuration.

Attribute Types ​

Every attribute has a type that controls which value field is used when assigning values to products or variants.

TypeValue fieldExample
textvalue_text"Adidas", "Full-grain leather"
numbervalue_numeric320, 4.5
selectoption_idOne predefined option (e.g. "Leather")
multi_selectoption_idsMultiple predefined options (e.g. ["Red", "Blue"])
booleanvalue_booleantrue, false

Units ​

number attributes accept an optional unit field (max 20 characters). Units are informational — the API does not perform conversions.

unit: "g"    → Weight in grams
unit: "mm"   → Dimensions in millimetres
unit: "kg"   → Mass in kilograms

Identifier ​

Every attribute has a unique identifier slug. It follows the same pattern used by Property Groups:

^[a-z0-9][a-z0-9_-]*$

Examples: brand, material, weight-g, is_waterproof. Identifiers cannot be changed once other systems rely on them for filtering.

Flags ​

FlagDefaultDescription
filterablefalseMarks the attribute as available for storefront filter UIs
requiredfalseIndicates the attribute should always be set on products

Stoa does not enforce required at the API level — it is a hint for the Admin UI and import tools.

i18n ​

Both attribute definitions and options support per-locale translations.

Attribute translations carry name and description:

json
{
  "translations": [
    { "locale": "en", "name": "Material", "description": "Primary material composition" },
    { "locale": "de", "name": "Material", "description": "Primäres Materialgefüge" }
  ]
}

Option translations carry name only:

json
{
  "translations": [
    { "locale": "en", "name": "Leather" },
    { "locale": "de", "name": "Leder" }
  ]
}

Store API ​

Attribute values are embedded directly in product and variant responses. You do not need a separate request.

json
{
  "data": {
    "id": "uuid",
    "sku": "BOOT-BLK-42",
    "attributes": [
      {
        "attribute_id": "uuid-brand",
        "attribute_identifier": "brand",
        "type": "text",
        "value_text": "Timberland",
        "translations": [{ "locale": "en", "name": "Brand" }]
      },
      {
        "attribute_id": "uuid-weight",
        "attribute_identifier": "weight-g",
        "type": "number",
        "unit": "g",
        "value_numeric": 680,
        "translations": [{ "locale": "en", "name": "Weight" }]
      }
    ]
  }
}

Variants include their own attributes array alongside the parent product's attributes.

Example: Creating and Assigning Attributes ​

1. Create a text attribute ​

bash
curl -X POST http://localhost:8080/api/v1/admin/attributes \
  -H 'Authorization: Bearer <token>' \
  -H 'Content-Type: application/json' \
  -d '{
    "identifier": "brand",
    "type": "text",
    "filterable": true,
    "translations": [
      { "locale": "en", "name": "Brand", "description": "Product brand or manufacturer" },
      { "locale": "de", "name": "Marke", "description": "Produktmarke oder Hersteller" }
    ]
  }'

2. Create a number attribute with a unit ​

bash
curl -X POST http://localhost:8080/api/v1/admin/attributes \
  -H 'Authorization: Bearer <token>' \
  -H 'Content-Type: application/json' \
  -d '{
    "identifier": "weight-g",
    "type": "number",
    "unit": "g",
    "filterable": false,
    "translations": [
      { "locale": "en", "name": "Weight" }
    ]
  }'

3. Create a select attribute with options ​

bash
# Create the attribute
curl -X POST http://localhost:8080/api/v1/admin/attributes \
  -H 'Authorization: Bearer <token>' \
  -H 'Content-Type: application/json' \
  -d '{
    "identifier": "material",
    "type": "select",
    "filterable": true,
    "translations": [
      { "locale": "en", "name": "Material" }
    ]
  }'

# Add an option (use the attribute UUID from the response above)
curl -X POST http://localhost:8080/api/v1/admin/attributes/<attribute-id>/options \
  -H 'Authorization: Bearer <token>' \
  -H 'Content-Type: application/json' \
  -d '{
    "position": 0,
    "translations": [
      { "locale": "en", "name": "Leather" },
      { "locale": "de", "name": "Leder" }
    ]
  }'

4. Assign values to a product ​

bash
curl -X PUT http://localhost:8080/api/v1/admin/products/<product-id>/attributes \
  -H 'Authorization: Bearer <token>' \
  -H 'Content-Type: application/json' \
  -d '{
    "attributes": [
      { "attribute_id": "<brand-uuid>", "value_text": "Timberland" },
      { "attribute_id": "<weight-uuid>", "value_numeric": 680 },
      { "attribute_id": "<material-uuid>", "option_id": "<leather-option-uuid>" }
    ]
  }'

This replaces (upserts) the existing values for each listed attribute. To remove a single attribute value, use DELETE /api/v1/admin/products/{id}/attributes/{attributeId}.

5. Assign values to a variant ​

bash
curl -X PUT http://localhost:8080/api/v1/admin/products/<product-id>/variants/<variant-id>/attributes \
  -H 'Authorization: Bearer <token>' \
  -H 'Content-Type: application/json' \
  -d '{
    "attributes": [
      { "attribute_id": "<weight-uuid>", "value_numeric": 720 }
    ]
  }'

Variant-level values allow you to override or supplement product-level attribute values per configuration.

See also ​

Released under the APACHE 2.0 License.