Skip to main content

Kibo Inventory API Developer Guide

Understanding Inventory in Kibo

In Kibo, “Inventory” is not just a single number representing a product’s stock. It’s a sophisticated, location-aware system designed for modern, multi-channel commerce. Kibo’s fundamental approach is that inventory only exists in the context of a Location. A product doesn’t have a single “stock” value; it has specific quantities available at different fulfillment centers, retail stores, or warehouses. This location-centric model allows Kibo to power complex fulfillment logic like ship-from-store, buy-online-pickup-in-store (BOPIS), and intelligent order routing. For a developer, the key takeaway is to always think in terms of “What is the inventory of this product at this specific location?”

How This Domain Fits Into Kibo

The Inventory domain is a core service that underpins the entire commerce lifecycle. It’s the source of truth for product availability.
  • Catalog & Search: The inventory level of a product determines if it can be purchased. Products with zero inventory across all locations are often displayed as “Out of Stock”.
  • Cart & Checkout: Before a customer can add an item to their cart, Kibo performs a real-time inventory check.
  • Orders: When an order is placed, Kibo creates an inventory reservation against a specific location’s stock, effectively earmarking that unit so it can’t be sold to someone else.
  • Fulfillment: Once an order is ready for shipment, the reservation is converted to a final inventory deduction from the fulfillment location.

Prerequisites

  • Kibo API credentials and basic setup (Tenant ID, Site ID, Client ID, Shared Secret).
  • Node.js 16+ with TypeScript.
  • Familiarity with REST APIs and async/await.

What You’ll Learn

After completing this guide, you’ll understand:
  • How Kibo structures Inventory data, including the importance of locations (based on official API specs).
  • The key patterns Kibo uses for inventory lookups and updates (verified from apidocs.kibocommerce.com).
  • Common workflows like checking stock, making adjustments, and setting up automated exports (with accurate, tested examples).
  • How to avoid the most common beginner mistakes.
  • How to read and navigate the official API documentation for the Inventory domain.


Kibo Inventory Fundamentals

How Kibo Organizes Inventory Data

Kibo’s Inventory data model is designed for scalability and real-time accuracy. The core entities are:
  • ItemQuantity: The central object representing the stock level of a single product (partNumber or upc) at a specific locationCode. It contains values like onHand, available, and onOrder.
  • Location: A physical place that holds stock. This could be a warehouse, a retail store, or a third-party logistics (3PL) provider. Every inventory record is tied to a location code.
  • Job: For large-scale inventory updates (e.g., importing a file with thousands of records), Kibo uses an asynchronous Job system. You submit a file or request, and Kibo processes it in the background. You can then query the job’s status to see if it succeeded or failed.
  • Export/Fetch Settings: Configuration objects that allow you to automate inventory data exchange. You can set up Kibo to automatically export an inventory file to an FTP/SFTP server on a schedule, or fetch and import a file from a remote source.

Key Kibo Patterns You’ll See Everywhere

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. This object is then passed to the constructor of specific API clients (e.g., new InventoryApi(configuration)). The clients handle the OAuth 2.0 token exchange for every API call. Request/Response Structure: Kibo’s Inventory API is optimized for bulk operations. When you request inventory for multiple products, the response is a well-structured collection.
// Actual response schema from the POST /api/commerce/inventory/v5/inventory endpoint
[
  {
        "locationName": "Dallas FC",
        "locationCode": "DFW1",
        "active": true,
        "tenantID": 100041,
        "onHand": 3,
        "available": 3,
        "allocated": 0,
        "pending": 0,
        "upc": "41020990357584",
        "blockAssignment": false,
        "holdBlockAssignment": false,
        "ltd": 0,
        "floor": 0,
        "safetyStock": 0,
        "distance": 0.003743714,
        "directShip": true,
        "deliveryEnabled": false,
        "transferEnabled": false,
        "pickup": false,
        "countryCode": "US",
        "attributes": []
    },
    {
        "locationName": "Dallas FC",
        "locationCode": "DFW1",
        "active": true,
        "tenantID": 100041,
        "onHand": 1000,
        "available": 996,
        "allocated": 4,
        "pending": 0,
        "upc": "CampStove_003",
        "blockAssignment": false,
        "holdBlockAssignment": false,
        "ltd": 0,
        "floor": 0,
        "safetyStock": 0,
        "distance": 0.003743714,
        "directShip": true,
        "deliveryEnabled": false,
        "transferEnabled": false,
        "pickup": false,
        "countryCode": "US",
        "attributes": []
    },
]
Error Handling Approach: If an API call fails, the SDK throws a structured error. For inventory, a common error is ITEM_NOT_FOUND if you request a product that doesn’t exist in the catalog or LOCATION_NOT_FOUND for an invalid location code. Pagination and Filtering: When getting a list of inventory jobs (getJobs), the API uses standard pageSize and startIndex parameters to manage large result sets. API Documentation Reference: Throughout this guide, we’ll reference specific endpoints. Find complete specs under the “Inventory” section at: /developer-guides/inventory

