Skip to main content

Kibo Location (Admin) API Developer Guide

Understanding Locations in Kibo

In Kibo, a Location is far more than just a physical address. It’s a fundamental operational entity that represents any place where inventory is stored, from which orders can be fulfilled, or where customers can pick up purchases. This could be a massive warehouse, a retail store, a third-party drop-shipper, or even a virtual holding location. What makes Kibo’s approach different is that each Location is defined by its capabilities. You explicitly declare what a location can do using fulfillmentTypes (e.g., DirectShip, InStorePickup) and what it is using locationTypes (e.g., Warehouse, Store). This rich data model allows Kibo’s advanced order routing and fulfillment logic to make intelligent decisions about how to process customer orders efficiently.

How This Domain Fits Into Kibo

The Location domain is the foundation of Kibo’s omnichannel commerce capabilities. It is linked to several other core domains:
  • Inventory: Inventory levels are not stored globally; they are tracked per Location. To know if a product is in stock, you must ask, “How many are in stock at the Austin warehouse?”
  • Fulfillment & Orders: When an order is placed, Kibo’s fulfillment engine consults the list of Locations. It checks their inventory and their fulfillmentTypes to determine the optimal location(s) from which to source the order’s shipments.
  • Returns: Locations can be designated as places where customers can return items purchased online.

Prerequisites

  • Kibo API credentials and basic setup
  • Node.js 16+ with TypeScript
  • Familiarity with REST APIs

What You’ll Learn

After completing this guide, you’ll understand:
  • How Kibo structures Location data, including its types and fulfillment capabilities (based on official API specs).
  • The key patterns for creating, updating, and managing your physical and virtual locations (verified from apidocs.kibocommerce.com).
  • Common workflows like onboarding a new warehouse or managing holiday shipping exceptions.
  • How to avoid the most common beginner mistakes, like performing a partial update incorrectly.
  • How to read and navigate the official Location Administration API documentation effectively.

Kibo Location Fundamentals

How Kibo Organizes Location Data

The system revolves around a few core data structures:
  • Location: The central object. It is uniquely identified by a code (a user-defined string like DAL-WAREHOUSE-01). It contains properties like name, address, phone, and, most importantly, arrays defining its capabilities:
    • fulfillmentTypes: An array of strings defining what the location can do. Key values verified from API docs include DirectShip (ship-to-home) and InStorePickup (BOPIS).
    • locationTypes: An array of strings defining what the location is. Key values include Store, Warehouse, and DropShipper.
  • CutoffTimeOverride: A subordinate object linked to a Location. It allows you to define exceptions to the location’s standard shipping cutoff times, which is essential for managing holidays or special events. It is identified by its own unique code.

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 (Client ID, Shared Secret, etc.). This object is then passed to the constructor of specific API clients (e.g., new LocationAdminApi(configuration)). The clients will automatically handle the OAuth 2.0 token exchange behind the scenes for every API call. Request/Response Structure: When you request a collection of locations, Kibo’s API provides a consistent, paginated response. The actual data is always inside the items array.
// Actual response schema for GET /commerce/admin/locations
{
  "startIndex": 0,
  "pageSize": 20,
  "pageCount": 2,
  "totalCount": 35,
  "items": [
    {
      "code": "AUS-01",
      "name": "Austin Downtown Store",
      "isActive": true,
      "fulfillmentTypes": [
        "DirectShip",
        "InStorePickup"
      ],
      "locationTypes": [
        "Store"
      ]
    }
  ]
}
Error Handling Approach: If an API call fails, the SDK will throw a structured error object. This helps you programmatically handle failures instead of just getting a generic HTTP status code.
// Actual error schema from Kibo
{
    "message": "Location with code 'AUS-01' already exists.",
    "errorCode": "LOCATION_ALREADY_EXISTS",
    "correlationId": "e0b5b9b012345abcdeffe0b5b9b012345abcdef"
}
API Documentation Reference: Throughout this guide, we’ll reference specific endpoints. Find complete specs at: /api-overviews/openapi_location_admin_overview

Common Location Workflows

Kibo developers typically work with Locations in these scenarios:
  1. Onboarding a New Fulfillment Center: Creating a new warehouse location and defining its shipping capabilities.
  2. Synchronizing Store Information: Running a scheduled job to update details like phone numbers or addresses for a network of retail stores from an external system of record.
  3. Managing Holiday Operations: Adding temporary overrides to shipping cutoff times for the peak holiday season to manage customer expectations.
Let’s explore each pattern step by step.

Add a New Location: The Kibo Way

When You Need This

This is the foundational step for expanding your fulfillment network. You need this whenever you open a new retail store, partner with a new warehouse, or enable a new drop-shipper.

API Documentation Reference

Endpoint: POST /commerce/admin/locations Method: POST API Docs: Add Location

Understanding the Kibo Approach

Kibo treats the code of a location as its permanent, unique business key. You define it once during creation, and it cannot be changed. This code is used throughout the platform to reference this location in inventory, fulfillment, and reporting APIs. The creation process requires you to be explicit about the location’s capabilities (fulfillmentTypes), ensuring it’s immediately ready to be integrated into the order processing workflow.

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 Location Administration API.
// 3. **Data Preparation**: Construct the full request body object for the new Location according to the API schema.
// 4. **API Call**: Use the instantiated client to call the `addLocation` method.

