Variants
Variants represent different versions of a product, for example a T-Shirt in multiple colors and sizes. Each variant is a separate product linked to a parent, with its own price, stock and identifiers.
How variants work
Variants build on property groups:
- Create a property group with properties (Color, Size) and values (Red, Blue, S, M, L)
- Create a parent product with
propertyGroupIdset and avariantsarray - Each variant specifies its
propertyPairs, one value per property - The API auto-generates each variant's
referencefrom the parent reference + value codes
Parent: "TSHIRT" (propertyGroup: "T-Shirt Options")
├── Variant: "TSHIRT-RED-S" (Color=Red, Size=Small) → price: 15.99
├── Variant: "TSHIRT-RED-M" (Color=Red, Size=Medium) → price: 15.99
├── Variant: "TSHIRT-BLUE-S" (Color=Blue, Size=Small) → price: 16.99
└── Variant: "TSHIRT-BLUE-M" (Color=Blue, Size=Medium) → price: 16.99
There is no separate "variant" mutation; variants are created and updated through productCreate and productUpdate.
Creating a product with variants
You need the UUIDs from your property group. If you haven't created one yet, see the Property Groups guide.
mutation {
productCreate(
companyId: 1
data: {
name: "Classic T-Shirt"
reference: "TSHIRT"
type: 1
productCategoryId: 5
measurementUnitId: 1
price: 15.99
hasStock: true
propertyGroupId: "group-uuid"
taxes: [
{ taxId: 1, value: 23, ordering: 1, cumulative: false }
]
variants: [
{
name: "Red Small"
price: 15.99
propertyPairs: [
{ propertyId: "color-uuid", propertyValueId: "red-uuid" }
{ propertyId: "size-uuid", propertyValueId: "small-uuid" }
]
warehouses: [
{ warehouseId: 1, stock: 25 }
]
},
{
name: "Red Medium"
price: 15.99
propertyPairs: [
{ propertyId: "color-uuid", propertyValueId: "red-uuid" }
{ propertyId: "size-uuid", propertyValueId: "medium-uuid" }
]
warehouses: [
{ warehouseId: 1, stock: 40 }
]
},
{
name: "Blue Small"
price: 16.99
propertyPairs: [
{ propertyId: "color-uuid", propertyValueId: "blue-uuid" }
{ propertyId: "size-uuid", propertyValueId: "small-uuid" }
]
warehouses: [
{ warehouseId: 1, stock: 15 }
]
}
]
}
) {
errors { field msg }
data {
productId
name
reference
variantsCount
variants {
productId
parentId
name
reference
price
propertyPairs {
property { name }
propertyValue { value code }
ordering
}
warehouses {
warehouseId
stock
}
}
}
}
}
Response:
{
"data": {
"productCreate": {
"errors": [],
"data": {
"productId": 100,
"name": "Classic T-Shirt",
"reference": "TSHIRT",
"variantsCount": 3,
"variants": [
{
"productId": 101,
"parentId": 100,
"name": "Red Small",
"reference": "TSHIRT-RED-S",
"price": 15.99,
"propertyPairs": [
{ "property": { "name": "Color" }, "propertyValue": { "value": "Red", "code": "RED" }, "ordering": 1 },
{ "property": { "name": "Size" }, "propertyValue": { "value": "Small", "code": "S" }, "ordering": 2 }
],
"warehouses": [{ "warehouseId": 1, "stock": 25 }]
},
{
"productId": 102,
"parentId": 100,
"name": "Red Medium",
"reference": "TSHIRT-RED-M",
"price": 15.99,
"propertyPairs": [
{ "property": { "name": "Color" }, "propertyValue": { "value": "Red", "code": "RED" }, "ordering": 1 },
{ "property": { "name": "Size" }, "propertyValue": { "value": "Medium", "code": "M" }, "ordering": 2 }
],
"warehouses": [{ "warehouseId": 1, "stock": 40 }]
},
{
"productId": 103,
"parentId": 100,
"name": "Blue Small",
"reference": "TSHIRT-BLUE-S",
"price": 16.99,
"propertyPairs": [
{ "property": { "name": "Color" }, "propertyValue": { "value": "Blue", "code": "BLUE" }, "ordering": 1 },
{ "property": { "name": "Size" }, "propertyValue": { "value": "Small", "code": "S" }, "ordering": 2 }
],
"warehouses": [{ "warehouseId": 1, "stock": 15 }]
}
]
}
}
}
}
Notice that variants get their own productId and an auto-generated reference built from the parent reference + property value codes, ordered by the property's ordering.
What variants inherit from the parent
When created, variants automatically inherit these fields from the parent product:
type: Product or ServicecompanyIdmeasurementUnitIdproductCategoryIdhasStock/minStockexemptionReasontaxessupplierscustomFields
What variants can override
Each variant can have its own:
| Field | Description |
|---|---|
name | Variant display name |
price | Price (can differ from parent) |
summary | Description |
notes | Internal notes |
visible | Visibility flag |
img | Variant-specific image |
warehouses | Stock per warehouse |
priceClasses | Price class overrides |
identifications | Barcodes specific to this variant |
Variant input fields
The ProductVariantInsert input:
| Field | Type | Description |
|---|---|---|
name | String! | Variant name (required) |
price | Float | Variant price |
summary | String | Description |
notes | String | Internal notes |
visible | Int | Visibility flag |
img | Upload | Image |
minStock | Float | Minimum stock threshold |
warehouseId | Int | Default warehouse |
propertyPairs | [ProductVariantPropertyPairsAssociation!] | Property value assignments |
warehouses | [ProductWarehouseInsertAssociation!] | Stock per warehouse |
priceClasses | [ProductPriceClassAssociation!] | Price class values |
identifications | [ProductIdentification!] | Barcodes |
Stock requirements
If the parent has hasStock: true, every variant must include at least one warehouses entry. If the parent has hasStock: false, variants cannot include warehouses.
Querying a product with its variants
query {
product(companyId: 1, productId: 100) {
data {
productId
name
reference
price
variantsCount
propertyGroup {
propertyGroupId
name
properties {
propertyId
name
ordering
values {
propertyValueId
code
value
}
}
}
variants {
productId
name
reference
price
visible
stock
propertyPairs {
property { name ordering }
propertyValue { value code }
ordering
}
warehouses {
warehouseId
stock
minStock
}
}
}
}
}
The variants field accepts an optional visible parameter to filter by visibility:
variants(visible: 1) {
productId
name
price
}
Filtering variants in product lists
When listing products with the products query, variants appear as separate products with a non-null parentId. You can use this to distinguish parent products from variants:
query {
products(companyId: 1, options: {
pagination: { page: 1, qty: 20 }
}) {
data {
productId
parentId
name
reference
price
variantsCount
}
}
}
parentId: null→ this is a parent product (or a standalone product)parentId: 100→ this is a variant of product 100
Updating variants
Use productUpdate on the parent product to manage its variants. The variants array follows the same create/update/delete convention:
- With
productId→ update that existing variant - Without
productId→ create a new variant - Existing variants not in the array → deleted
Adding a new variant
Include all existing variants plus the new one:
mutation {
productUpdate(
companyId: 1
data: {
productId: 100
variants: [
{
productId: 101
name: "Red Small"
price: 15.99
propertyPairs: [
{ propertyId: "color-uuid", propertyValueId: "red-uuid" }
{ propertyId: "size-uuid", propertyValueId: "small-uuid" }
]
warehouses: [{ warehouseId: 1, minStock: 5 }]
},
{
productId: 102
name: "Red Medium"
price: 15.99
propertyPairs: [
{ propertyId: "color-uuid", propertyValueId: "red-uuid" }
{ propertyId: "size-uuid", propertyValueId: "medium-uuid" }
]
warehouses: [{ warehouseId: 1, minStock: 5 }]
},
{
productId: 103
name: "Blue Small"
price: 16.99
propertyPairs: [
{ propertyId: "color-uuid", propertyValueId: "blue-uuid" }
{ propertyId: "size-uuid", propertyValueId: "small-uuid" }
]
warehouses: [{ warehouseId: 1, minStock: 5 }]
},
{
name: "Blue Medium"
price: 16.99
propertyPairs: [
{ propertyId: "color-uuid", propertyValueId: "blue-uuid" }
{ propertyId: "size-uuid", propertyValueId: "medium-uuid" }
]
warehouses: [{ warehouseId: 1, minStock: 3 }]
}
]
}
) {
errors { field msg }
data {
productId
variantsCount
variants {
productId
name
reference
price
}
}
}
}
The new "Blue Medium" variant (no productId) will be created with the auto-generated reference "TSHIRT-BLUE-M".
Updating a variant's price
To change just one variant's price, you still need to include all variants in the array:
variants: [
{ productId: 101, name: "Red Small", price: 17.99, propertyPairs: [...], warehouses: [...] },
{ productId: 102, name: "Red Medium", price: 17.99, propertyPairs: [...], warehouses: [...] },
{ productId: 103, name: "Blue Small", price: 18.99, propertyPairs: [...], warehouses: [...] },
{ productId: 104, name: "Blue Medium", price: 18.99, propertyPairs: [...], warehouses: [...] }
]
Removing a variant
Simply omit it from the variants array. The API deletes variants that are present in the database but absent from the update.
Reference generation
Variant references are auto-generated and cannot be set manually. The format is:
{parentReference}-{code1}-{code2}-...
The codes are taken from the property values in the order defined by the property's ordering field. For example:
| Parent Reference | Color Code | Size Code | Variant Reference |
|---|---|---|---|
TSHIRT | RED | S | TSHIRT-RED-S |
TSHIRT | BLUE | XL | TSHIRT-BLUE-XL |
MUG-CERAMIC | WHITE | - | MUG-CERAMIC-WHITE |
The full variant reference must fit within 50 characters (the database limit for all product references). Because of this, parent products that have variants are limited to a 30-character reference, leaving room for the separator and codes.
Keep property value codes short to avoid hitting this limit. If a generated reference exceeds 50 characters or conflicts with an existing product, the API returns a validation error.
Next steps
- See the full
ProductVariantInsertandProductVariantUpdatereference pages - Property Groups: Manage the properties and values used by variants
- Product Overview: Product fields, taxes, stock and more