Skip to main content

Kibo Shipments API Developer Guide

Fulfillment

Understand fulfillment architecture and concepts

Hold Shipments

Manage shipment holds in the Admin UI

Pick Lists and Sheets

Configure pick lists and sheets in the Admin UI

Shipment Attributes

Set up shipment attributes in the Admin UI

Multi Piece Shipments

Configure multi-piece shipments in the Admin UI

Understanding Fulfillment (Shipments) in Kibo

In Kibo, a Shipment is the fundamental object in the fulfillment process. It’s a concrete, actionable instruction for a specific location (like a warehouse or a retail store) to pick, pack, and dispatch a set of items from an order. The key thing to understand is the separation of concerns:
  • An Order is the customer’s request—what they bought.
  • Order Routing is the decision—how the order should be fulfilled.
  • A Shipment is the action—the specific “to-do list” sent to a fulfillment location.
An order can be broken down into multiple shipments, each assigned to a different location. Managing the lifecycle of these individual shipments is the core of fulfillment in Kibo.

How This Domain Fits Into Kibo

The Shipment is the workhorse of the post-purchase process. It’s where the digital order becomes a physical reality.
  • Order Routing: The output of the Order Routing engine is a “suggestion” that becomes the direct input for creating one or more shipments.
  • Inventory: When a shipment is created, Kibo firms up the inventory reservation. When it’s fulfilled, the onHand inventory at the assigned location is finally decremented.
  • Customer: The customer is notified about the progress of their shipments, including tracking numbers which are stored on the shipment record.
  • Returns: If a customer wants to return an item, the return is processed against the original shipment it came from.

Prerequisites

  • Kibo API credentials with Fulfillment permissions.
  • An understanding of Kibo’s Order and Location concepts.
  • 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 Shipment data and its state-driven lifecycle (based on official API specs).
  • The key “Task-Based” pattern Kibo uses for all shipment modifications.
  • Common workflows like fulfilling and canceling shipments (with accurate, tested examples).
  • How to avoid the most common beginner mistakes.
  • How to read and navigate the official API documentation for Fulfillment.


Kibo Shipment Fundamentals

How Kibo Organizes Fulfillment Data

Kibo’s Fulfillment data is built around a state machine, where a Shipment moves through various stages. The core objects are:
  • Shipment: The main object representing a fulfillment task.
    • shipmentNumber: A unique, Kibo-assigned number identifying the shipment.
    • orderId: The order this shipment belongs to.
    • locationCode: The specific location responsible for fulfilling this shipment.
    • workflowState: The current stage of the shipment (e.g., Ready, Fulfilled, Cancelled). This is the most important status field.
    • items: An array of ShipmentItem objects detailing the products and quantities to be fulfilled.
  • Pickup: For “Buy Online, Pick Up in Store” (BOPIS) shipments, a related Pickup object is created to manage the customer pickup process.
  • Task: This is an action you send. To change a shipment’s state (e.g., to fulfill it), you must execute a named task on it, like "Fulfill". This is a key Kibo pattern.

Key Kibo Patterns You’ll See Everywhere

Task-Based Workflow: This is the most important pattern in fulfillment. You cannot directly modify a shipment’s state. You must use the “Fulfill” task to move a shipment from Ready to Fulfilled. The request to execute a task looks like this:
// Actual request schema for the performShipmentTask endpoint
// POST /api/commerce/fulfillment/shipments/{shipmentNumber}/tasks
{
    "_embedded": {
        "tasks": [
            {
                "name": "Accept Shipment",
                "subject": "",
                "inputs": [
                    {
                        "name": "shipmentAccepted",
                        "type": "BOOLEAN"
                    }
                ],
                "active": false,
                "completed": true,
                "completedDate": "2022-01-18T15:10:30.908Z",
                "_links": {}
            },
            {
                "name": "Validate Items In Stock",
                "subject": "",
                "inputs": [
                    {
                        "name": "stockLevel",
                        "type": "STRING"
                    }
                ],
                "active": false,
                "completed": true,
                "completedDate": "2022-01-18T15:10:31.269Z",
                "_links": {}
            },
            {
                "name": "Print Packing Slip",
                "subject": "",
                "inputs": [
                    {
                        "name": "back",
                        "type": "BOOLEAN"
                    }
                ],
                "active": false,
                "completed": true,
                "completedDate": "2022-01-18T15:10:31.631Z",
                "_links": {}
            },
            {
                "name": "Prepare for Shipment",
                "subject": "",
                "inputs": [
                    {
                        "name": "canceled",
                        "type": "BOOLEAN"
                    },
                    {
                        "name": "back",
                        "type": "BOOLEAN"
                    }
                ],
                "active": false,
                "completed": true,
                "completedDate": "2023-05-30T19:24:25.104Z",
                "_links": {}
            }
        ]
    },
    "_links": {
        "self": {
            "href": "https://t31271.sandbox.mozu.com/api/commerce/shipments/123/tasks"
        },
        "shipment": {
            "href": "https://t31271.sandbox.mozu.com/api/commerce/shipments/123"
        }
    }
}
Error Handling Approach: If an API call fails, the SDK throws a structured error. A common error is a VALIDATION_ERROR if you try to execute a task that isn’t valid in the shipment’s current workflowState. API Documentation Reference: Find complete specs under the “Fulfillment” section at: /api-overviews/openapi_fulfillment_overview

