PIM Systems

Struct PIM API v4: Complete Guide to Complex Attributes (Now with Global Lists!)

A comprehensive guide to using Complex Attributes in Struct PIM API v4, including the undocumented ability to use them in Global Lists. Features practical code examples and real-world implementations.

Published January 27, 2025
18 min read
Updated January 30, 2025
Sivert Kjøller Bertelsen
Struct
API
Tutorial
Integration
Complex Attributes

Overview

Complex Attributes in Struct PIM allow you to create structured, nested data within your product information. Unlike simple attributes that store single values, Complex Attributes contain multiple Sub-Attributes that work together to form a cohesive data structure.

Key Benefits

  • Organized Data: Group related attributes logically (e.g., technical specifications, dimensions)
  • Reusable Structures: Define once, use across multiple product structures
  • Rich Data Modeling: Support complex product information requirements
  • UI Flexibility: Control how nested data is displayed and rendered

Understanding Complex Attributes

What Are Complex Attributes?

A Complex Attribute is a container that holds multiple Sub-Attributes. Think of it as a structured object in programming:

Complex Attribute Structure

Example showing the difference between simple and complex attributes

javascript
// Simple attributes store single values
ProductName: "iPhone 15 Pro"
Price: 999.99

// Complex attributes store structured objects
TechnicalSpecs: {
  ModelNumber: "A2848",
  PowerRating: 120,
  Voltage: "220V",
  IsEnergyEfficient: true
}

// Architecture
ComplexAttribute
├── SubAttribute 1 (TextAttribute)
├── SubAttribute 2 (NumberAttribute) 
├── SubAttribute 3 (BooleanAttribute)
└── SubAttribute N (Any supported type)

API Schema vs Reality

The Documentation Gap

The official Swagger documentation shows a simplified schema, but our empirical testing revealed the actual working structure:

Documentation vs Reality

What the Swagger docs suggest vs what actually works

json
// ❌ What the Swagger Docs Suggest:
{
  "alias": "TechSpecs",
  "name": "Technical Specifications",
  "type": "ComplexAttribute",
  "configuration": {
    "subAttributes": [...]
  }
}

// ✅ What Actually Works:
{
  "Uid": "uuid-here",
  "Alias": "TechSpecs", 
  "BackofficeName": "Technical Specifications",
  "AttributeType": "ComplexAttribute",
  "Localized": false,
  "ReadOnly": false,
  "Mandatory": false,
  "Columns": 12,
  "Unchangeable": false,
  "DisableRevisionLogging": false,
  "DisableIndexing": false,
  "SubAttributes": [
    // Full attribute definitions here, not references
  ],
  "RenderValuesForAttributeFieldUids": [],
  "RenderValuesForBackofficeAttributeFieldUids": [],
  "RenderedValueSeparator": null,
  "RenderedValueInBackofficeSeparator": null
}

Creating Complex Attributes

Basic Structure

Every Complex Attribute must include these essential properties:

TypeScript Interface

Complete interface for Complex Attributes

typescript
interface ComplexAttributeBase {
  Uid: string;                    // Required: Generate UUID
  Alias: string;                  // Required: Unique identifier
  BackofficeName: string;         // Required: Display name
  AttributeType: 'ComplexAttribute';
  Localized: boolean;             // Usually false
  ReadOnly: boolean;              // Usually false
  Mandatory: boolean;             // Usually false
  Columns: number;                // 1-12, commonly 12 for full width
  Unchangeable: boolean;          // Usually false
  DisableRevisionLogging: boolean; // Usually false
  DisableIndexing: boolean;       // Usually false
  SubAttributes: SubAttribute[];  // The nested attributes
}

Step-by-Step Creation

Follow these steps to create a Complex Attribute:

Creation Process

  1. Generate Unique IDs for the complex attribute and each sub-attribute
  2. Define Sub-Attributes with complete attribute definitions
  3. Compose the Complex Attribute with all required properties
  4. Create via API endpoint with proper error handling

Practical Examples