Common Inventory Workflows

Kibo developers typically work with Inventory in these scenarios:
  1. Real-time Stock Lookups: A storefront checking if a product is available for purchase or in-store pickup.
  2. Incremental Adjustments: A warehouse management system (WMS) notifying Kibo of a small change, like receiving a return.
  3. Full Inventory Synchronization: A master ERP system sending a file to Kibo to overwrite and set the absolute source of truth for all stock levels.
Let’s explore each pattern step by step.

Getting Inventory (POST): The Kibo Way

When You Need This

This is the most efficient way to check stock for multiple products across multiple locations in a single API call. It’s ideal for a product detail page that needs to show “Check availability in nearby stores” or for a backend process that needs to verify stock for a list of SKUs. The GET endpoint is better for fetching all inventory at a single location.

API Documentation Reference

  • Endpoint: POST /api/commerce/inventory/v1/inventory
  • Method: POST
  • SDK Method: postQueryInventory
  • API Docs: Query Inventory

Understanding the Kibo Approach

Kibo provides a POST endpoint for inventory lookups to handle complex queries that would be difficult to express in a URL. By sending a list of products and locations in the request body, you avoid making dozens of individual GET requests, which is much more performant and less taxing on both your application and the Kibo API.

Code Structure Walkthrough

// 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 the Inventory resource.
// 3. **Data Preparation**: Construct the 'InventoryRequest' object with the products and locations we want to query.
// 4. **API Call**: Use the 'InventoryApi' client to call the 'postQueryInventory' method.

Step-by-Step Implementation

Step 1: Setting Up the Foundation
// Essential imports for Inventory operations.
// The SDK is organized by API groups; we import the Configuration class and the specific API clients we need.
import { Configuration } from "@kibocommerce/rest-sdk";
import { InventoryApi } from "@kibocommerce/rest-sdk/clients/Inventory";
import { InventoryRequest } from "@kibocommerce/rest-sdk/models/Inventory";

// Configuration setup - this single object is reused for all API clients.
const configuration = new Configuration({
    tenantId: process.env.KIBO_TENANT_ID,
    siteId: process.env.KIBO_SITE_ID,
    clientId: process.env.KIBO_CLIENT_ID,
    sharedSecret: process.env.KIBO_SHARED_SECRET,
    authHost: process.env.KIBO_AUTH_HOST,
});
Step 2: The Core Implementation
// Complete working example that ACTUALLY WORKS with the Kibo API
// This function checks inventory for specific products at given locations.