Step-by-Step Implementation

Step 1: Setting Up the Foundation
// Essential imports for Location operations.
// The SDK is organized by API groups; 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 { LocationAdminApi } from "@kibocommerce/rest-sdk/clients/LocationAdmin";
import { Location } from "@kibocommerce/rest-sdk/clients/LocationAdmin/models";

// 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: Understanding the Data Flow Our application will send a complete Location JSON object to the Kibo API. The API validates that the code is unique and the payload is correctly structured. If successful, it creates the location and returns the full Location object, including any server-generated values. Step 3: The Core Implementation
// Complete working example that ACTUALLY WORKS with the Kibo API
// This function adds a new retail store that can also ship orders.

async function addNewRetailStore(storeDetails: Location): Promise<Location> {
    console.log(`Adding new location with code: ${storeDetails.code}...`);

    // 1. Instantiate a dedicated client for the Location Administration API.
    const locationAdminClient = new LocationAdminApi(configuration);

    // 2. The storeDetails object is our request payload.
    //    It must match the schema defined in the Kibo API documentation.

    // 3. Call the method on the client. The method name `addLocation` corresponds to the API operation.
    try {
        const newLocation = await locationAdminClient.addLocation({
            location: storeDetails,
        });
        console.log(`Success! Location "${newLocation.name}" created.`);
        return newLocation;
    } catch (error) {
        // Handle common errors, like the location code already existing.
        console.error("API Error adding location:", JSON.stringify(error, null, 2));
        throw error;
    }
}

// Example usage:
// const newStorePayload: Location = {
//     code: "ATX-Lamar",
//     name: "Austin South Lamar Store",
//     description: "Our newest retail location in Austin.",
//     isActive: true,
//     fulfillmentTypes: [
//         { code: "DS", name: "Direct Ship" }, // Verified from API schema, this is an object
//         { code: "SP", name: "In Store Pickup" }
//     ],
//     locationTypes: [{ code: "ST", name: "Store" }],
//     address: {
//         address1: "123 S Lamar Blvd",
//         cityOrTown: "Austin",
//         stateOrProvince: "TX",
//         postalOrZipCode: "78704",
//         countryCode: "US"
//     },
//     phone: "512-555-1234"
// };
// addNewRetailStore(newStorePayload);

What Just Happened? (Code Explanation)

  • The setup phase created the standard Configuration object for authentication.
  • The API call was made using an instance of LocationAdminApi. This client provides type-safe methods for all location management operations.
  • The payload we prepared was a complete Location object. We explicitly defined its unique code and its fulfillmentTypes, making it immediately available to Kibo’s fulfillment engine.
  • The response was the newly created Location object, confirming its creation.

Common Beginner Mistakes

Mistake 1: Trying to update a location’s code. The code is immutable. Once a location is created with code: "STORE-A", you cannot change it. To “rename” it, you would need to deactivate the old location and create a new one. Mistake 2: Using PUT for a partial update and accidentally deleting data. The updateLocation endpoint (PUT) performs a full replacement of the object. If you only send { "phone": "new-number" }, you will wipe out the address, fulfillment types, and everything else. The correct pattern is to GET the location, modify the object, then PUT the entire modified object back.

Advanced Patterns & Multiple Examples

Pattern 1: Idempotent Location Synchronization

Business Scenario: You need a script that runs daily to sync location data from an external “source of truth” into Kibo. The script must correctly create new locations and update existing ones without creating duplicates. Kibo’s Architecture Consideration: The API does not have a single “upsert” endpoint. The correct, idempotent pattern is to first try to GET the location by its unique code. If you get a 404 Not Found error, you know it doesn’t exist and you should call POST to create it. If the GET call succeeds, you know it exists and you should call PUT to update it. API Endpoints Used:
  • GET /commerce/admin/locations/{locationCode}
  • PUT /commerce/admin/locations/{locationCode}
  • POST /commerce/admin/locations
Implementation Strategy (Example 1: The Sync Function):
// Advanced example: A function to synchronize a single location's data.
async function syncLocation(locationData: Location) {
    const locationAdminClient = new LocationAdminApi(configuration);
    const locationCode = locationData.code as string;

    try {
        // 1. Try to GET the location first.
        const existingLocation = await locationAdminClient.getLocation({ locationCode });
        console.log(`Location ${locationCode} exists. Updating...`);

        // 2. If it exists, PUT the new data.
        // Important: We send the full locationData object to replace the existing one.
        await locationAdminClient.updateLocation({ locationCode, location: locationData });
        console.log(`Location ${locationCode} updated successfully.`);

    } catch (error: any) {
        // 3. If GET fails with a 404, we know we need to create it.
        if (error.status === 404) {
            console.log(`Location ${locationCode} does not exist. Creating...`);
            await locationAdminClient.addLocation({ location: locationData });
            console.log(`Location ${locationCode} created successfully.`);
        } else {
            // Re-throw any other unexpected errors.
            console.error(`An unexpected error occurred for location ${locationCode}:`, error);
            throw error;
        }
    }
}

