Moloni ON Logo WhiteGuidesAPI ReferenceExplorer
Guides

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 code and a human-readable value

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:

FieldTypeDescription
nameString!Group name (unique per company)
visibleInt!1 = visible, 0 = hidden
properties[PropertyInsert!]!At least one property required

PropertyInsert:

FieldTypeDescription
nameString!Property name (e.g. "Color")
orderingInt!Display order (starting from 1)
visibleInt!1 = visible, 0 = hidden
values[PropertyValueInsert!]!The available options

PropertyValueInsert:

FieldTypeDescription
codeString!Machine-readable code (e.g. "RED"). Unique within the property. Used to auto-generate variant references.
valueString!Human-readable label (e.g. "Red")
orderingInt!Display order
visibleInt!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.
  • propertyGroupUpdate rewrites 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

ConditionMessage
A value's code is not unique within its propertyA value with this code already exists for this property.
code is longer than 30 charactersThe code exceeds the maximum length of 30
Changing the code of a value a variant usesCan't change the code of a property value while it's in use by a variant.
Deleting a value a variant usesThis property value is in use by a variant and can't be deleted.
Deleting a property whose values a variant usesThis property is in use by a variant and can't be deleted.
Deleting the only value left on a propertyCan't delete the last value of a property. Delete the property instead.
Deleting the only property left in a groupCan't delete the last property of a group. Delete the property group instead.
propertyCreate sent an empty values arrayProperty [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:

LevelCan delete when...
PropertyGroupNo products reference this group (propertyGroupId)
PropertyNo product variants use values from this property
PropertyValueNo 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

© 2026 Moloni ON

Tax Authority Certificate No. 3075