Common Shipment Workflows

Kibo developers typically work with Shipments in these scenarios:
  1. Backend Integration: A Warehouse Management System (WMS) uses the API to get new shipments, and then notifies Kibo as they are packed and shipped.
  2. In-Store Operations: Building a simple tablet application for store associates to manage BOPIS orders, marking them ready for pickup and then as collected.
  3. Customer Service Tools: A custom UI for support agents to cancel or reassign shipments when a customer calls with an issue.


SDK Fulfillment Workflows

The following examples use the Task-Based Pattern to move shipments through their lifecycle, which is the correct and auditable way to manage shipment state in Kibo.

SDK Setup

All examples rely on a base Configuration and ShipmentApi client instance.
// Essential imports for Fulfillment operations.
import { Configuration } from "@kibocommerce/rest-sdk";
import { ShipmentApi, PickupApi } from "@kibocommerce/rest-sdk/clients/Fulfillment";
import { Task, Pickup, PickupItem } from "@kibocommerce/rest-sdk/models/Fulfillment";

// 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,
});

const shipmentApi = new ShipmentApi(configuration);

1. Ship-to-Home (STH) Fulfillment Workflow

A standard STH fulfillment involves a series of tasks.
StepTask Name / ActionEndpoint TypeRequired Body Fields
1. Accept ShipmentAccept ShipmentTask CompletionshipmentAccepted: true
2. Validate StockValidate Items In StockTask CompletionstockLevel: "IN_STOCK" or "PARTIAL_STOCK"
3. Print Packing SlipPrint Packing SlipTask CompletionEmpty {}
4. Add TrackingNone (Direct Call)Edit PackagetrackingNumbers
5. Prepare for ShipmentPrepare for ShipmentTask Completionback: false, canceled: false
6. Mark As FulfilledNone (Direct Call)PUT /fulfilledNone (Optional Manual Step)
async function processShipToHome(shipmentNumber: number, trackingNumber: string) {
    console.log(`Processing STH for Shipment #${shipmentNumber}`);

    try {
        // Step 1: Accept Shipment
        let taskBodyAccept = { "taskBody": { "shipmentAccepted": true }, "handleOption": {} } as any; 
        await shipmentApi.execute({ shipmentNumber, taskName: 'Accept Shipment', taskCompleteDto: taskBodyAccept });
        console.log("   Step 1: Accepted.");

        // Step 2: Validate Stock (assuming IN_STOCK for success case)
        let taskBodyValidate = { "taskBody": { "stockLevel": "IN_STOCK" }, "handleOption": {} } as any;
        await shipmentApi.execute({ shipmentNumber, taskName: 'Validate Items In Stock', taskCompleteDto: taskBodyValidate });
        console.log("   Step 2: Stock validated (IN_STOCK).");

        // Step 3: Print Packing Slip
        let taskBodyPrint = {}; // Empty body
        await shipmentApi.execute({ shipmentNumber, taskName: 'Print Packing Slip', taskCompleteDto: taskBodyPrint });
        console.log("   Step 3: Pick List Printed.");

        // Step 5: Prepare for Shipment (Marks it as fulfilled)
        let taskBodyPrepare = { "taskBody": { "back": false, "canceled": false } } as any;
        await shipmentApi.execute({ shipmentNumber, taskName: 'Prepare for Shipment', taskCompleteDto: taskBodyPrepare });
        console.log("   Step 5: Prepared for Shipment (Now Fulfilled).");

        console.log("STH Fulfillment Complete.");
    } catch (error: any) {
        console.error("STH Fulfillment API Error:", JSON.stringify(error, null, 2));
    }
}

