Property Groups
Property groups define the attributes used to create product variants. For example, a "T-Shirt" property group might contain a Color property (Red, Blue, Green) and a Size property (S, M, L, XL).
Structure
Property groups have a three-level hierarchy:
- PropertyGroup: A named collection of properties, scoped to a company
- Property: An attribute dimension (Color, Size, Material, etc.)
- PropertyValue: A specific option within a property, with a machine-readable
codeand a human-readablevalue
Creating a property group
Use propertyGroupCreate to create a group with its properties and values in a single call:
mutation {
propertyGroupCreate(
companyId: 1
data: {
name: "T-Shirt Options"
visible: 1
properties: [
{
name: "Color"
ordering: 1
visible: 1
values: [
{ code: "RED", value: "Red", ordering: 1, visible: 1 }
{ code: "BLUE", value: "Blue", ordering: 2, visible: 1 }
{ code: "GREEN", value: "Green", ordering: 3, visible: 1 }
]
},
{
name: "Size"
ordering: 2
visible: 1
values: [
{ code: "S", value: "Small", ordering: 1, visible: 1 }
{ code: "M", value: "Medium", ordering: 2, visible: 1 }
{ code: "L", value: "Large", ordering: 3, visible: 1 }
]
}
]
}
) {
errors { field msg }
data {
propertyGroupId
name
properties {
propertyId
name
ordering
values {
propertyValueId
code
value
ordering
}
}
}
}
}
The response includes the generated UUIDs (propertyGroupId, propertyId, propertyValueId) that you'll need when creating variants.
Field reference
PropertyGroupInsert:
| Field | Type | Description |
|---|---|---|
name | String! | Group name (unique per company) |
visible | Int! | 1 = visible, 0 = hidden |
properties | [PropertyInsert!]! | At least one property required |
PropertyInsert:
| Field | Type | Description |
|---|---|---|
name | String! | Property name (e.g. "Color") |
ordering | Int! | Display order (starting from 1) |
visible | Int! | 1 = visible, 0 = hidden |
values | [PropertyValueInsert!]! | The available options |
PropertyValueInsert:
| Field | Type | Description |
|---|---|---|
code | String! | Machine-readable code (e.g. "RED"). Unique within the property. Used to auto-generate variant references. |
value | String! | Human-readable label (e.g. "Red") |
ordering | Int! | Display order |
visible | Int! | 1 = visible, 0 = hidden |
Querying property groups
List all groups
query {
propertyGroups(companyId: 1, options: {
pagination: { page: 1, qty: 20 }
}) {
data {
propertyGroupId
name
properties {
propertyId
name
values {
propertyValueId
code
value
}
}
}
options {
pagination {
page
qty
count
}
}
}
}
Get a single group
query {
propertyGroup(companyId: 1, propertyGroupId: "uuid-here") {
data {
propertyGroupId
name
properties {
propertyId
name
ordering
values {
propertyValueId
code
value
ordering
deletable
}
deletable
}
deletable
}
}
}
The deletable field on each level tells you whether that item can be safely deleted without affecting existing products.
Updating a property group
There are two ways to change a group once it exists:
- Granular mutations act on a single property or a single value, leaving everything else alone. Prefer these for day to day edits.
propertyGroupUpdaterewrites the group's whole structure in one call. Reach for it when you really are replacing the structure, and mind its replacement semantics.
Adding a value to a property
propertyValueCreate appends one value to an existing property:
mutation {
propertyValueCreate(
companyId: 1
propertyId: "color-uuid"
data: {
code: "YELLOW"
value: "Yellow"
}
) {
errors { field msg }
data {
propertyValueId
code
value
ordering
visible
}
}
}
ordering and visible are optional. Omit ordering and the value goes to the end of the property's list.
Changing a value
propertyValueUpdate takes the value's own ID, and only the fields you send are updated:
mutation {
propertyValueUpdate(
companyId: 1
propertyValueId: "yellow-uuid"
data: {
value: "Golden Yellow"
visible: 0
}
) {
errors { field msg }
data {
propertyValueId
code
value
visible
}
}
}
Renaming the human-readable value is always allowed. Changing code is not, once a variant uses that value, because variant references are built from codes.
Removing a value
mutation {
propertyValueDelete(companyId: 1, propertyValueId: "yellow-uuid") {
status
deletedCount
errors { field msg }
}
}
Adding a property
propertyCreate adds a property, with its values, to an existing group. At least one value is required:
mutation {
propertyCreate(
companyId: 1
propertyGroupId: "group-uuid"
data: {
name: "Material"
values: [
{ code: "COTTON", value: "Cotton" }
{ code: "POLY", value: "Polyester" }
]
}
) {
errors { field msg }
data {
propertyId
name
ordering
values {
propertyValueId
code
value
}
}
}
}
Changing a property
propertyUpdate renames or reorders a property. It also accepts a values array, which replaces the property's values, so the same include-everything-you-keep rule applies there as in propertyGroupUpdate:
mutation {
propertyUpdate(
companyId: 1
propertyId: "material-uuid"
data: {
name: "Fabric"
ordering: 3
}
) {
errors { field msg }
data {
propertyId
name
ordering
}
}
}
Removing a property
mutation {
propertyDelete(companyId: 1, propertyId: "material-uuid") {
status
deletedCount
errors { field msg }
}
}
Rules for granular changes
| Condition | Message |
|---|---|
A value's code is not unique within its property | A value with this code already exists for this property. |
code is longer than 30 characters | The code exceeds the maximum length of 30 |
Changing the code of a value a variant uses | Can't change the code of a property value while it's in use by a variant. |
| Deleting a value a variant uses | This property value is in use by a variant and can't be deleted. |
| Deleting a property whose values a variant uses | This property is in use by a variant and can't be deleted. |
| Deleting the only value left on a property | Can't delete the last value of a property. Delete the property instead. |
| Deleting the only property left in a group | Can't delete the last property of a group. Delete the property group instead. |
propertyCreate sent an empty values array | Property [name] - Needs at least a value. |
A group therefore always keeps at least one property, and a property always keeps at least one value. To get rid of the last one, delete its parent instead.
Rewriting a whole group in one call
propertyGroupUpdate can rename the group and add, update or remove properties and values together. To add a value this way, include the existing properties and values with their IDs, plus the new value without an ID:
mutation {
propertyGroupUpdate(
companyId: 1
data: {
propertyGroupId: "group-uuid"
properties: [
{
propertyId: "color-uuid"
values: [
{ propertyValueId: "red-uuid", code: "RED", value: "Red", ordering: 1, visible: 1 }
{ propertyValueId: "blue-uuid", code: "BLUE", value: "Blue", ordering: 2, visible: 1 }
{ propertyValueId: "green-uuid", code: "GREEN", value: "Green", ordering: 3, visible: 1 }
{ code: "YELLOW", value: "Yellow", ordering: 4, visible: 1 }
]
}
]
}
) {
errors { field msg }
data {
propertyGroupId
properties {
propertyId
name
values {
propertyValueId
code
value
}
}
}
}
}
The logic is:
- Entries with an ID are updated
- Entries without an ID are created
- Existing entries not included are deleted
A new property works the same way, included without a propertyId:
properties: [
{ propertyId: "color-uuid", name: "Color", ordering: 1, visible: 1, values: [...] },
{ propertyId: "size-uuid", name: "Size", ordering: 2, visible: 1, values: [...] },
{ name: "Material", ordering: 3, visible: 1, values: [
{ code: "COTTON", value: "Cotton", ordering: 1, visible: 1 },
{ code: "POLY", value: "Polyester", ordering: 2, visible: 1 }
]}
]
Deletion constraints
Property groups, properties, and values are protected by referential integrity:
| Level | Can delete when... |
|---|---|
| PropertyGroup | No products reference this group (propertyGroupId) |
| Property | No product variants use values from this property |
| PropertyValue | No product variants use this specific value |
Check the deletable field on each item to know if deletion is safe. Attempting to delete a referenced item returns an error.
mutation {
propertyGroupDelete(companyId: 1, propertyGroupId: ["group-uuid"]) {
status
deletedCount
errors { field msg }
}
}
Two structural rules apply on top of the reference checks: a property cannot lose its last value and a group cannot lose its last property. Delete the parent instead. Both propertyDelete and propertyValueDelete report these as validation errors, listed under Rules for granular changes.
Next steps
- Variants: Create product SKUs using property groups
- Overview & Creating a Product: Back to product fundamentals
- Deletion: Understand deletion constraints before removing groups and values
- See
PropertyGroupInsertandPropertyGroupReadfor all fields - See
PropertyCreate,PropertyEdit,PropertyValueCreateandPropertyValueEditfor the granular inputs