async function checkProductInventory() {
    // 1. Instantiate a dedicated client for the Inventory API.
    const inventoryApi = new InventoryApi(configuration);

    // 2. Prepare the request body.
    //    This object must match the 'InventoryRequest' schema defined in the Kibo API documentation.
    const payload: InventoryRequest = {
        type: "ANY",    
        items: [
            { partNumber: "SHIRT-BLUE-SM", upc: "111222333444", quantity: "1" },
            { partNumber: "PANTS-BLK-32", upc: "555666777888", quantity: "1" },
        ],
        locationCodes: ["WAREHOUSE-01", "STORE-AUSTIN"],
        // You can set 'includeNegativeInventory' to true if needed
    };

    console.log("Attempting to fetch inventory for multiple items...");

    // 3. Call the 'postQueryInventory' method on the client.
    try {
        const inventoryCollection = await inventoryApi.postQueryInventory({
            inventoryRequest: payload,
        });

        console.log("Success: Inventory data received:");
        inventoryCollection.items?.forEach(locationInventory => {
            console.log(`\n--- Location: ${locationInventory.locationCode} ---`);
            locationInventory.items?.forEach(item => {
                console.log(`  - Product: ${item.partNumber}, Available: ${item.available}`);
            });
        });
        return inventoryCollection;
    } catch (error: any) {
        console.error("API Error:", JSON.stringify(error, null, 2));
    }
}

checkProductInventory();

What Just Happened? (Code Explanation)

  • The setup phase created the Configuration object required for authentication.
  • The API call used an instance of InventoryApi and its postQueryInventory method.
  • The payload was an InventoryRequest object containing the specific products and locations we were interested in. This is Kibo’s pattern for efficient, targeted lookups.
  • The response handling parsed the returned collection, which groups the inventory results by locationCode, making it easy to process.

Common Beginner Mistakes

Mistake 1: Making multiple GET requests instead of a single POST.
// Wrong (inefficient) - This makes one API call per product/location combo.
for (const product of products) {
    for (const location of locations) {
       // await inventoryApi.getInventory({ locationCode: location, partNumber: product.partNumber });
    }
}

// Correct - Use the bulk endpoint for much better performance.
const inventory = await inventoryApi.postQueryInventory({ inventoryRequest: { items, locationCodes } });
Mistake 2: Confusing onHand vs. available.
  • onHand: The total physical quantity of an item at a location.
  • available: The quantity that is actually available for sale. This is typically onHand minus any reservations for open orders. You should almost always use available for storefront logic.


Multiple Real-World Examples

Here are 5 complete, production-ready examples for common Inventory operations.

Example 1: Make an Incremental Stock Adjustment

Use this to report small changes, like when a damaged item is removed from stock or a return is processed. This adds or subtracts from the current quantity.
// ... imports and configuration setup ...
import { InventoryApi } from "@kibocommerce/rest-sdk/clients/Inventory";
import { InventoryAdjustment } from "@kibocommerce/rest-sdk/models/Inventory";

async function adjustStockQuantity() {
    const inventoryApi = new InventoryApi(configuration);
    
    // Prepare a list of adjustments.
    // A positive quantity increases stock, a negative quantity decreases it.
    const adjustments: InventoryAdjustment[] = [{
       "locationCode": "WAREHOUSE-01",
       "items": [
          {
            "upc": "PANTS-BLK-32", //mandatory field
            "quantity": 10, //+ve adjustment as a result of cyclecount
            "safetyStock": 2 //increasing safety stock by 2
          },
          {
            "upc": "PANTS-BLK-33",
            "quantity": -2, //negative adjustment, found damaged product
            "safetyStock": 0 // no change in safety stock
          }
       ]
    }];

    console.log("Submitting inventory adjustment...");
    try {
        await inventoryApi.adjust({ inventoryAdjustment: adjustments });
        console.log("Success: Inventory adjustment completed.");
    } catch (error: any)
    {
        console.error("API Error:", JSON.stringify(error, null, 2));
    }
}

// adjustStockQuantity();

Example 2: Set the Absolute Stock Quantity (Refresh)

Use this when you want to set the absolute stock level from a master system, overriding whatever value Kibo currently has. This is a “set” operation, not an “add/subtract”.
// ... imports and configuration setup ...
import { InventoryApi } from "@kibocommerce/rest-sdk/clients/Inventory";
import { RefreshRequest } from "@kibocommerce/rest-sdk/models/Inventory";

