Working with product models using the API

A guide in how to digest and use a product model

Getting started

Below is an example in JSON format, showcasing the size of the product structure model when attempting to create one through the /v1/productstructures endpoint.

POST /v1/productstructures

There is a lot to digest and in the following segment we will break the model into smaller pieces making it easier to digest. To start off, we will demonstrate a general example where we focus on configuring the layout of products under the product structure. For a more detailed explanation of each property and their child attributes you can read the API documentation for the ProductStructure-endpoints.

Product structure model

C#
public class ProductStructure
{
    public Guid Uid { get; set; }

    public string? Alias { get; set; }

    public string? Label { get; set; }

    public bool HasVariants { get; set; }

    public bool HasVariantGroups { get; set; }

    public ProductConfiguration? ProductConfiguration { get; set; }

    public VariantGroupConfiguration? VariantGroupConfiguration { get; set; }

    public VariantConfiguration? VariantConfiguration { get; set; }

    public List<VariationDefinition>? VariationDefinitions { get; set; }

    public List<Guid>? AlwaysOnDefiningAttributes { get; set; }

    public List<IdentifierDefinition>? ProductIdentifierDefinitions { get; set; }

    public List<IdentifierDefinition>? VariantGroupIdentifierDefinitions { get; set; }

    public List<IdentifierDefinition>? VariantIdentifierDefinitions { get; set; }
}

Above you can see the class ProductStructure and all the content within. Not all is required for setting up a product structure. We begin by creating an instance of the class ProductStructure and defining the label and alias, along with a unique Uid so that it is easily identifiable. As this structure does not contain variants, HasVariants and HasVariantGroups are set to false.

C#
ProductStructure productStructure = new ProductStructure()
{
    Uid = Guid.NewGuid(),
    Alias = "ClothingDemo",
    Label = "ClothingDemo",
    HasVariants = false,
    HasVariantGroups = false,

    ProductConfiguration = new ProductConfiguration
    {
        ...
    },
    ...
}

Product configuration

Setting up ProductConfiguration will define the layout of the products following this structure. The properties you will be working with here are:

  • ThumbnailReference: Refers to a media attribute which holds image the image which should be presented as thumbnail for the product.

  • NameConfiguration: Contains NamePattern and NameAttributes which will define how names are generated for products following this structure.

  • CreateWizard: Step wizard presentation for data fulfillment when creating a product following this structure.

  • CopyWizard: Step wizard presentation for data fulfillment when copying a product following this structure.

  • Tabs: Contains the tabs which shall be shown on products following this product configuration in the backoffice. Also contains sections which contains the properties that should be displayed on each product.

To keep the example simple the wizard properties will not be used along with the thumbnail reference.

Name configuration

C#
ProductConfiguration = new ProductConfiguration
{
    NameConfiguration = new NameConfiguration
    {
        NamePattern = "{0} - {1}",
        NameAttributes = new List<Guid> 
        { 
            new Guid("74a43fc6-63b1-4743-aa1e-816a8133b4c1"),
            new Guid("de5572ed-1fb5-4192-a9ca-db73140f4cf5") 
        }
    },
    ...
}

The property NameConfiguration consists of a along with a list of named attributes. Together they define how each product should be named and it is important for the NamePattern to match the number of attributes in NameAttributes as seen in the example above.

The Guid assigned is a previously created attribute which we have fetched using the following Attributes endpoint /v1/attributes.

Tabs

C#
Tabs = new List<TabSetup>
{
    new DynamicTabSetup
    {
        Uid = Guid.NewGuid(),
        Label = "General",
        Sections = new List<SectionSetup>
        {
            ...
        }
    },
    ...
}

After setting up the NameConfiguration, the next step is to define the tabs and their layout.

Since TabSetup is an abstract class, you must use a class that inherits from it. In this case, we use DynamicTabSetup, which defines the layout of a single tab.

Each tab must be uniquely identifiable, which is done by assigning a new Guid to the Uid property. The visible name of the tab is defined through the Label property. Inside each tab, we configure one or more sections using the Sections property.

Here is an example setup:

