Developer Tutorials

Complete Bluestone PIM API Integration: Authentication, Attributes & Products with TypeScript

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

Published January 15, 2025
20 min read
Sivert Kjøller Bertelsen
Bluestone
API
TypeScript
Tutorial
Integration
MACH
Authentication

What You'll Learn

This tutorial guides you through integrating with the Bluestone PIM API, from initial authentication to advanced product search functionality. You'll build a complete TypeScript application that demonstrates real-world API usage patterns.

Bluestone's MACH-certified API architecture provides powerful search capabilities through Elasticsearch, comprehensive authentication via MAPI credentials, and flexible data access patterns. This tutorial covers both the successful integration patterns and the permission-based limitations you may encounter.

Tutorial Outcomes

  • Authenticate with Bluestone MAPI credentials
  • Set up TypeScript project with auto-generated SDK
  • Implement product search using Elasticsearch API
  • Handle authentication tokens and automatic renewal
  • Build reusable API client patterns
  • Understand API permissions and access levels

Prerequisites

Required Credentials

Before starting, ensure you have access to a Bluestone PIM test environment with the following credentials:

MAPI Credentials Required

  • Organization name (e.g., 'SANDBOX - POC')
  • Environment designation (TEST/PRODUCTION)
  • PAPI-key for API access
  • MAPI Client ID for OAuth2 authentication
  • MAPI Client Secret for secure token generation

Development Environment

  • Node.js 16+ with npm/yarn
  • TypeScript development setup
  • Code editor with TypeScript support
  • Basic understanding of REST APIs and OAuth2
  • Familiarity with async/await patterns

Getting Started

Step 1: Initialize Your Project

Create a new Node.js project and install the necessary dependencies for TypeScript development and Bluestone API integration.

Project Initialization

Set up a new TypeScript project with required dependencies

bash
# Initialize new Node.js project
npm init -y

# Install Bluestone SDK using the official generator
npx api install "@docs-api-test-bspim/v1-core#dzoga1clxllapfw" --yes

# Install TypeScript development dependencies
npm install -D typescript @types/node ts-node

# Create TypeScript configuration
echo '{
  "compilerOptions": {
    "target": "ES2020",
    "module": "commonjs",
    "lib": ["ES2020", "DOM"],
    "outDir": "./dist",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "resolveJsonModule": true,
    "baseUrl": "."
  },
  "include": ["src/**/*", ".api/**/*"],
  "exclude": ["node_modules", "dist"]
}' > tsconfig.json

# Create source directory
mkdir src
"While this tutorial focuses on TypeScript implementation, Bluestone's API reference provides comprehensive examples across 20+ programming languages including C#/.NET, Python, Java, PHP, Ruby, and Go. The core authentication and API patterns remain consistent regardless of your chosen language."
SB
Sivert Kjøller Bertelsen
API Integration Specialist

Step 2: Implement Authentication

Bluestone uses OAuth2 client credentials flow for API authentication. The MAPI credentials generate bearer tokens that expire after 3600 seconds (1 hour).

Understanding Bluestone Authentication

The authentication process involves posting your client credentials to the identity provider endpoint to receive a bearer token. This token must be included in all subsequent API requests as an Authorization header.

For complete API documentation including all available endpoints, authentication details, and request/response schemas, refer to the Bluestone OpenAPI Documentation.

Authentication Implementation

Complete authentication module with credential management and token handling

typescript
import docsApiTestBspim from '../.api/apis/docs-api-test-bspim';

// Bluestone API Credentials
const CREDENTIALS = {
  // Replace with your actual credentials
  clientId: 'your-client-id-here',
  clientSecret: 'your-client-secret-here',
  grantType: 'client_credentials'
};

async function authenticateWithBluestoneAPI() {
  try {
    console.log('🔐 Authenticating with Bluestone API...');
    console.log('📍 Organization: Your Organization');
    console.log('🌍 Environment: TEST');
    
    // Generate token using MAPI credentials
    const response = await docsApiTestBspim.generateToken({
      client_id: CREDENTIALS.clientId,
      client_secret: CREDENTIALS.clientSecret,
      grant_type: CREDENTIALS.grantType
    });

    console.log('✅ Authentication successful!');
    
    if (response.data && response.data.access_token) {
      console.log('🎟️  Access Token obtained');
      console.log('⏱️  Expires in:', response.data.expires_in, 'seconds');
      console.log('🏷️  Token Type:', response.data.token_type);
      
      return {
        accessToken: response.data.access_token,
        expiresIn: response.data.expires_in,
        tokenType: response.data.token_type
      };
    }
    
  } catch (error) {
    console.error('❌ Authentication failed:', error);
    throw error;
  }
}

export default authenticateWithBluestoneAPI;
export { CREDENTIALS };