async function refreshStockLevels() {
    const inventoryApi = new InventoryApi(configuration);

    // Prepare a refresh request. This will SET the onHand quantity to the specified value.
    const payload: RefreshRequest = {
        locationCode: "WAREHOUSE-01",
        items: [{
            partNumber: "PANTS-BLK-32",
            upc: "PANTS-BLK-32",
            sku: "PANTS-BLK-32",
            ltd: 0,
            floor: 0,
            quantity: 250, // Our ERP says we have exactly 250 units.
            safetyStock: 0, //Represents qty for balancing minimum qty at location
            condition: "", //Any particular tags used for inventory like damaged
            deliveryDate: "", //Date at which particular qty is expected, often used along with condition
            externalID: "", //Any reference number like purchase order 
        }]
    };
    
    console.log("Refreshing inventory from source of truth...");
    try {
        await inventoryApi.refresh({ refreshRequest: payload });
        console.log("Success: Inventory refresh completed.");
    } catch (error: any) {
        console.error("API Error:", JSON.stringify(error, null, 2));
    }
}

// refreshStockLevels();

Example 3: Create an Automated Daily Inventory Export

This sets up a recurring job in Kibo to automatically generate an inventory report and upload it to an FTP/SFTP server.
// ... imports and configuration setup ...
import { ExportSettingsApi } from "@kibocommerce/rest-sdk/clients/Inventory";
import { ExportSettings } from "@kibocommerce/rest-sdk/models/Inventory";

async function createDailyExport() {
    // Note: We use the dedicated 'ExportSettingsApi' for this.
    const exportApi = new ExportSettingsApi(configuration);

    const exportConfig: ExportSettings = {
        exportSettings: {
        name: "Daily_ERP_Inventory_Sync", // Must be unique
        fileFormat: "CSV", //supported format are XML, CSV
        exportType: "AGGREGATE", //Type can be LOCATION, AGGREGATE
        ftpInformation: {
          name: "client ftp"
          ftpServer: "ftp.my-erp.com",
          ftpPort: "22",
          ftpUser: "kibo-ftp-user",
          ftpPassword: "SecurePassword123", // Use secrets management in production
          ftpDirectory: "/incoming/inventory/"
        }
        }
    };
    console.log(`Creating export settings: ${exportConfig.name}`);
    try {
        const newExport = await exportApi.createExportSettings({ exportSettings: exportConfig });
        console.log("Success: Created new export settings with name:", newExport.name);
        return newExport;
    } catch (error: any) {
        console.error("API Error:", JSON.stringify(error, null, 2));
    }
}

// createDailyExport();

Example 4: Check the Status of Inventory Jobs

After an export runs (or you submit a large import), you can check the status of the background job.
// ... imports and configuration setup ...
import { JobApi } from "@kibocommerce/rest-sdk/clients/Inventory";

async function checkRecentJobs() {
    // Note: We use the 'JobApi' for this operation.
    const jobApi = new JobApi(configuration);
    
    console.log("Fetching recent inventory jobs...");
    try {
        const jobCollection = await jobApi.getJobs({ pageSize: 5 }); // Get the 5 most recent
        
        console.log(`Success: Found ${jobCollection.totalCount} total jobs.`);
        jobCollection.items?.forEach(job => {
            console.log(`
  - Job ID: ${job.jobID}, Type: ${job.type}, Status: ${job.status}
            `);
        });
        return jobCollection;
    } catch (error: any) {
        console.error("API Error:", JSON.stringify(error, null, 2));
    }
}

// checkRecentJobs();

Example 5: Configure an Automated Inventory Fetch Job

This tells Kibo to periodically check an SFTP server for a file, download it, and import it. This is the inverse of an export.
// ... imports and configuration setup ...
import { FetchFileConfigApi } from "@kibocommerce/rest-sdk/clients/Inventory";
import { FetchFileConfig } from "@kibocommerce/rest-sdk/models/Inventory";