C#
Tabs = new List<TabSetup>
{
    new DynamicTabSetup
    {
        Uid = Guid.NewGuid(),
        Label = "General",
        Sections = new List<SectionSetup>
        {
            new DynamicSectionSetup
            {
                Uid = Guid.NewGuid(),
                Headline = "General information",
                Properties = new List<PropertySetup>
                {
                    new AttributeSetup { Uid = Guid.NewGuid(), AttributeUid = new Guid("de5572ed-1fb5-4192-a9ca-db73140f4cf5") },
                    new AttributeSetup { Uid = Guid.NewGuid(), AttributeUid = new Guid("c28af57f-7f8e-413c-909b-fea67ef42b25") },
                    new AttributeSetup { Uid = Guid.NewGuid(), AttributeUid = new Guid("74a43fc6-63b1-4743-aa1e-816a8133b4c1") },
                    new AttributeSetup { Uid = Guid.NewGuid(), AttributeUid = new Guid("f886acd6-1397-4004-aba0-a3b2d6180151") },
                    new AttributeSetup { Uid = Guid.NewGuid(), AttributeUid = new Guid("2c19e970-77e2-45db-9508-925082684081") },
                    new AttributeSetup { Uid = Guid.NewGuid(), AttributeUid = new Guid("e2342c65-3def-4f57-9fe7-3fe9d3f093ed") },
                    new AttributeSetup { Uid = Guid.NewGuid(), AttributeUid = new Guid("04b4f1b6-7240-44ac-a631-bca618c0f8b8") },
                    new AttributeSetup { Uid = Guid.NewGuid(), AttributeUid = new Guid("45e8b8b2-bf4a-4c85-92c9-db70cedcac9a"), ReadOnly = true },
                    new AttributeSetup { Uid = Guid.NewGuid(), AttributeUid = new Guid("ccfcffdf-236a-4740-9009-c7cb25365a27") },
                    new AttributeSetup { Uid = Guid.NewGuid(), AttributeUid = new Guid("a7522788-ad62-4759-83a6-d8ef2bdb75db") }
                }
            },
            ...
        }
    },
    ...
}

Sections

Setting up a section is similar to setting up a tab. The main differences are:

  • A section uses the Headline property instead of Label.

  • Sections define a list of properties, each of which maps to a specific attribute.

Each property within a section is configured using a class such as AttributeSetup, and references an attribute via its AttributeUid. These attribute UIDs can be retrieved using the Attributes endpoint from the API.

When defining a property, you can also configure the behavior through the following properties:

  • ReadOnly

  • Mandatory

  • Unchangeable

  • Inherits

These are all boolean values that control how the property behaves in the UI. In the example above, we have set ReadOnly = true on one of the properties.

Each property also gets a unique Uid.

Once you have defined the layout for one tab, adding additional tabs follow the same structure. Here is an example of what it would look like with multiple tabs:

C#
ProductStructure productStructure = new ProductStructure()
{
    Uid = Guid.NewGuid(),
    Alias = "ClothingDemo",
    Label = "ClothingDemo",
    HasVariants = false,
    HasVariantGroups = false,

    ProductConfiguration = new ProductConfiguration
    {
        NameConfiguration = new NameConfiguration
        {
            NamePattern = "{0} - {1}",
            NameAttributes = new List<Guid> 
            { 
                new Guid("74a43fc6-63b1-4743-aa1e-816a8133b4c1"),
                new Guid("de5572ed-1fb5-4192-a9ca-db73140f4cf5") 
            }
        },
        Tabs = new List<TabSetup>
        {
            new DynamicTabSetup
            {
                Uid = Guid.NewGuid(),
                Label = "General",
                Sections = new List<SectionSetup>
                {

                    new DynamicSectionSetup
                    {
                        Uid = Guid.NewGuid(),
                        Headline = "General information",
                        Properties = new List<PropertySetup>
                        {
                            new AttributeSetup { Uid = Guid.NewGuid(), AttributeUid = new Guid("de5572ed-1fb5-4192-a9ca-db73140f4cf5") },
                            new AttributeSetup { Uid = Guid.NewGuid(), AttributeUid = new Guid("c28af57f-7f8e-413c-909b-fea67ef42b25") },
                            new AttributeSetup { Uid = Guid.NewGuid(), AttributeUid = new Guid("74a43fc6-63b1-4743-aa1e-816a8133b4c1") },
                            new AttributeSetup { Uid = Guid.NewGuid(), AttributeUid = new Guid("f886acd6-1397-4004-aba0-a3b2d6180151") },
                            new AttributeSetup { Uid = Guid.NewGuid(), AttributeUid = new Guid("2c19e970-77e2-45db-9508-925082684081") },
                            new AttributeSetup { Uid = Guid.NewGuid(), AttributeUid = new Guid("e2342c65-3def-4f57-9fe7-3fe9d3f093ed") },
                            new AttributeSetup { Uid = Guid.NewGuid(), AttributeUid = new Guid("04b4f1b6-7240-44ac-a631-bca618c0f8b8") },
                            new AttributeSetup { Uid = Guid.NewGuid(), AttributeUid = new Guid("45e8b8b2-bf4a-4c85-92c9-db70cedcac9a"), ReadOnly = true },
                            new AttributeSetup { Uid = Guid.NewGuid(), AttributeUid = new Guid("ccfcffdf-236a-4740-9009-c7cb25365a27") },
                            new AttributeSetup { Uid = Guid.NewGuid(), AttributeUid = new Guid("a7522788-ad62-4759-83a6-d8ef2bdb75db") }
                        }
                    },
                    ...
                }
            },
            new DynamicTabSetup
            {
                Uid = Guid.NewGuid(),
                Label = "Media",
                Sections = new List<SectionSetup>
                {
                    new DynamicSectionSetup
                    {
                        Uid = Guid.NewGuid(),
                        Headline = "Images",
                        Properties = new List<PropertySetup>
                        {
                            new AttributeSetup { Uid = Guid.NewGuid(), AttributeUid = new Guid("2380b93c-9a98-4321-a18e-dabb628e5958") },
                            new AttributeSetup { Uid = Guid.NewGuid(), AttributeUid = new Guid("80156c4b-bd7f-447d-b8ea-93d84da86a87") }
                        }
                    },
                    ...
                }
            },
            ...
        }
    },
    ...
}

From here we can proceed to define the product identifier definitions. The definition will configure how the identifiers of products following the structure should be generated.

Product identifier definitions

Now we just need to add the ProductIdentifierDefinitions, where we configure the identifier(s) for the products using this product structure.

C#
ProductIdentifierDefinitions = new List<IdentifierDefinition>
{
    new IdentifierDefinition
    {
        Uid = Guid.NewGuid(),
        Alias = "ModelNumber",
        IdentifierPattern = "{0}",
        IdentifierAttributes = new List<Guid>
        {
            new Guid("74a43fc6-63b1-4743-aa1e-816a8133b4c1")
        }
    }
},

In the example above, we have added a ProductIdentifierDefinitions collection containing a single IdentifierDefinition.

Each identifier definition includes:

  • A unique Uid

  • An Alias to distinguish it from other definitions

  • An IdentifierPattern used to construct the identifier string

  • A list of IdentifierAttributes whose values are used in the pattern

In this case, the pattern is simply {0}, and the single attribute used is the one with the GUID 74a43fc6-63b1-4743-aa1e-816a8133b4c1, which represents the ModelNumber attribute in the PIM. This means the generated product identifier will directly reflect the value of the ModelNumber attribute.

With this you now have a model setup which you can use to create a product structure inside the PIM.

Tip identifiers must be unique across value and alias. So be mindful when choosing the alias, identifier pattern and identifier attributes to avoid collisions.

Example of wizard use and permission

In this section, we will demonstrate how to configure both the CreateWizard and CopyWizard for guiding the user when creating or copying a product in backoffice. We will also cover the use and set up of view and save permissions on individual tabs.

Wizard create and copy

The step wizard setup enables a guided, step-by-step flow for entering product data. This improves data consistency and user experience in the PIM backoffice.

Both CreateWizard and CopyWizard share the same structure, so we will focus on the CreateWizard example below:

C#
ProductConfiguration = new ProductConfiguration
{
    NameConfiguration = new NameConfiguration
    {
        NamePattern = "{0}",
        NameAttributes = new List<Guid>
        { 
            new Guid("de5572ed-1fb5-4192-a9ca-db73140f4cf5")
        }
    },
    CreateWizard = new WizardSetup
    {
        Uid = Guid.NewGuid(),
        Steps = new List<WizardStepSetup>
        {
            new DynamicWizardStepSetup
            {
                Uid = Guid.NewGuid(),
                Headline = "Step 1",
                Description = "Basic product details",
                Properties = new List<WizardPropertySetup>
                {
                    new AttributeWizardPropertySetup
                    {
                        Uid = new Guid("30995d23-9492-4467-ab13-faa565afd579"),
                        PropertyUid = newGuid, Mandatory = true
                    },
                    ...
                }
            },
            ...
        }
    },
    CopyWizard = new WizardSetup
    {
        Uid = Guid.NewGuid(),
        Steps = new List<WizardStepSetup>
        {
            new DynamicWizardStepSetup
            {
                Uid = Guid.NewGuid(),
                Headline = "Step A",
                Description = "Select base product",
                Properties = new List<WizardPropertySetup>
                {
                    new AttributeWizardPropertySetup
                    { 
                        Uid = Guid.NewGuid(),
                        PropertyUid = new Guid("30995d23-9492-4467-ab13-faa565afd579"),
                        Mandatory = true
                    },
                    ...
                }
            },
            ...
        }
    },
    ...
}

The wizard setup contains:

  • A Uid for identification

  • A list of Steps (WizardStepSetup instances)

Each step includes:

  • A Uid, Headline, and Description to define the step

  • A list of Properties of type WizardPropertySetup

    • They are inherited by AttributeWizardPropertySetup, which includes:

      • Uid — unique identifier for the wizard property

      • PropertyUid — references a property UID from the SectionSetup (can be found via API)

      • Mandatory — marks the field as required

View and save permission

You can define view and save permissions on multiple levels within the product structure, including:

  • Tabs

  • Sections

  • Individual Properties

These permissions ensure that only users with the correct access rights (fetched using the /v1/permissions endpoint) can view or modify specific UI components.

Here is an example of permission setup on a tab:

C#