Step 3: Build API Client with Token Management

Create a reusable API client that handles authentication, token validation, and automatic renewal. This pattern ensures your application maintains valid authentication throughout its lifecycle.

Key Features

The API client implements automatic token validation, handles expired tokens by re-authenticating, and provides a clean interface for making authenticated requests to different Bluestone API endpoints.

API Client with Token Management

Reusable API client that handles authentication and token lifecycle

typescript
import docsApiTestBspim from '../.api/apis/docs-api-test-bspim';
import authenticateWithBluestoneAPI from './authenticate';

interface AuthenticatedApiClient {
  accessToken: string;
  expiresIn: number;
  tokenType: string;
  authenticatedAt: Date;
}

class BluestoneApiClient {
  private client: AuthenticatedApiClient | null = null;

  async initialize(): Promise<void> {
    console.log('🚀 Initializing Bluestone API Client...');
    
    const authInfo = await authenticateWithBluestoneAPI();
    
    if (authInfo && authInfo.accessToken && authInfo.expiresIn && authInfo.tokenType) {
      this.client = {
        accessToken: authInfo.accessToken,
        expiresIn: authInfo.expiresIn,
        tokenType: authInfo.tokenType,
        authenticatedAt: new Date()
      };
      
      // Configure the SDK with the bearer token
      docsApiTestBspim.auth(authInfo.accessToken);
      
      console.log('✅ API Client initialized and authenticated!');
    } else {
      throw new Error('Failed to authenticate with Bluestone API');
    }
  }

  isTokenValid(): boolean {
    if (!this.client) return false;
    
    const now = new Date();
    const elapsed = (now.getTime() - this.client.authenticatedAt.getTime()) / 1000;
    const isValid = elapsed < this.client.expiresIn;
    
    if (!isValid) {
      console.log('⚠️  Token has expired, re-authentication required');
    }
    
    return isValid;
  }

  getTokenInfo(): AuthenticatedApiClient | null {
    return this.client;
  }

  displayStatus(): void {
    if (!this.client) {
      console.log('❌ API Client not initialized');
      return;
    }

    const now = new Date();
    const elapsed = (now.getTime() - this.client.authenticatedAt.getTime()) / 1000;
    const remaining = this.client.expiresIn - elapsed;

    console.log('\n📊 Bluestone API Client Status:');
    console.log(`🔑 Token Type: ${this.client.tokenType}`);
    console.log(`⏰ Authenticated at: ${this.client.authenticatedAt.toISOString()}`);
    console.log(`⏱️  Time remaining: ${Math.max(0, Math.floor(remaining))} seconds`);
    console.log(`✅ Token valid: ${this.isTokenValid() ? 'Yes' : 'No'}`);
  }
}

export default BluestoneApiClient;

Step 4: Implement Product Search

Bluestone provides powerful product search capabilities through Elasticsearch. The search API supports complex queries, filtering, sorting, and pagination - essential for building robust PIM integrations.

Understanding Bluestone API Endpoints

Based on real-world testing, different API endpoints have varying permission requirements. The Elasticsearch search endpoint typically has broader access than detailed product management endpoints.

Product Search Implementation

Complete product search client with Elasticsearch support

typescript
import BluestoneApiClient from './api-client';

// Bluestone API URLs for different services
const API_ENDPOINTS = {
  MANAGEMENT_API: 'https://api.test.bluestonepim.com',
  SEARCH_API: 'https://api.test.bluestonepim.com/search'
};

interface ElasticsearchQuery {
  query?: any;
  size?: number;
  from?: number;
  sort?: any[];
}

class ProductSearchClient extends BluestoneApiClient {
  
  /**
   * Search for products using Elasticsearch query
   */
  async searchProductsElasticsearch(elasticsearchQuery: ElasticsearchQuery): Promise<any> {
    await this.ensureAuthenticated();
    
    console.log('🔍 Searching products with Elasticsearch query:', elasticsearchQuery);

    try {
      const response = await this.makeAuthenticatedAPIRequest(
        `${API_ENDPOINTS.SEARCH_API}/products/search`,
        'POST',
        elasticsearchQuery
      );
      
      console.log('✅ Elasticsearch product search successful!');
      return response;
      
    } catch (error) {
      console.error('❌ Elasticsearch product search failed:', error);
      throw error;
    }
  }

  /**
   * Ensure we have a valid authentication token
   */
  protected async ensureAuthenticated(): Promise<void> {
    if (!this.isTokenValid()) {
      console.log('🔄 Re-authenticating...');
      await this.initialize();
    }
  }