Example 2: Get a Specific Location

// A simple function to retrieve a single location by its code.
async function getLocationByCode(locationCode: string): Promise<Location | null> {
    const locationAdminClient = new LocationAdminApi(configuration);
    try {
        console.log(`Fetching details for location: ${locationCode}`);
        const location = await locationAdminClient.getLocation({ locationCode });
        console.log("Found location:", location.name);
        return location;
    } catch (error: any) {
        if (error.status === 404) {
            console.log(`Location with code ${locationCode} not found.`);
            return null;
        }
        throw error;
    }
}

Example 3: Add a Holiday Cutoff Time Override

// Adds a special shipping cutoff time for a specific date.
async function addHolidayCutoff(id: string,locationCode: string, date: string, startTime: string, endTime:string) {
    const locationAdminClient = new LocationAdminApi(configuration);
    console.log(`Adding cutoff override for ${date} at ${locationCode}`);
    try {
        await locationAdminClient.createCutoffTimeOverride({
            cutoffTimeOverride: {
                id: id,
                locationCode,
                date, // Format: "YYYY-MM-DD"
                startTime: startTime, // Format: "HH:MM:SS"
                endTime: endTime, // Format: "HH:MM:SS"
            }
        });
        console.log("Cutoff override added successfully.");
    } catch (error) {
        console.error("Failed to add cutoff override:", JSON.stringify(error, null, 2));
        throw error;
    }
}
// Usage: addHolidayCutoff("override-1","ATX-Lamar", "2025-12-23", "14:00:00", "XMAS-EVE-2025");

Example 4: Get All Cutoff Overrides for a Location

// Retrieves all configured shipping cutoff exceptions for a location.
async function listCutoffOverrides(locationCode: string) {
    const locationAdminClient = new LocationAdminApi(configuration);
    try {
        const overrides = await locationAdminClient.getCutoffTimeOverrides({ locationCode });
        console.log(`Found ${overrides.items?.length} cutoff overrides for ${locationCode}:`, overrides);
        return overrides;
    } catch (error) {
        console.error("Failed to get cutoff overrides:", JSON.stringify(error, null, 2));
        throw error;
    }
}

Example 5: Delete a Cutoff Time Override

// Removes a specific shipping cutoff exception.
async function deleteCutoffOverride(locationCode: string, overrideId: string) {
    const locationAdminClient = new LocationAdminApi(configuration);
    console.log(`Deleting override ${overrideId} from location ${locationCode}`);
    try {
        // This call returns a 204 No Content on success.
        await locationAdminClient.deleteCutoffTimeOverride({ overrideId});
        console.log("Override deleted successfully.");
    } catch (error) {
        console.error("Failed to delete cutoff override:", JSON.stringify(error, null, 2));
        throw error;
    }
}

Integrating Location with Other Kibo Domains

Location + Inventory Integration

Inventory is always location-specific. You cannot ask Kibo for a product’s overall stock level. You must ask for its stock level at a particular location. Any API call to update or retrieve inventory will require a locationCode. This direct link is what enables features like ship-from-store and in-store pickup.

Location + Fulfillment Integration

The fulfillment process is a direct consumer of Location data. When an order needs to be shipped, the fulfillment engine queries for active locations with the DirectShip fulfillment type and available inventory for the ordered items. The location’s address is used to calculate shipping rates, and its cutoff times determine the expected ship date.

Troubleshooting Your Location Implementation

Reading Kibo Error Messages

interface KiboApiError {
  errorCode: string;      // Specific error codes from apidocs.kibocommerce.com
  message: string;          // Error description
  correlationId: string;  // For support tracking
}
Common Error Codes for Location:
  • LOCATION_ALREADY_EXISTS: You tried to call addLocation with a code that is already in use.
  • LOCATION_NOT_FOUND: The locationCode you provided in a URL (e.g., for getLocation or updateLocation) does not exist.
  • VALIDATION_ERROR: The request body is malformed. A common cause is providing an invalid countryCode or malformed fulfillmentTypes object.

Common Development Issues

Issue 1: My location was created, but it never gets assigned any shipments.
  • Why it happens: This is almost always because the fulfillmentTypes array is either empty or does not contain the necessary capability, like DirectShip. If a location cannot ship orders, the fulfillment engine will ignore it.
  • How to fix it: GET the location, add the correct fulfillment type object(s) to the array (e.g., { code: "DS", name: "Direct Ship" }), and then PUT the entire updated location object back.
  • API Reference: /api-reference/locationadmin/update-location
Issue 2: I tried to update a location’s name, and now its address and phone number are gone.
  • Why it happens: The PUT /commerce/admin/locations/{locationCode} endpoint performs a complete replacement of the location object. If your request body only contains {"name": "New Name"}, you are telling Kibo to replace the entire existing object with that, effectively deleting all other fields.
  • How to fix it: This is a standard REST pattern. You must always GET the full location object first, modify the properties you want to change on that object in your code, and then PUT the entire modified object back in the request body. See the syncLocation advanced example for the correct implementation.