Example 1: Technical Specifications

Perfect for products with multiple technical details:

Technical Specifications Example

Complete example of a Complex Attribute for technical specifications

typescript
const technicalSpecsAttribute = {
  Uid: generateGuid(),
  Alias: 'TechnicalSpecs',
  BackofficeName: 'Technical Specifications', 
  AttributeType: 'ComplexAttribute',
  Localized: false,
  ReadOnly: false,
  Mandatory: false,
  Columns: 12,
  Unchangeable: false,
  DisableRevisionLogging: false,
  DisableIndexing: false,
  SubAttributes: [
    {
      Uid: generateGuid(),
      Alias: 'ModelNumber',
      BackofficeName: 'Model Number',
      AttributeType: 'TextAttribute',
      Localized: false,
      ReadOnly: false,
      Mandatory: false,
      Columns: 6,
      Unchangeable: false,
      DisableRevisionLogging: false,
      DisableIndexing: false,
      MaxLength: 100,
      UseMultiRowInput: false,
      UseRichText: false,
      ShowCharacterCount: false
    },
    {
      Uid: generateGuid(),
      Alias: 'PowerRating', 
      BackofficeName: 'Power Rating (W)',
      AttributeType: 'NumberAttribute',
      Localized: false,
      ReadOnly: false,
      Mandatory: false,
      Columns: 6,
      Unchangeable: false,
      DisableRevisionLogging: false,
      DisableIndexing: false,
      NumberOfDecimals: 0,
      Min: 0,
      Max: 2000,
      Unit: 'W'
    },
    {
      Uid: generateGuid(),
      Alias: 'IsEnergyEfficient',
      BackofficeName: 'Energy Efficient',
      AttributeType: 'BooleanAttribute',
      Localized: false,
      ReadOnly: false,
      Mandatory: false,
      Columns: 6,
      Unchangeable: false,
      DisableRevisionLogging: false,
      DisableIndexing: false
    }
  ],
  RenderValuesForAttributeFieldUids: [],
  RenderValuesForBackofficeAttributeFieldUids: [],
  RenderedValueSeparator: null,
  RenderedValueInBackofficeSeparator: null
};

⚡ Complex Attributes in Global Lists (Important Discovery)

Breaking: Complex Attributes DO Work in Global Lists!

Contrary to what might be assumed from the documentation, Struct PIM DOES support Complex Attributes for Global Lists. This is a powerful, undocumented feature that enables rich, structured data models for global list values.

Instead of simple text values in your global lists, you can now have rich objects with multiple properties like brand name, code, description, logo URL, etc. This enables sophisticated use cases like:

Business Value

  • Master Feature Dictionary (MFD): Complex feature definitions with metadata, ownership, documentation links
  • Rich Reference Data: Global lists with multiple attributes per entry (code, name, description, sort order)
  • Structured Lookups: Multi-field dropdown options that carry additional context
  • Brand Management: Complete brand information including logos, codes, and descriptions in one global list

Critical Implementation Details

  • Use ComplexAttribute (NOT CompositeAttribute) as the AttributeType
  • Use SubAttributes array (NOT CompositeFields) to define nested fields
  • Each SubAttribute must be a complete attribute definition with all required properties
  • Attribute types must match exactly: NumberAttribute not NumericAttribute, TextAttribute not Text

Global List with Complex Attributes Example

Working example of a global list with complex structure for rich brand data