2. Buy Online, Pick Up in Store (BOPIS) Fulfillment Workflow

BOPIS fulfillment follows a flow for in-store pickup.
StepTask Name / ActionEndpoint TypeRequired Body Fields
1. Accept ShipmentAccept ShipmentTask CompletionshipmentAccepted: true
2. Print Pick ListPrint Pick ListTask CompletionEmpty {}
3. Validate StockValidate Items In StockTask CompletionstockLevel: "IN_STOCK" or "PARTIAL_STOCK"
4. Provide to CustomerCustomer PickupTask CompletioncustomerAccepted: true
5. Mark As FulfilledNone (Direct Call)PUT /fulfilledNone (Optional Manual Step)
async function processBopisFulfillment(shipmentNumber: number) {
    console.log(`Processing BOPIS for Shipment #${shipmentNumber}`);

    try {
        // Step 1: Accept Shipment
        let taskBodyAccept = { "taskBody": { "shipmentAccepted": true }, "handleOption": {} } as any;
        await shipmentApi.execute({ shipmentNumber, taskName: 'Accept Shipment', taskCompleteDto: taskBodyAccept });
        console.log("   Step 1: Accepted.");

        // Step 2: Print Pick List
        let taskBodyPrint = {};
        await shipmentApi.execute({ shipmentNumber, taskName: 'Print Pick List', taskCompleteDto: taskBodyPrint });
        console.log("   Step 2: Pick List Printed.");

        // Step 3: Validate Stock
        let taskBodyValidate = { "taskBody": { "stockLevel": "IN_STOCK" }, "handleOption": {} } as any;
        await shipmentApi.execute({ shipmentNumber, taskName: 'Validate Items In Stock', taskCompleteDto: taskBodyValidate });
        console.log("   Step 3: Stock validated (IN_STOCK).");

        // Step 4: Provide to Customer (Customer Pickup task)
        let taskBodyPickup = { "taskBody": { "customerAccepted": true } } as any;
        await shipmentApi.execute({ shipmentNumber, taskName: 'Customer Pickup', taskCompleteDto: taskBodyPickup });
        console.log("   Step 4: Provided to Customer. Shipment should now be Fulfilled.");

        console.log("BOPIS Fulfillment Complete.");
    } catch (error: any) {
        console.error("BOPIS Fulfillment API Error:", JSON.stringify(error, null, 2));
    }
}

3. Transfer Fulfillment Workflow

Transfer shipments move inventory between locations to fulfill an order.
StepTask Name / ActionEndpoint TypeRequired Body Fields
1. Validate StockValidate Items In StockTask CompletionstockLevel: "IN_STOCK" or "PARTIAL_STOCK"
2. Print Packing SlipPrint Packing SlipTask CompletionEmpty {}
3. Add TrackingNone (Direct Call)Edit PackagetrackingNumbers
4. Prepare for ShipmentPrepare for ShipmentTask Completionback: false, canceled: false
5. Validate Incoming TransferValidate Incoming TransferTask CompletionstockLevel: "IN_STOCK" or "PARTIAL_STOCK"
async function processTransferFulfillment(shipmentNumber: number) {
    console.log(`Processing Transfer for Shipment #${shipmentNumber}`);

    try {
        // Step 1: Validate Stock at Origin Location
        let taskBodyValidateOrigin = { "taskBody": { "stockLevel": "IN_STOCK" }, "handleOption": {} } as any;
        await shipmentApi.execute({ shipmentNumber, taskName: 'Validate Items In Stock', taskCompleteDto: taskBodyValidateOrigin });
        console.log("   Step 1: Origin Stock validated (IN_STOCK).");

        // Step 2: Print Packing Slip
        let taskBodyPrint = {};
        await shipmentApi.execute({ shipmentNumber, taskName: 'Print Packing Slip', taskCompleteDto: taskBodyPrint });
        console.log("   Step 2: Pick List Printed.");

        // Step 4: Prepare for Shipment (Marks it as Fulfilled/In Transit)
        let taskBodyPrepare = { "taskBody": { "back": false, "canceled": false } } as any;
        await shipmentApi.execute({ shipmentNumber, taskName: 'Prepare for Shipment', taskCompleteDto: taskBodyPrepare });
        console.log("   Step 4: Prepared for Shipment (In Transit).");

        // Step 5: Validate Incoming Transfer at Destination Location
        let taskBodyValidateIncoming = { "taskBody": { "stockLevel": "IN_STOCK" } } as any;
        await shipmentApi.execute({ shipmentNumber, taskName: 'Validate Incoming Transfer', taskCompleteDto: taskBodyValidateIncoming });
        console.log("   Step 5: Incoming Transfer validated (IN_STOCK).");

        console.log("Transfer Fulfillment Complete.");
    } catch (error: any) {
        console.error("Transfer Fulfillment API Error:", JSON.stringify(error, null, 2));
    }
}