  /**
   * Make an authenticated HTTP request to the Bluestone API
   */
  protected async makeAuthenticatedAPIRequest(
    url: string, 
    method: 'GET' | 'POST' | 'PUT' | 'DELETE' = 'GET',
    body?: any
  ): Promise<any> {
    const tokenInfo = this.getTokenInfo();
    
    if (!tokenInfo) {
      throw new Error('No authentication token available');
    }

    const headers: Record<string, string> = {
      'Authorization': `${tokenInfo.tokenType} ${tokenInfo.accessToken}`,
      'Content-Type': 'application/json'
    };

    const requestOptions: RequestInit = {
      method,
      headers
    };

    if (body && (method === 'POST' || method === 'PUT')) {
      requestOptions.body = JSON.stringify(body);
    }

    console.log(`📡 Making ${method} request to: ${url}`);
    
    const response = await fetch(url, requestOptions);
    
    if (!response.ok) {
      const errorText = await response.text();
      throw new Error(`HTTP ${response.status}: ${errorText}`);
    }

    return await response.json();
  }
}

export default ProductSearchClient;

Step 5: Attribute Management

Beyond basic product search, Bluestone PIM provides comprehensive attribute management capabilities. You can create custom attribute types, organize them in groups, and assign values to products - essential for building robust product information management workflows.

Attribute System Overview

Bluestone's attribute system consists of three main components:

Attribute System Components

  • <strong>Attribute Groups</strong> - Organize related attributes together
  • <strong>Attribute Definitions</strong> - Define the structure and data type of attributes
  • <strong>Attribute Values</strong> - Assign actual values to products for each attribute

Supported Data Types (11 Working Types)

  • Text fields for names and descriptions
  • Numbers (integers and decimals) with units
  • Dates, times, and timestamps
  • Boolean fields for flags and status
  • Pattern validation for structured data
  • Rich formatted text with HTML
  • Location fields for addresses

Creating Attribute Groups

Organize related attributes by creating attribute groups first

typescript
import ProductSearchClient from './product-search';

class AttributeManagementClient extends ProductSearchClient {
  
  /**
   * Create an attribute group to organize related attributes
   */
  async createAttributeGroup(groupData: {
    name: string;
    description: string;
    metadata?: any;
  }): Promise<string | null> {
    await this.ensureAuthenticated();
    
    console.log('📁 Creating attribute group:', groupData.name);

    try {
      const response = await this.makeAuthenticatedAPIRequest(
        'https://api.test.bluestonepim.com/pim/attribute-groups',
        'POST',
        groupData
      );
      
      // Extract group ID from Location header
      const groupId = response.headers?.get?.('resource-id') || 
                     response.headers?.get?.('location')?.split('/').pop();
      
      if (groupId) {
        console.log('✅ Attribute group created with ID:', groupId);
        return groupId;
      } else {
        console.warn('⚠️  Group created but ID not found in headers');
        return null;
      }
      
    } catch (error) {
      if (error.message.includes('409')) {
        console.log('ℹ️  Attribute group already exists:', groupData.name);
        return null; // Handle conflict gracefully
      }
      console.error('❌ Failed to create attribute group:', error);
      throw error;
    }
  }

  /**
   * Override the base request method to handle headers properly
   */
  protected async makeAuthenticatedAPIRequest(
    url: string, 
    method: 'GET' | 'POST' | 'PUT' | 'DELETE' = 'GET',
    body?: any
  ): Promise<any> {
    const tokenInfo = this.getTokenInfo();
    
    if (!tokenInfo) {
      throw new Error('No authentication token available');
    }

    const headers: Record<string, string> = {
      'Authorization': `${tokenInfo.tokenType} ${tokenInfo.accessToken}`,
      'Content-Type': 'application/json'
    };

    const requestOptions: RequestInit = {
      method,
      headers
    };

    if (body && (method === 'POST' || method === 'PUT')) {
      requestOptions.body = JSON.stringify(body);
    }

    console.log(`📡 Making ${method} request to: ${url}`);
    
    const response = await fetch(url, requestOptions);
    
    if (!response.ok) {
      const errorText = await response.text();
      throw new Error(`HTTP ${response.status}: ${errorText}`);
    }

    // Return response object with headers for resource ID extraction
    return {
      data: response.status === 204 ? null : await response.json().catch(() => null),
      headers: response.headers
    };
  }
}

export default AttributeManagementClient;

Step 6: Creating Attribute Definitions

Attribute definitions specify the data type, validation rules, and structure for product attributes. Bluestone supports 11 fully working attribute types that cover most PIM use cases.

Working Attribute Types

Based on comprehensive testing, these attribute types provide complete end-to-end functionality:

Attribute Definition Creation Examples

Create different types of attribute definitions with proper validation

typescript
class AttributeDefinitionClient extends AttributeManagementClient {
  