Tabs = new List<TabSetup>
{
    new DynamicTabSetup
    {
        Uid = Guid.NewGuid(),
        Label = "General",
        ViewPermission = new Guid("313e11e2-e2e4-4a82-82a0-f68ec8682522"),
        SavePermission = new Guid("e3d4c882-517c-4d89-9337-8a0b74d09b54"),
        Sections = new List<SectionSetup>
        {

            new DynamicSectionSetup
            {

Now, to view or save the tab, the user must have the required permission that has been set.

Example of product structure with variants

In this example we will focus on adding variants as well as variant groups to our product structure.

To create a product structure that support variants, we must set HasVariants = true and provide a VariantConfiguration object. We do the same for VariantGroupConfiguration. Additionally, we define VariationDefinitions, which specify how variants are structured based on certain attributes like size, color, etc.

C#
VariantConfiguration = new VariantConfiguration
{
    Tabs = new List<TabSetup>
    {
        new DynamicTabSetup
        {
            Uid = Guid.NewGuid(),
            Label = "VariantTab",
            Sections = new List<SectionSetup>
            {

                new DynamicSectionSetup
                {
                    Uid = Guid.NewGuid(),
                    Headline = "Variant Information",
                    Properties = new List<PropertySetup>
                    {
                        new AttributeSetup { Uid = Guid.NewGuid(), AttributeUid = new Guid("de5572ed-1fb5-4192-a9ca-db73140f4cf5") },
                        new AttributeSetup { Uid = Guid.NewGuid(), AttributeUid = new Guid("17c5eca3-a439-42b2-95df-8d4e82d1f16b") },
                        new AttributeSetup { Uid = Guid.NewGuid(), AttributeUid = new Guid("74a43fc6-63b1-4743-aa1e-816a8133b4c1") }
                    }
                },
                ...
            }
        },
        ...
    }
}

Above you can see we have set up VariantConfiguration just like ProductConfiguration. The main difference is that it does not contain NameConfiguration.

Variant definitions

To support dynamic variant structures, we define one or more VariationDefinitions. Each defines how variants are differentiated based on selected attributes.

C#
VariationDefinitions = new List<VariationDefinition>
{
    new VariationDefinition
    {
        Uid = Guid.NewGuid(),
        Name = "DemoNew",
        Alias = "DemoNew",
        NameConfiguration = new NameConfiguration
        {
            NamePattern = "{0}",
            NameAttributes = new List<Guid>
            {
                new Guid("17c5eca3-a439-42b2-95df-8d4e82d1f16b")
            }
        },
        DefiningAttributes = new List<Guid>
        {
            new Guid("17c5eca3-a439-42b2-95df-8d4e82d1f16b")
        },
        SortOrder = 0

    },
    ...
}

This example defines a single variation definition based on the Color attribute. The NameConfiguration ensures each variant’s name will be based on that attribute’s value.

Always on defining attributes

Defining attributes are typically only available to variants if they are included in a specific VariationDefinition. However, there are cases where you may want certain attributes, like size, color, or SKU, to always be available for all variants, regardless of how the variations are defined.

The AlwaysOnDefiningAttributes property allows you to specify a set of defining attributes that are always included for every variant following the product structure.

In the example we have SKU as our AlwaysOnDefiningAttributes. If we want other options like name or StyleNumber, we would have to expand the VariantDefinition to contain those attributes before being able to assign them as an always show defining attribute.

C#
AlwaysOnDefiningAttributes = new List<Guid>
{
    new Guid("17c5eca3-a439-42b2-95df-8d4e82d1f16b")
}

Variant identifier definitions

When setting up the VariantConfiguration in the product structure, you also have the option to specify identifier definitions, just like you do for the product itself.

C#
VariantIdentifierDefinitions = new List<IdentifierDefinition>
{
    new IdentifierDefinition
    {
        Uid = Guid.NewGuid(),
        Alias = "SKU",
        IdentifierPattern = "{0}",
        IdentifierAttributes = new List<Guid>
        {
            new Guid("17c5eca3-a439-42b2-95df-8d4e82d1f16b")
        }
    },
    ...
}

Below is a similar setup for variant groups, configured using the VariantGroupConfiguration. Variant groups help organize and manage variants that share common characteristics like color.

When looking at the VariantGroupConfiguration model below, you can see that it includes a property called GroupingAttributes. This property is a list of attribute GUIDs from the PIM, used to determine how variants are grouped. In this case, the attribute is color, meaning variants will be grouped by color.

C#
VariantGroupConfiguration = new VariantGroupConfiguration
{
    GroupingAttributes = new List<Guid>
    {
        new Guid("c3fbf795-79be-4ab0-ae7d-e264b8227b88")
    },
    NameConfiguration = new NameConfiguration
    {
        NamePattern = "{0}",
        NameAttributes = new List<Guid>
        {
            new Guid("c3fbf795-79be-4ab0-ae7d-e264b8227b88")
        }
    },
    Tabs = new List<TabSetup?>
    {
        new DynamicTabSetup
        {
            Uid = Guid.NewGuid(),
            Label = "VariantGroupTab",
            Sections = new List<SectionSetup>
            {

                new DynamicSectionSetup
                {
                    Uid = Guid.NewGuid(),
                    Headline = "Variant Group Information",
                    Properties = new List<PropertySetup>
                    {
                        new AttributeSetup { AttributeUid = new Guid("c3fbf795-79be-4ab0-ae7d-e264b8227b88") },
                        new AttributeSetup { AttributeUid = new Guid("f0a0cc7b-1dbf-4b55-bb20-a2004400811c") }
                    }
                },
                ...
            }
        },
        ...
    }
}

Variant group identifier definitions

Just like products and variants, VariantGroupConfiguration can also include identifier definitions. These definitions allow you to generate unique identifiers (e.g., group StyleNumbers or codes) for variant groups based on selected attributes.

C#
VariantGroupIdentifierDefinitions = new List<IdentifierDefinition>
{
    new IdentifierDefinition
    {
        Uid = Guid.NewGuid(),
        Alias = "StyleNumber",
        IdentifierPattern = "{0}",
        IdentifierAttributes = new List<Guid>
        {
            new Guid("f0a0cc7b-1dbf-4b55-bb20-a2004400811c")
        }
    }
}

Last updated