typescript
// ✅ WORKING: Global List with Complex Attributes
const brandGlobalListAttribute = {
  Uid: generateGuid(),
  Alias: 'BrandList',
  BackofficeName: 'Brand Information',
  AttributeType: 'ComplexAttribute', // MUST be ComplexAttribute
  Localized: false,
  ReadOnly: false,
  Mandatory: false,
  Columns: 12,
  Unchangeable: false,
  DisableRevisionLogging: false,
  DisableIndexing: false,
  SubAttributes: [ // MUST be SubAttributes, not CompositeFields
    {
      Uid: generateGuid(),
      Alias: 'BrandName',
      BackofficeName: 'Brand Name',
      AttributeType: 'TextAttribute', // MUST be TextAttribute
      Localized: false,
      ReadOnly: false,
      Mandatory: true,
      Columns: 6,
      Unchangeable: false,
      DisableRevisionLogging: false,
      DisableIndexing: false,
      MaxLength: 255,
      UseMultiRowInput: false,
      UseRichText: false,
      ShowCharacterCount: false
    },
    {
      Uid: generateGuid(),
      Alias: 'BrandCode',
      BackofficeName: 'Brand Code',
      AttributeType: 'TextAttribute',
      Localized: false,
      ReadOnly: false,
      Mandatory: true,
      Columns: 3,
      Unchangeable: false,
      DisableRevisionLogging: false,
      DisableIndexing: false,
      MaxLength: 50
    },
    {
      Uid: generateGuid(),
      Alias: 'BrandLogoUrl',
      BackofficeName: 'Logo URL',
      AttributeType: 'TextAttribute',
      Localized: false,
      ReadOnly: false,
      Mandatory: false,
      Columns: 12,
      Unchangeable: false,
      DisableRevisionLogging: false,
      DisableIndexing: false,
      MaxLength: 500
    },
    {
      Uid: generateGuid(),
      Alias: 'IsActive',
      BackofficeName: 'Active',
      AttributeType: 'BooleanAttribute',
      Localized: false,
      ReadOnly: false,
      Mandatory: false,
      Columns: 3,
      Unchangeable: false,
      DisableRevisionLogging: false,
      DisableIndexing: false
    }
  ],
  RenderValuesForAttributeFieldUids: [],
  RenderValuesForBackofficeAttributeFieldUids: [],
  RenderedValueSeparator: null,
  RenderedValueInBackofficeSeparator: null
};

// Common errors and solutions:
// ❌ "No implementation was found for: CompositeAttribute" → Use ComplexAttribute
// ❌ "No implementation was found for: NumericAttribute" → Use NumberAttribute
// ❌ "No implementation was found for: Text" → Use TextAttribute
// ⚠️ Elasticsearch indexing errors may occur but don't indicate configuration problems

Example 2: Physical Dimensions

Ideal for products requiring size specifications with custom separators for display:

Physical Dimensions Example

Complex Attribute for product dimensions with custom rendering

typescript
const dimensionsAttribute = {
  Uid: generateGuid(),
  Alias: 'PhysicalDimensions',
  BackofficeName: 'Physical Dimensions',
  AttributeType: 'ComplexAttribute',
  Localized: false,
  ReadOnly: false,
  Mandatory: false,
  Columns: 12,
  Unchangeable: false,
  DisableRevisionLogging: false,
  DisableIndexing: false,
  SubAttributes: [
    {
      Uid: generateGuid(),
      Alias: 'Length',
      BackofficeName: 'Length (cm)',
      AttributeType: 'NumberAttribute',
      Localized: false,
      ReadOnly: false,
      Mandatory: false,
      Columns: 4,
      Unchangeable: false,
      DisableRevisionLogging: false,
      DisableIndexing: false,
      NumberOfDecimals: 2,
      Min: 0,
      Max: 1000,
      Unit: 'cm'
    },
    {
      Uid: generateGuid(),
      Alias: 'Width',
      BackofficeName: 'Width (cm)', 
      AttributeType: 'NumberAttribute',
      Localized: false,
      ReadOnly: false,
      Mandatory: false,
      Columns: 4,
      Unchangeable: false,
      DisableRevisionLogging: false,
      DisableIndexing: false,
      NumberOfDecimals: 2,
      Min: 0,
      Max: 1000,
      Unit: 'cm'
    },
    {
      Uid: generateGuid(),
      Alias: 'Height',
      BackofficeName: 'Height (cm)',
      AttributeType: 'NumberAttribute',
      Localized: false,
      ReadOnly: false,
      Mandatory: false,
      Columns: 4,
      Unchangeable: false,
      DisableRevisionLogging: false,
      DisableIndexing: false,
      NumberOfDecimals: 2,
      Min: 0,
      Max: 1000,
      Unit: 'cm'
    }
  ],
  // Custom separators for display: "100 × 50 × 25"
  RenderValuesForAttributeFieldUids: [],
  RenderValuesForBackofficeAttributeFieldUids: [],
  RenderedValueSeparator: ' × ',
  RenderedValueInBackofficeSeparator: ' × '
};