  /**
   * Create an attribute definition
   */
  async createAttributeDefinition(attributeData: any): Promise<string | null> {
    await this.ensureAuthenticated();
    
    console.log('🏷️  Creating attribute definition:', attributeData.name);

    try {
      const response = await this.makeAuthenticatedAPIRequest(
        'https://api.test.bluestonepim.com/pim/attribute-definitions',
        'POST',
        attributeData
      );
      
      const definitionId = response.headers?.get('resource-id');
      
      if (definitionId) {
        console.log('✅ Attribute definition created with ID:', definitionId);
        return definitionId;
      }
      
      return null;
      
    } catch (error) {
      if (error.message.includes('409')) {
        console.log('ℹ️  Attribute definition already exists:', attributeData.name);
        return null;
      }
      console.error('❌ Failed to create attribute definition:', error);
      throw error;
    }
  }

  /**
   * Get attribute definition templates for all working types
   */
  getAttributeTemplates(groupId: string) {
    return {
      // 1. Basic text field
      text: {
        name: 'Product Title',
        dataType: 'text',
        description: 'Basic product title field',
        groupId: groupId
      },

      // 2. Multi-line text
      multiline: {
        name: 'Product Description',
        dataType: 'multiline',
        description: 'Detailed product description field',
        groupId: groupId
      },

      // 3. Pattern validation (email example)
      pattern: {
        name: 'Contact Email',
        dataType: 'pattern',
        description: 'Email address with validation',
        groupId: groupId,
        restrictions: {
          text: {
            pattern: '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$'
          }
        }
      },

      // 4. Boolean field
      boolean: {
        name: 'Is Featured Product',
        dataType: 'boolean',
        description: 'Mark product as featured',
        groupId: groupId
      },

      // 5. Decimal number with unit
      decimal: {
        name: 'Product Weight',
        dataType: 'decimal',
        unit: 'kg',
        description: 'Product weight in kilograms',
        groupId: groupId
      },

      // 6. Integer number
      integer: {
        name: 'Stock Quantity',
        dataType: 'integer',
        description: 'Available stock quantity',
        groupId: groupId
      },

      // 7. Date field
      date: {
        name: 'Launch Date',
        dataType: 'date',
        description: 'Product launch date',
        groupId: groupId
      },

      // 8. DateTime field
      dateTime: {
        name: 'Last Updated',
        dataType: 'date_time',
        description: 'Last modification timestamp',
        groupId: groupId
      },

      // 9. Time field
      time: {
        name: 'Processing Time',
        dataType: 'time',
        description: 'Required processing time',
        groupId: groupId
      },

      // 10. Location field
      location: {
        name: 'Origin Location',
        dataType: 'location',
        description: 'Product origin location',
        groupId: groupId
      },

      // 11. Formatted text (HTML)
      formattedText: {
        name: 'Marketing Content',
        dataType: 'formatted_text',
        description: 'Rich marketing content with formatting',
        groupId: groupId
      }
    };
  }

  /**
   * Create a complete set of common product attributes
   */
  async createProductAttributeSchema(groupId: string): Promise<Record<string, string>> {
    const templates = this.getAttributeTemplates(groupId);
    const attributeIds: Record<string, string> = {};
    
    console.log('🏗️  Creating complete product attribute schema...');
    
    // Create each attribute type with delay to respect rate limits
    for (const [key, template] of Object.entries(templates)) {
      try {
        const definitionId = await this.createAttributeDefinition(template);
        if (definitionId) {
          attributeIds[key] = definitionId;
        }
        
        // Add delay between creation calls
        await new Promise(resolve => setTimeout(resolve, 200));
        
      } catch (error) {
        console.error(`Failed to create ${key} attribute:`, error);
      }
    }
    
    console.log(`✅ Created ${Object.keys(attributeIds).length} attribute definitions`);
    return attributeIds;
  }
}

export default AttributeDefinitionClient;

Step 7: Product Creation and Attribute Assignment

With your attribute structure in place, you can now create products and assign attribute values. This demonstrates the complete PIM workflow from schema definition to data population.

Product Creation Process

Product creation follows a two-step process: first create the product with basic information, then assign attribute values using the product ID and attribute definition IDs.

Product Creation and Attribute Assignment

Complete workflow for creating products and assigning attribute values

typescript
class ProductManagementClient extends AttributeDefinitionClient {
  
  /**
   * Create a new product
   */
  async createProduct(productData: {
    sku: string;
    name: string;
    description?: string;
  }): Promise<string | null> {
    await this.ensureAuthenticated();
    
    console.log('📦 Creating product:', productData.name);

    try {
      const response = await this.makeAuthenticatedAPIRequest(
        'https://api.test.bluestonepim.com/pim/products',
        'POST',
        productData
      );
      
      // Extract product ID from Location header
      const productId = response.headers?.get('location')?.split('/').pop() ||
                       response.headers?.get('resource-id');
      
      if (productId) {
        console.log('✅ Product created with ID:', productId);
        return productId;
      }
      
      return null;
      
    } catch (error) {
      console.error('❌ Failed to create product:', error);
      throw error;
    }
  }