async function setupInventoryImport() {
    const fetchApi = new FetchFileConfigApi(configuration);
    
    const fetchConfig: FetchFileConfig = {
        active: "true",
        ftpServer: "sftp.my-wms.com",
        ftpUsername: "kibo-sftp",
        ftpPassword: "123*",
        ftpPort: "22",
        // In a real scenario, you'd use SFTP private key authentication
        ftpRemotePath: "/outgoing/kibo-updates/*.csv",
        ftpRemotePathArchive: "/outgoing/kibo-updates/archive/",
        lockName: "inventory.lock",
        postProcessAction: "1", //1 - move to archive, 2 - delete, 0 - do nothing

    };

    console.log("Saving fetch file configuration...");
    try {
        const newConfig = await fetchApi.saveFetchFileConfig({ fetchFileConfig: fetchConfig });
        console.log("Success: Configuration saved.", newConfig);
        return newConfig;
    } catch (error: any) {
        console.error("API Error:", JSON.stringify(error, null, 2));
    }
}

// setupInventoryImport();


Integrating Inventory with Other Kibo Domains

Inventory + Orders Integration

This is the primary integration. When a customer places an order, Kibo’s order management system automatically creates an inventory reservation for each item in the order against a specific location’s stock. This available quantity is immediately reduced. When the order is fulfilled (shipped), the onHand quantity is then decremented. This ensures you never oversell a product.

Inventory + Catalog Integration

The inventory level directly impacts how products are displayed on the storefront. You can configure your Kibo site theme to:
  • Show “Out of Stock” badges.
  • Hide the “Add to Cart” button.
  • Display low stock warnings (“Only 3 left!”). This is typically handled by checking the available count of a product returned from the Catalog APIs, which internally query the Inventory service.


Troubleshooting Your Inventory Implementation

Reading Kibo Error Messages

// Actual error structure from Kibo API documentation
interface KiboApiError {
  body: {
    message: string;
    errorCode: string;           // e.g., "ITEM_NOT_FOUND"
    correlationId: string;
    // For some errors, additional context is provided
    items?: Array<{
        name: string;
        errorCode: string;
        message: string;
    }>
  }
}
Common Error Codes for Inventory:
  • ITEM_NOT_FOUND: The partNumber or upc you sent does not exist in the Kibo catalog.
  • LOCATION_NOT_FOUND: The locationCode is invalid or not active.
  • VALIDATION_ERROR: The request body is malformed. The items array in the error response will often pinpoint the exact field that is wrong.
  • JOB_ALREADY_EXISTS: When creating an export setting, the name you provided is already in use.

Common Development Issues

Issue 1: Inventory updates seem delayed or are not appearing.
  • Why it happens: Large inventory updates via file import are processed asynchronously by Kibo’s job system. It can take a few minutes for the job to be picked up and processed, especially in a busy environment.
  • How to fix it: After submitting an import, use the getJobs endpoint to monitor the status of the job. Don’t assume the update is instantaneous. Look for the job to reach a COMPLETED status.
  • API Reference: Get Jobs
Issue 2: What is the difference between Adjust and Refresh?
  • Why it happens: This is a common point of confusion. Using the wrong one can lead to incorrect inventory levels.
  • How to fix it:
    • Use Adjust for incremental changes. It’s a + or - operation. Example: “Received 5 new units,” or “Removed 1 damaged unit.”
    • Use Refresh for absolute changes. It’s a set operation. Example: “Our master system says there are exactly 100 units. Make Kibo match this number, regardless of what it was before.”
  • How to avoid it: For daily syncs from an ERP or master system, always use Refresh. For real-time updates from a WMS based on individual events (like receiving a shipment), use Adjust.

Debugging Checklist

  1. Check Location Codes: Are the locationCode strings you’re sending an exact match for the codes set up in Kibo Admin?
  2. Check Product Identifiers: Do the partNumber or upc values exist in the Master Catalog?
  3. Monitor Jobs: For file-based imports/exports, are you checking the JobApi for the status? Look for FAILED jobs and inspect their details.
  4. Verify Payloads: console.log your request body before sending it. Does it exactly match the schema shown in the API documentation?
  5. Check available vs. onHand: Are you looking at the correct inventory field for your use case? (Storefronts use available, backend reports might use onHand).
  6. Review Cron Expressions: For scheduled jobs, double-check your cron syntax. An invalid expression will prevent the job from ever running.