Best Practices

1. Naming Conventions

Follow consistent naming patterns:

Naming Guidelines

  • Aliases: Use PascalCase without spaces (TechnicalSpecs, PhysicalDimensions)
  • BackofficeNames: Use readable names with spaces (Technical Specifications, Physical Dimensions)
  • SubAttribute Aliases: Use descriptive names (ModelNumber, PowerRating, IsEnergyEfficient)

UI Layout (Columns)

  • Complex Attributes: Use full width (Columns: 12)
  • SubAttributes: Half width for paired fields (Columns: 6)
  • Dimensions: Third width for triplets (Columns: 4)
  • Compact fields: Quarter width (Columns: 3)

Troubleshooting

Common Issues and Solutions

Frequent Problems

  • "The attribute field is required" Error: Send attribute object directly, not wrapped in container
  • SubAttribute Creation Failures: Ensure each SubAttribute has all required base properties
  • Type Validation Errors: Use type casting for API calls (as any)
  • Complex Attribute Not Displaying: Check Columns layout and rendering configuration

Debugging Tips

  • Log Full Payloads: Always log the complete request before sending
  • Check Response Data: API errors often contain detailed validation messages
  • Validate UUIDs: Ensure all Uid fields contain valid UUIDs
  • Test SubAttributes Individually: Create simple attributes first, then complex ones
  • Export and Compare: Use export functionality to see how existing complex attributes are structured

Error Handling Example

Proper error handling when creating Complex Attributes

typescript
// Proper error handling
try {
  await client.createAttribute(complexAttribute as any);
  console.log(`✅ Complex attribute created: ${complexAttribute.Alias}`);
} catch (error) {
  console.error(`❌ Failed to create ${complexAttribute.Alias}:`, error.response?.data);
  // Log detailed error information for debugging
}

// API Response Patterns
// Success Response: "" (empty string)
// Error Response:
{
  "error": "Validation failed",
  "details": {
    "SubAttributes[0].Alias": "The field is required",
    "SubAttributes[1].NumberOfDecimals": "Must be between 0 and 10"
  }
}

Conclusion

Complex Attributes are a powerful feature of Struct PIM that enable sophisticated product data modeling. By understanding the actual API requirements (not just the Swagger documentation) and following the patterns outlined in this guide, you can successfully implement complex nested data structures for your products.

The key insights from our empirical testing:

Key Takeaways

  1. Use the complete attribute schema - don't rely solely on Swagger docs
  2. SubAttributes must be complete attribute definitions - not just references
  3. All base properties are required - even if they seem optional
  4. TypeScript casting may be necessary - the API is more flexible than types suggest
  5. Test thoroughly and iterate - complex attributes have many moving parts

Related Articles

Detailed technical review of Struct PIM system including configurable product models, API capabilities, and real-world implementation insights.

Struct
Configurable
PIM
Read Article

Comprehensive tutorial for Bluestone PIM API integration. Learn authentication, create attribute types, manage products, and build complete PIM workflows with TypeScript examples.

Bluestone
API
TypeScript
Read Article

Practical guide to PIM system selection focusing on data model testing, attribute requirements, and vendor-neutral evaluation criteria.

PIM
Selection
Guide
Read Article

About This Article

Category: PIM Systems

Review Status: Published

Related PIM Systems: struct

Related Articles: 3 related articles available

Sivert Kjøller Bertelsen

Ready to Transform Your Product Data Management?

Let's discuss how Impact Commerce can help you achieve your digital commerce goals.