  /**
   * Assign attribute values to a product
   */
  async assignProductAttributes(
    productId: string, 
    attributeValues: Array<{ definitionId: string; value: any }>
  ): Promise<boolean> {
    await this.ensureAuthenticated();
    
    console.log(`🏷️  Assigning ${attributeValues.length} attributes to product ${productId}`);

    try {
      await this.makeAuthenticatedAPIRequest(
        `https://api.test.bluestonepim.com/pim/products/${productId}/attributes`,
        'POST',
        attributeValues
      );
      
      console.log('✅ Attribute values assigned successfully');
      return true;
      
    } catch (error) {
      console.error('❌ Failed to assign attribute values:', error);
      throw error;
    }
  }

  /**
   * Get sample attribute values for testing
   */
  getSampleAttributeValues(attributeIds: Record<string, string>) {
    return [
      // Text value
      { 
        definitionId: attributeIds.text, 
        value: 'Amazing Sample Product' 
      },
      
      // Multiline text
      { 
        definitionId: attributeIds.multiline, 
        value: 'This is a detailed\nproduct description with\nmultiple lines of content.' 
      },
      
      // Email pattern
      { 
        definitionId: attributeIds.pattern, 
        value: 'contact@example.com' 
      },
      
      // Boolean value
      { 
        definitionId: attributeIds.boolean, 
        value: true 
      },
      
      // Decimal with unit
      { 
        definitionId: attributeIds.decimal, 
        value: 2.5 
      },
      
      // Integer value
      { 
        definitionId: attributeIds.integer, 
        value: 150 
      },
      
      // Date value
      { 
        definitionId: attributeIds.date, 
        value: '2024-06-01' 
      },
      
      // DateTime value
      { 
        definitionId: attributeIds.dateTime, 
        value: '2024-01-15T10:30:00Z' 
      },
      
      // Time value
      { 
        definitionId: attributeIds.time, 
        value: '14:30:00' 
      },
      
      // Location value
      { 
        definitionId: attributeIds.location, 
        value: 'Oslo, Norway' 
      },
      
      // Formatted text with HTML
      { 
        definitionId: attributeIds.formattedText, 
        value: '<h2>Product Features</h2><p>This product includes <strong>amazing</strong> features and <em>premium</em> quality.</p>' 
      }
    ].filter(attr => attr.definitionId); // Only include attributes that were created successfully
  }

  /**
   * Complete product creation workflow
   */
  async createCompleteProduct(productInfo: {
    sku: string;
    name: string;
    description?: string;
  }, attributeIds: Record<string, string>): Promise<string | null> {
    
    console.log('🚀 Starting complete product creation workflow...');
    
    try {
      // Step 1: Create the product
      const productId = await this.createProduct(productInfo);
      
      if (!productId) {
        throw new Error('Failed to create product - no ID returned');
      }
      
      // Step 2: Prepare attribute values
      const attributeValues = this.getSampleAttributeValues(attributeIds);
      
      if (attributeValues.length === 0) {
        console.warn('⚠️  No attribute values to assign');
        return productId;
      }
      
      // Step 3: Assign attribute values
      await this.assignProductAttributes(productId, attributeValues);
      
      console.log('🎉 Complete product created successfully!');
      console.log(`📋 Product ID: ${productId}`);
      console.log(`🏷️  Attributes assigned: ${attributeValues.length}`);
      
      return productId;
      
    } catch (error) {
      console.error('💥 Complete product creation failed:', error);
      throw error;
    }
  }

  /**
   * Validate attribute value format before assignment
   */
  validateAttributeValue(dataType: string, value: any): boolean {
    switch (dataType) {
      case 'boolean':
        return typeof value === 'boolean';
      case 'integer':
        return Number.isInteger(value);
      case 'decimal':
        return typeof value === 'number' && !isNaN(value);
      case 'date':
        return /^\d{4}-\d{2}-\d{2}$/.test(value);
      case 'date_time':
        return /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z?$/.test(value);
      case 'time':
        return /^\d{2}:\d{2}:\d{2}$/.test(value);
      case 'pattern':
        return typeof value === 'string'; // Pattern validation happens server-side
      default:
        return typeof value === 'string';
    }
  }
}

export default ProductManagementClient;

Step 8: Complete Integration Examples

Now let's put everything together with comprehensive examples that demonstrate the complete Bluestone PIM integration workflow from authentication through product creation with attributes.

Complete Integration Examples

Comprehensive examples showing the complete PIM workflow from attributes to products