Additional Common Operations (Task-Based)

These examples align with Kibo’s use of either the execute task method or direct API actions for state changes.

Example 1: Get a Shipment’s Details

async function getShipmentDetails(shipmentNumber: number) {
    console.log(`Fetching details for Shipment #${shipmentNumber}`);

    try {
        const shipment = await shipmentApi.getShipment({ shipmentNumber: shipmentNumber });
        console.log("Success: Found shipment.");
        console.log("   Current State:", shipment.workflowState);
        console.log("   Fulfillment Location:", shipment.locationCode);
        return shipment;
    } catch (error: any) {
        console.error("API Error:", JSON.stringify(error, null, 2));
    }
}

Example 2: Reassign a Shipment

Reassigning a shipment is a direct action to transfer the fulfillment responsibility.
async function reassignShipment(shipmentNumber: number, newLocationCode: string) {
    console.log(`Reassigning Shipment #${shipmentNumber} to ${newLocationCode}`);

    try {
        // This is a direct action call
        const reassignResult = await shipmentApi.reassignShipment({
            shipmentNumber: shipmentNumber,
            reassignShipmentRequestDto: {
              fulfillmentLocationCode: newLocationCode
            }
        });
        const newShipmentNumber = reassignResult.childShipmentNumbers?.[0] || 'N/A';
        console.log(`Success: Original shipment reassigned. New shipment created: ${newShipmentNumber}`);
    } catch (error: any) {
        console.error("API Error:", JSON.stringify(error, null, 2));
    }
}

Example 3: Cancel an Item from a Shipment

Canceling items uses a specific direct API call (/canceledItems).
async function cancelItemFromShipment(shipmentNumber: number, lineIdToCancel: number, quantityToCancel: number) {
    console.log(`Canceling ${quantityToCancel} item(s) from Shipment #${shipmentNumber}`);

    try {
        // Using the SDK's direct method for canceled items (PUT /commerce/shipments/{shipmentNumber}/canceledItems)
        const canceledShipment = await shipmentApi.cancelItems({ 
            shipmentNumber: shipmentNumber, 
            canceledItemsDto: {
                items: [
                    {
                        lineId: lineIdToCancel,
                        quantity: quantityToCancel,
                        reason: { reasonCode: "Customer request" }
                    },
                ],
            }
        });
        console.log("Success: The item(s) have been canceled from the shipment. New state:", canceledShipment.workflowState);
    } catch (error: any) {
        console.error("API Error:", JSON.stringify(error, null, 2));
    }
}


Troubleshooting Your Fulfillment Implementation

Reading Kibo Error Messages

// Actual error structure from Kibo API documentation
interface KiboApiError {
  body: {
    message: string;
    errorCode: string;           // e.g., "VALIDATION_ERROR"
    correlationId: string;
  }
}
Common Error Codes for Shipments:
  • ITEM_NOT_FOUND: The shipmentNumber or orderId you provided does not exist.
  • VALIDATION_ERROR: This means you tried to do something that violates the shipment’s current state.
    • Example: Trying to execute the Fulfill task on a shipment not in the Ready state.
  • TASK_NOT_FOUND: The name provided in your Task object is not a valid task (e.g., misspelled).

Common Development Issues

Issue 1: My shipment is “stuck” and I can’t update it.
  • Why it happens: The shipment object is largely read-only; its state is controlled by the task-based workflow. Developers mistakenly try to PUT an update to the shipment object to change its status.
  • How to fix it: You must use the performShipmentTask (or execute) endpoint for all state changes. Before trying to change a shipment, ask “What task do I need to execute?”
Issue 2: The order status is still “Awaiting Shipment” even after I fulfilled one of its shipments.
  • Why it happens: An order’s status is an aggregate of all its shipments. The order will not move to a Completed status until all of its constituent shipments have been fulfilled (or canceled).
  • How to fix it: Ensure your fulfillment process handles all shipments associated with the order.