Kibo’s approach to catalog management is built on a foundation of reusability and inheritance. Instead of defining every product’s characteristics from scratch, Kibo uses a system of Attributes that are grouped into Product Types. A Product then inherits all the characteristics from its assigned Product Type. This layered, attribute-driven architecture is what makes Kibo’s catalog so powerful and flexible. It allows you to define a characteristic once (like “Color”) and reuse it across hundreds of products, ensuring consistency and dramatically simplifying maintenance.
The Catalog is the heart of your Kibo e-commerce ecosystem. It’s the central repository of all product information. This data directly feeds into other key domains:
Pricing: Prices are attached to products defined in the catalog.
Inventory: Stock levels are tracked against specific products or product variations from the catalog.
Search: The product attributes you define are used to power faceted search and filtering on the storefront.
Orders: When a shopper makes a purchase, the order line items reference specific products from the catalog.
Kibo’s catalog is a hierarchy. Understanding this structure is the key to mastering the API.
Attribute: The smallest piece of data, defining a characteristic (e.g., “Color,” “Size,” “Brand”). Attributes have a defined input type (text box, list, radio buttons).
Product Type: A template or a blueprint for a group of similar products. It’s a collection of Attributes. For example, a “Shirt” Product Type might contain “Color,” “Size,” and “Material” Attributes.
Product: The actual item you sell. Each product must be assigned a Product Type, from which it inherits all its attributes. You then provide values for those attributes at the product level.
Category: A grouping of products for storefront navigation. Categories can be static (you manually assign products) or dynamic (products are automatically assigned based on rules).
Before we write code, understand these patterns that appear in every Kibo API:Authentication Pattern:
The Kibo SDK manages authentication for you. You create a single Configuration object containing your credentials (Client ID, Shared Secret, etc.). This object is then used to instantiate specific API clients. The clients will automatically handle the OAuth 2.0 token exchange behind the scenes for every API call.Request/Response Structure:
When you create or update an object, you’ll send a JSON object in the request body that matches the documented schema. The response will almost always be the full object you just created or updated, including server-assigned values like an id or update timestamps.Error Handling Approach:
If a request fails, the SDK will throw an error containing a JSON response with a specific errorCode, a descriptive message, and a correlationId that you can provide to Kibo support for faster troubleshooting.Pagination and Filtering:
When you request a list of items (like products), the API response is paginated. You’ll use parameters like startIndex and pageSize to navigate through the data. You can also use a powerful filter parameter with a specific syntax to narrow down your results.API Documentation Reference:
Throughout this guide, we’ll reference specific endpoints. Find complete specs at:
/api-overviews/openapi_catalog_admin_overview
You need this anytime you want to define a new, reusable characteristic for your products. This is the first step in building your catalog’s structure. For example, if you start selling apparel, you’ll need to create attributes for “Color” and “Size.”
Kibo treats attributes as first-class citizens. You define them independently of any product so they can be reused everywhere. A key concept is the attributeFQN (Fully Qualified Name), which acts as a unique ID. You also define the inputType (e.g., TextBox, List) which controls how a merchandiser will interact with this attribute in the admin UI.
Before we implement, let’s understand what we’re building:
// We'll build this step by step:// 1. **Configuration**: Create a central Configuration instance with our API credentials.// 2. **API Client Instantiation**: Create a dedicated client for interacting with the Product Attributes API.// 3. **Attribute Definition**: Create a JSON object that defines our new attribute according to the API schema.// 4. **API Call**: Send the definition to the Kibo API to create the attribute.
// Essential imports for Catalog operations. Note we import the Configuration class// and the specific API client we need.// These imports are verified from @kibocommerce/rest-sdk documentation.import { Configuration } from "@kibocommerce/rest-sdk";import { ProductAttributesApi, CatalogAdminsAttribute } from "@kibocommerce/rest-sdk/clients/CatalogAdministration";// Configuration setup - this single object is reused for all API clients.// These properties are required per official API documentation.const configuration = new Configuration({ // Your Tenant ID from Dev Center tenantId: process.env.KIBO_TENANT_ID, // Your Site ID from Dev Center siteId: process.env.KIBO_SITE_ID, // Your Application Key from Dev Center clientId: process.env.KIBO_CLIENT_ID, // Your Application Secret from Dev Center sharedSecret: process.env.KIBO_SHARED_SECRET, // The base auth URL for your region authHost: process.env.KIBO_AUTH_HOST});
Step 2: Understanding the Data Flow
We will create a plain JavaScript object that precisely matches the Attribute schema required by the API. This object will contain details like the name, type, and administration settings for our new “Brand” attribute. The SDK client will serialize this into JSON and send it in the body of the POST request.Step 3: The Core Implementation
// Complete working example that ACTUALLY WORKS with the Kibo API.// Each line verified against official API documentation.async function createBrandAttribute(): Promise<CatalogAdminsAttribute> { // Instantiate a dedicated client for the Product Attributes resource, // passing it our shared configuration. const productAttributesClient = new ProductAttributesApi(configuration); // Define the attribute payload. Every property here is defined // in the official API schema for creating an attribute. const brandAttribute: CatalogAdminsAttribute = { // A unique identifier. The `admin@` prefix is a // common Kibo convention for custom attributes. attributeFQN: 'admin@brand', // The data type Kibo should store. dataType: 'String', // How this attribute will be displayed in the Admin UI. inputType: 'TextBox', // This tells Kibo this attribute applies to Products. attributeScope: 'Product', // The label that merchandisers will see in the Admin UI. adminName: 'Brand', // How the attribute is displayed on the storefront. labels: [{ localeCode: 'en-US', value: 'Brand', }], }; try { console.log('Creating "Brand" attribute...'); // The client provides strongly-typed methods that map to API operations. const newAttribute = await productAttributesClient.addAttribute({ catalogAdminsAttribute: brandAttribute, }); console.log('Successfully created attribute:', newAttribute); return newAttribute; } catch (error) { // The SDK surfaces Kibo's detailed error messages. console.error('Error creating attribute:', JSON.stringify(error, null, 2)); throw error; }}
The setup phase involved creating a Configuration instance. This object is the single source of truth for your API credentials.
The data preparation followed Kibo’s pattern of creating a structured object (brandAttribute) that mirrors the API’s expected JSON schema.
The API call used an instance of ProductAttributesApi. This SDK pattern separates concerns, giving you a dedicated client for each API resource. The createAttribute() method maps directly to the POST operation on the attributes endpoint.
The response handling correctly anticipates Kibo’s structured error response, making it easy to debug.
// Wrong - The SDK is not designed this way. There is no single "ApiClient".const apiClient = new ApiClient(configuration);// Correct - Instantiate a specific client for the resource you need to work with.const productAttributesClient = new ProductAttributesApi(configuration);const productTypesClient = new ProductTypesApi(configuration);
Mistake 2: Forgetting the admin@ prefix for attributeFQN.
// Wrong - Fails because `attributeFQN` must be unique and "brand" might be a system-reserved name.const attribute = { attributeFQN: 'brand' };// Correct - Using a namespace like `admin@` prevents collisions with system attributes.const attribute = { attributeFQN: 'admin@brand' };
Now let’s explore a sophisticated, multi-step workflow: adding a new color option to an existing product and activating the new product variations that result from it.
Pattern 1: Dynamically Adding an Option and Activating Variations
Business Scenario:
Your supplier has introduced a new color, “Kibo Yellow,” for an existing T-shirt. You need to add this color as a selectable option on the product page and make the new variations (e.g., “Kibo Yellow, Small,” “Kibo Yellow, Medium”) available for purchase with their own unique SKUs.Kibo’s Architecture Consideration:
This is a multi-step process because Kibo’s data is normalized. The new color “Kibo Yellow” must be:
Added to the master list of all possible colors (Attribute Vocabulary).
Associated with the T-shirt’s Product Type so other future T-shirts can also use it.
Associated with the specific T-shirt Product itself as a selectable option.
Used to generate and activate the new Product Variations.
API Endpoints Used:
POST /api/commerce/catalog/admin/attributedefinition/attributes/{attributeFQN}/vocabularyvalues
PUT /api/commerce/catalog/admin/producttypes/{productTypeId}/attributes/{attributeFQN}
PUT /api/commerce/catalog/admin/products/{productCode}/options/{attributeFQN}
PUT /api/commerce/catalog/admin/products/{productCode}/variations
Implementation Strategy:
import { Configuration } from "@kibocommerce/rest-sdk";import { ProductAttributesApi, ProductTypesApi, ProductOptionsApi, ProductVariationsApi, CatalogAdminsAttributeVocabularyValue, AttributeInProductType, ProductOption, ProductVariationPagedCollection } from "@kibocommerce/rest-sdk/clients/CatalogAdministration";// Use the same configuration object from the previous exampleconst configuration = new Configuration({ // Your Tenant ID from Dev Center tenantId: process.env.KIBO_TENANT_ID, // Your Site ID from Dev Center siteId: process.env.KIBO_SITE_ID, // Your Application Key from Dev Center clientId: process.env.KIBO_CLIENT_ID, // Your Application Secret from Dev Center sharedSecret: process.env.KIBO_SHARED_SECRET, // The base auth URL for your region authHost: process.env.KIBO_AUTH_HOST });async function addNewColorOption(colorName: string, attributeFQN: string, productTypeId: number, productCode: string) { // 1. Instantiate all the necessary API clients const productAttributesClient = new ProductAttributesApi(configuration); const productTypesClient = new ProductTypesApi(configuration); const productOptionsClient = new ProductOptionsApi(configuration); const productVariationClient = new ProductVariationsApi(configuration); // 2. Define the new color value in the required format const vocabularyValue: CatalogAdminsAttributeVocabularyValue = { value: colorName.replace(/\s/g, '-').toLowerCase(), content: { localeCode: 'en-US', stringValue: colorName } }; try { // STEP A: Add the new color to the master attribute list const newVocabValue = await productAttributesClient.addAttributeVocabularyValue({ attributeFQN, attributeVocabularyValue: vocabularyValue }); console.log(`Added attribute value: ${colorName}`); // STEP B: Update the Product Type to include this new color as a possibility const productTypeAttr = await productTypesClient.getProductType({ productTypeId }); productTypeAttr.options?.find(opt => opt.attributeFQN === attributeFQN)?.vocabularyValues?.push(newVocabValue); await productTypesClient.updateProductType({ productTypeId, productType: productTypeAttr }); console.log(`Updated product type ${productTypeId} with new color`); // STEP C: Update the specific Product to include this new color as a selectable option const productOption = await productOptionsClient.getOption({ productCode, attributeFQN }); productOption.values?.push(newVocabValue); await productOptionsClient.updateOption({ productCode, attributeFQN, catalogAdminsProductOption: productOption }); console.log(`Added new color option to product ${productCode}`); // STEP D: Find and activate the newly created (but inactive) variations const existingVariations = await productVariationClient.getProductVariations({ productCode }); const colorMatchValue = colorName.replace(/\s/g, '-').toLowerCase(); if (existingVariations.items) { for (const variation of existingVariations.items) { const hasMatchingOption = variation.options?.some(o => o.attributeFQN === attributeFQN && o.value === colorMatchValue); // Activate only if it's new and matches our color if (hasMatchingOption && !variation.isActive) { variation.isActive = true; variation.variationExists = true; // Mark it as real // Generate a unique SKU for the new variation variation.variationProductCode = `${productCode}-${colorMatchValue}-${variation.options?.find(o=>o.attributeFQN?.includes('size'))?.value || 'sz'}`; } } } await productVariationClient.updateProductVariations({ productCode, productVariationPagedCollection: existingVariations }); console.log(`Activated new variations for color ${colorName} on product ${productCode}`); } catch (e) { console.error("Failed to add new color option:", JSON.stringify(e, null, 2)); }}// Example usage:// addNewColorOption("Kibo Yellow", "tenant~color", 6, "HikeJack_001");
import { Configuration } from "@kibocommerce/rest-sdk";import { ProductTypesApi, ProductType } from "@kibocommerce/rest-sdk/clients/CatalogAdministration";// Use the same configuration object from the previous exampleconst configuration = new Configuration({ // Your Tenant ID from Dev Center tenantId: process.env.KIBO_TENANT_ID, // Your Site ID from Dev Center siteId: process.env.KIBO_SITE_ID, // Your Application Key from Dev Center clientId: process.env.KIBO_CLIENT_ID, // Your Application Secret from Dev Center sharedSecret: process.env.KIBO_SHARED_SECRET, // The base auth URL for your region authHost: process.env.KIBO_AUTH_HOST });async function createClothingProductType(config: Configuration) { const productTypesClient = new ProductTypesApi(config); const clothingType: ProductType = { name: 'Apparel', attributes: [ { attributeFQN: 'admin@brand' }, // Assumes these attributes already exist { attributeFQN: 'tenant~color' }, { attributeFQN: 'tenant~size' }, ], }; try { console.log('Creating "Apparel" product type...'); const newProductType = await productTypesClient.addProductType({ productType: clothingType }); console.log('Successfully created product type:', newProductType); return newProductType; } catch (error) { console.error('Error creating product type:', JSON.stringify(error, null, 2)); }}
Example 2: Create a Base Product for Variations
import { Configuration } from "@kibocommerce/rest-sdk";import { ProductsApi, catalogAdminsProduct } from "@kibocommerce/rest-sdk/clients/CatalogAdministration";// Use the same configuration object from the previous exampleconst configuration = new Configuration({ // Your Tenant ID from Dev Center tenantId: process.env.KIBO_TENANT_ID, // Your Site ID from Dev Center siteId: process.env.KIBO_SITE_ID, // Your Application Key from Dev Center clientId: process.env.KIBO_CLIENT_ID, // Your Application Secret from Dev Center sharedSecret: process.env.KIBO_SHARED_SECRET, // The base auth URL for your region authHost: process.env.KIBO_AUTH_HOST });async function createBaseTshirt(config: Configuration, productTypeId: number) { const productsClient = new ProductsApi(config); const tshirt: CatalogAdminsProduct = { productCode: 'TSHIRT-01', productTypeId: productTypeId, // The ID of the "Apparel" type productUsage: 'Configurable', // This product is a container, not directly purchasable content: { productName: 'Basic Crewneck T-Shirt', productFullDescription: 'A comfortable and stylish t-shirt.', }, // Price and other details will be set on the variations }; try { const newProduct = await productsClient.addProduct({ catalogAdminsProduct: tshirt }); console.log('Successfully created base product:', newProduct); return newProduct; } catch (error) { console.error('Error creating product:', JSON.stringify(error, null, 2)); }}
Example 3: Create a Product Bundle
import { Configuration } from "@kibocommerce/rest-sdk";import { ProductsApi, CatalogAdminsProduct } from "@kibocommerce/rest-sdk/clients/CatalogAdministration";// Use the same configuration object from the previous exampleconst configuration = new Configuration({ // Your Tenant ID from Dev Center tenantId: process.env.KIBO_TENANT_ID, // Your Site ID from Dev Center siteId: process.env.KIBO_SITE_ID, // Your Application Key from Dev Center clientId: process.env.KIBO_CLIENT_ID, // Your Application Secret from Dev Center sharedSecret: process.env.KIBO_SHARED_SECRET, // The base auth URL for your region authHost: process.env.KIBO_AUTH_HOST });async function createStarterKitBundle(config: Configuration) { const productsClient = new ProductsApi(config); const bundle: CatalogAdminsProduct = { productCode: 'STARTER-KIT', productUsage: 'Bundle', // The key difference is the product usage type content: { productName: 'Hiking Starter Kit' }, price: { price: 150.00 }, // Bundles can have their own price bundledProducts: [ // List of products included in the bundle { productCode: 'HikeJack_001', quantity: 1 }, { productCode: 'HikeBoot_002', quantity: 1 }, { productCode: 'WaterBottle_003', quantity: 2 }, ] }; try { const newBundle = await productsClient.addProduct({ catalogAdminsProduct: bundle }); console.log('Successfully created bundle:', newBundle); return newBundle; } catch (error) { console.error('Error creating bundle:', JSON.stringify(error, null, 2)); }}
Example 4: Add a Product to a Category
import { Configuration } from "@kibocommerce/rest-sdk";import { CategoriesApi } from "@kibocommerce/rest-sdk/clients/CatalogAdministration";// Use the same configuration object from the previous exampleconst configuration = new Configuration({ // Your Tenant ID from Dev Center tenantId: process.env.KIBO_TENANT_ID, // Your Site ID from Dev Center siteId: process.env.KIBO_SITE_ID, // Your Application Key from Dev Center clientId: process.env.KIBO_CLIENT_ID, // Your Application Secret from Dev Center sharedSecret: process.env.KIBO_SHARED_SECRET, // The base auth URL for your region authHost: process.env.KIBO_AUTH_HOST });async function addProductToCategory(config: Configuration) { // Note: This operation is on the CategoriesApi in newer SDK versions. const categoriesClient = new CategoriesApi(config); const productCodes = ['TSHIRT-01']; const categoryId = 5; try { await categoriesClient.addProductsToCategory({ categoryId, requestBody: productCode }); console.log(`Successfully added product ${productCodes} to category ${categoryId}`); } catch (error) { console.error('Error adding product to category:', JSON.stringify(error, null, 2)); }}
Example 5: Create a Dynamic Category
import { Configuration } from "@kibocommerce/rest-sdk";import { CategoriesApi, CatalogAdminsCategory } from "@kibocommerce/rest-sdk/clients/CatalogAdministration";// Use the same configuration object from the previous exampleconst configuration = new Configuration({ // Your Tenant ID from Dev Center tenantId: process.env.KIBO_TENANT_ID, // Your Site ID from Dev Center siteId: process.env.KIBO_SITE_ID, // Your Application Key from Dev Center clientId: process.env.KIBO_CLIENT_ID, // Your Application Secret from Dev Center sharedSecret: process.env.KIBO_SHARED_SECRET, // The base auth URL for your region authHost: process.env.KIBO_AUTH_HOST });async function createOnSaleCategory(config: Configuration) { const categoriesClient = new CategoriesApi(config); const dynamicCategory: CatalogAdminsCategory = { content: { name: 'On Sale' }, categoryType: 'Dynamic', dynamicExpression: { // This Kibo filter expression finds all products where the price is less than 50 text: 'price lt 50' } }; try { const newCategory = await categoriesClient.addCategory({ CatalogAdminsCategory: dynamicCategory }); console.log('Successfully created dynamic category:', newCategory); return newCategory; } catch (error) { console.error('Error creating dynamic category:', JSON.stringify(error, null, 2)); }}
When a customer places an order, the Order.items array contains product details. The item.product.productCode directly links back to the unique identifier of a product or variation in your catalog.
// Actual error structure from Kibo API documentationinterface KiboApiError { errorCode: string; // Specific error codes from apidocs.kibocommerce.com message: string; // Error description correlationId: string; // For support tracking}
Common Error Codes for Catalog:
ATTRIBUTE_FQN_ALREADY_EXISTS: You tried to create an attribute with an attributeFQN that is already in use.
VALIDATION_ERROR: The request body is missing a required field or contains a value of the wrong data type.
PRODUCT_CODE_ALREADY_EXISTS: You tried to create a product with a productCode that already exists.
ITEM_NOT_FOUND: You referenced an item that doesn’t exist, such as a non-existent productTypeId.