typescript
import ProductManagementClient from './product-management';

async function demonstrateCompleteBluestoneIntegration() {
  const client = new ProductManagementClient();
  
  try {
    // Initialize and authenticate
    console.log('🚀 Initializing Bluestone PIM Integration...');
    await client.initialize();
    
    // Example 1: Create attribute structure
    console.log('\n1️⃣ Creating Attribute Structure');
    
    // Step 1: Create attribute group
    const groupId = await client.createAttributeGroup({
      name: 'Product Information',
      description: 'Core product information attributes',
      metadata: {
        source: 'api_integration_tutorial',
        category: 'core_data'
      }
    });
    
    if (!groupId) {
      console.log('ℹ️  Using existing attribute group or skipping...');
      return;
    }
    
    // Step 2: Create all attribute definitions
    console.log('\n📋 Creating attribute definitions...');
    const attributeIds = await client.createProductAttributeSchema(groupId);
    
    console.log(`\n✅ Created ${Object.keys(attributeIds).length} attribute definitions:`);
    Object.entries(attributeIds).forEach(([type, id]) => {
      console.log(`   ${type}: ${id}`);
    });
    
    // Example 2: Create product with attributes
    console.log('\n2️⃣ Creating Complete Product with Attributes');
    
    const productInfo = {
      sku: 'DEMO-PROD-001',
      name: 'Demo Product with Full Attributes',
      description: 'A demonstration product showcasing all attribute types'
    };
    
    const productId = await client.createCompleteProduct(productInfo, attributeIds);
    
    if (productId) {
      console.log(`\n🎉 Successfully created complete product: ${productId}`);
    }
    
    // Example 3: Search for the created product
    console.log('\n3️⃣ Searching for Created Product');
    const searchResults = await client.searchProductsElasticsearch({
      query: {
        bool: {
          must: [
            { term: { "sku": "DEMO-PROD-001" } }
          ]
        }
      },
      size: 1
    });
    
    if (searchResults && searchResults.data && searchResults.data.length > 0) {
      console.log('✅ Found created product:');
      console.log(`   ID: ${searchResults.data[0].id}`);
      console.log(`   SKU: ${searchResults.data[0].sku}`);
      console.log(`   Name: ${searchResults.data[0].name}`);
    }
    
    // Example 4: Validate attribute values
    console.log('\n4️⃣ Attribute Value Validation Examples');
    const testValues = [
      { type: 'boolean', value: true, expected: true },
      { type: 'boolean', value: 'true', expected: false },
      { type: 'integer', value: 42, expected: true },
      { type: 'integer', value: 3.14, expected: false },
      { type: 'date', value: '2024-01-15', expected: true },
      { type: 'date', value: '15/01/2024', expected: false },
      { type: 'date_time', value: '2024-01-15T10:30:00Z', expected: true },
      { type: 'time', value: '14:30:00', expected: true }
    ];
    
    testValues.forEach(test => {
      const isValid = client.validateAttributeValue(test.type, test.value);
      const status = isValid === test.expected ? '✅' : '❌';
      console.log(`   ${status} ${test.type}: ${JSON.stringify(test.value)} -> ${isValid}`);
    });
    
    // Example 5: Bulk product creation
    console.log('\n5️⃣ Bulk Product Creation Example');
    const bulkProducts = [
      { sku: 'BULK-001', name: 'Bulk Product 1' },
      { sku: 'BULK-002', name: 'Bulk Product 2' },
      { sku: 'BULK-003', name: 'Bulk Product 3' }
    ];
    
    const bulkResults = [];
    for (const product of bulkProducts) {
      try {
        const bulkProductId = await client.createProduct(product);
        if (bulkProductId) {
          bulkResults.push({ sku: product.sku, id: bulkProductId });
        }
        // Add delay between bulk operations
        await new Promise(resolve => setTimeout(resolve, 300));
      } catch (error) {
        console.error(`Failed to create bulk product ${product.sku}:`, error.message);
      }
    }
    
    console.log(`✅ Created ${bulkResults.length} bulk products`);
    
    // Show final status
    client.displayStatus();
    
  } catch (error) {
    console.error('💥 Complete integration demonstration failed:', error);
  }
}

// Alternative focused examples
async function demonstrateSpecificFeatures() {
  const client = new ProductManagementClient();
  await client.initialize();
  
  console.log('\n🎯 Focused Feature Demonstrations');
  
  // Search-only example
  console.log('\n🔍 Product Search Examples');
  const searchExamples = [
    {
      name: 'Match All Products',
      query: { query: { match_all: {} }, size: 5 }
    },
    {
      name: 'Recent Products',
      query: {
        query: { match_all: {} },
        size: 3,
        sort: [{ "_id": { "order": "desc" } }]
      }
    },
    {
      name: 'Products with Specific Fields',
      query: {
        query: {
          bool: {
            must: [
              { exists: { field: "sku" } },
              { exists: { field: "name" } }
            ]
          }
        },
        size: 3
      }
    }
  ];
  
  for (const example of searchExamples) {
    try {
      const results = await client.searchProductsElasticsearch(example.query);
      console.log(`   ${example.name}: ${results.data?.length || 0} products`);
    } catch (error) {
      console.error(`   ${example.name}: Failed - ${error.message}`);
    }
  }
}

// Run the demonstrations
if (require.main === module) {
  demonstrateCompleteBluestoneIntegration().then(() => {
    console.log('\n' + '='.repeat(50));
    return demonstrateSpecificFeatures();
  });
}

Successfully Created Attribute Values in Bluestone PIM

Screenshot showing all 11 attribute types successfully created and populated with values in the Bluestone PIM interface. This demonstrates the complete end-to-end functionality from API integration to PIM system display.

Package.json Scripts

Convenient npm scripts for development and testing

json
{
  "name": "bluestone-api-integration",
  "version": "1.0.0",
  "scripts": {
    "build": "tsc",
    "authenticate": "ts-node src/authenticate.ts",
    "search": "ts-node src/product-search.ts",
    "demo": "ts-node src/demo.ts",
    "dev": "ts-node src/demo.ts"
  },
  "dependencies": {
    "@api/docs-api-test-bspim": "file:.api/apis/docs-api-test-bspim"
  },
  "devDependencies": {
    "@types/node": "^22.15.29",
    "ts-node": "^10.9.2",
    "typescript": "^5.8.3"
  }
}

Step 6: Running Your Integration

With your code in place, you can now test different aspects of the Bluestone API integration using convenient npm scripts.

Available Commands

  • `npm run authenticate` - Test basic authentication
  • `npm run search` - Run product search examples
  • `npm run demo` - Full demonstration with multiple examples
  • `npm run build` - Compile TypeScript to JavaScript
  • `npm run dev` - Development mode with auto-restart

Expected Results

When your integration is working correctly, you should see output similar to this example, demonstrating successful authentication and product discovery.

Expected Console Output

Example of successful API integration results

bash
🚀 Initializing Bluestone API Client...
🔐 Authenticating with Bluestone API...
📍 Organization: SANDBOX - POC
🌍 Environment: TEST
✅ Authentication successful!
🎟️  Access Token obtained
⏱️  Expires in: 3600 seconds
🏷️  Token Type: Bearer
✅ API Client initialized and authenticated!

1️⃣ Basic Product Search (Match All)
🔍 Searching products with Elasticsearch query: { query: { match_all: {} }, size: 10 }
📡 Making POST request to: https://api.test.bluestonepim.com/search/products/search
✅ Elasticsearch product search successful!
✅ Found 6 products:
   1. ID: 67f3c4e7bb442e4f88e45c32
   2. ID: 67f3c4a810f2da33a7c88766
   3. ID: 67f3c47c36016948acd8d0dd
   4. ID: 67f3ba1cbb442e4f88e45958
   5. ID: 67f3a654bb442e4f88e4590d
   6. ID: 67f3a5d636016948acd8cc4b

2️⃣ Advanced Boolean Query
✅ Advanced query returned 6 products

📊 Bluestone API Client Status:
🔑 Token Type: Bearer
⏰ Authenticated at: 2025-01-15T12:06:07.957Z
⏱️  Time remaining: 3599 seconds
✅ Token valid: Yes

Advanced Elasticsearch Queries

Bluestone's search API supports the full Elasticsearch Query DSL, enabling sophisticated product discovery patterns. Here are common query patterns for PIM integrations.

Advanced Elasticsearch Query Examples

Common query patterns for product search and filtering

typescript
// Example query patterns for different use cases

// 1. Match all products (basic listing)
const matchAllQuery = {
  query: { match_all: {} },
  size: 20,
  from: 0
};

// 2. Boolean query with existence filter
const existenceQuery = {
  query: {
    bool: {
      must: [
        { exists: { field: "id" } },
        { exists: { field: "name" } }
      ]
    }
  },
  size: 10
};

// 3. Sorting and pagination
const sortedQuery = {
  query: { match_all: {} },
  size: 5,
  from: 0,
  sort: [
    { "_id": { "order": "desc" } },
    { "createdAt": { "order": "desc" } }
  ]
};

// 4. Term filtering (when you know specific values)
const termQuery = {
  query: {
    bool: {
      must: [
        { term: { "status": "active" } },
        { term: { "type": "product" } }
      ]
    }
  }
};

// 5. Range queries (for dates, numbers)
const rangeQuery = {
  query: {
    bool: {
      must: [
        {
          range: {
            "createdAt": {
              "gte": "2024-01-01",
              "lte": "2024-12-31"
            }
          }
        }
      ]
    }
  }
};

// Usage example:
// const results = await client.searchProductsElasticsearch(matchAllQuery);

Troubleshooting Common Issues

Authentication Failures

If authentication fails, verify your MAPI credentials are correct and that your organization has API access enabled. The client ID and secret must exactly match what's provided by Bluestone support.

403 Forbidden Errors

Some API endpoints require higher permission levels than others. The search endpoints typically work with basic MAPI credentials, while product management endpoints may require additional permissions. This is by design for security.

Token Expiration

Bearer tokens expire after 3600 seconds. The API client automatically handles token renewal, but if you're implementing custom logic, ensure you check token validity before making requests.

Attribute-Specific Issues

Based on comprehensive testing, here are common attribute-related problems and their solutions:

Resource ID Extraction

Always extract resource IDs from the `resource-id` or `location` headers, never from the response body which is typically empty for creation operations.

Attribute Value Formats

Each attribute type has strict format requirements. Use the validation functions provided in the examples to ensure correct data types before assignment.

Pattern Regex Escaping

When using pattern validation attributes, ensure proper JSON escaping of regex special characters (double backslashes).

Network Connectivity

Ensure your development environment can reach the Bluestone test endpoints. Some corporate networks may block external API access.

Common Error Codes

  • 401 Unauthorized - Invalid or expired credentials
  • 403 Forbidden - Insufficient permissions for endpoint
  • 404 Not Found - Invalid endpoint URL or resource not found
  • 409 Conflict - Resource already exists (safe to ignore for idempotent operations)
  • 429 Rate Limited - Too many requests (default: 500/minute)
  • 500 Internal Server Error - Bluestone service issue

Attribute Creation Issues

  • <strong>Field name mismatch</strong> - Use `definitionId` not `attributeDefinitionId` in value assignments
  • <strong>Wrong data types</strong> - Boolean values must be `true`/`false`, not strings
  • <strong>Date format errors</strong> - Use ISO formats: `YYYY-MM-DD` for dates, `YYYY-MM-DDTHH:MM:SSZ` for datetime
  • <strong>Pattern validation</strong> - Double-escape regex special characters in JSON
  • <strong>Missing group IDs</strong> - Always create attribute groups before definitions
  • <strong>Rate limiting</strong> - Add delays between bulk attribute creation calls

Integration Best Practices

Error Handling

Always implement proper error handling for API calls. Network issues, rate limiting, and permission changes can occur in production environments.

Token Management

Store tokens securely and implement automatic renewal. Never log bearer tokens in production environments as they provide full API access.

Rate Limiting

Respect Bluestone's rate limits (default 500 requests/minute). Implement exponential backoff for rate-limited requests and consider caching responses when appropriate.

Environment Management

Use environment variables for credentials and API endpoints. Never commit API keys to version control.

Monitoring

Implement logging and monitoring for API integrations. Track authentication success rates, response times, and error frequencies.

Production Considerations

  • Use environment variables for sensitive credentials
  • Implement proper logging without exposing tokens
  • Add retry logic with exponential backoff
  • Monitor API usage and error rates
  • Cache responses when appropriate to reduce API calls
  • Use HTTPS for all API communication

Ready for Production?

This tutorial covers the fundamentals of Bluestone API integration. For production implementations, consider implementing advanced features like webhook handling, bulk operations, and comprehensive error recovery.

Learn About Bluestone PIM

Next Steps

Extend Your Integration

With the foundation in place, you can extend your integration to include additional Bluestone API capabilities as your permissions allow.

Integration Extensions

  • Implement webhook handlers for real-time data sync
  • Add support for bulk product operations
  • Integrate with Bluestone's workflow and task APIs
  • Build asset management functionality using the DAM API
  • Add support for category and attribute management
  • Implement data export and synchronization workflows

Production Readiness

  • Set up proper environment configuration
  • Implement comprehensive error handling and retry logic
  • Add monitoring and alerting for API failures
  • Create automated tests for your integration
  • Document your API usage patterns for team knowledge
  • Plan for scaling and rate limit management

Sources (1)

[1]
Bluestone API Reference
Bluestone(2025)API Documentation

Related Articles

Detailed technical review of Bluestone PIM system including MACH architecture, micro-services approach, and real-world implementation insights.

Bluestone
MACH
PIM
Read Article

Complete guide to Product Information Management systems. Learn what PIM is, how it works, key benefits, and how to choose the right PIM system for your business.

PIM
Product Information
Guide
Read Article

About This Article

Category: Developer Tutorials

Review Status: Published

Related PIM Systems: bluestone

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.