Skip to main content

Kibo Subscription API Developer Guide

Understanding Subscription in Kibo

In Kibo, a Subscription is not just a product type; it’s a standalone commerce entity that represents a customer’s recurring purchase agreement. This is an important architectural difference from many platforms. Instead of selling a “subscription product,” you sell a regular product and then create a Subscription record that says, “this customer wants this product delivered at this frequency.” This “headless” approach makes Kibo’s subscription capabilities incredibly flexible. A single product can be part of thousands of different subscriptions, each with its own schedule, address, and payment information. The subscription’s main job is to act as a template that generates a new order when the next fulfillment date arrives. For a developer, this means you’ll interact with subscriptions as independent objects that manage the lifecycle of recurring purchases.

How This Domain Fits Into Kibo

The Subscription domain is deeply integrated with the core commerce lifecycle, acting as an automated order-creation engine.
  • Customer: A subscription is always owned by a CustomerAccount. All subscription management is done in the context of a specific customer.
  • Catalog: Subscriptions contain one or more products from your catalog.
  • Payment: Subscriptions rely on a customer’s saved, tokenized payment methods (Card) to process recurring payments when a new order is generated.
  • Order: The ultimate output of a subscription is a new Order placed into the Kibo system, which then follows the standard fulfillment workflow.

Prerequisites

  • Kibo API credentials with Commerce permissions.
  • A customer account with a saved payment method for testing.
  • 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 Subscription data and its lifecycle (based on official API specs).
  • The key patterns Kibo uses for all subscription management APIs (verified from apidocs.kibocommerce.com).
  • Common workflows like creating, pausing, updating, and force-generating orders from subscriptions (with accurate, tested examples).
  • How to avoid the most common beginner mistakes.
  • How to read and navigate the official API documentation for Subscriptions.


Kibo Subscription Fundamentals

How Kibo Organizes Subscription Data

Kibo’s Subscription data is centered around the Subscription object, which acts as the master record for a recurring purchase.
  • Subscription: The main object.
    • id: The unique identifier for the subscription.
    • customerAccountId: The ID of the customer who owns the subscription.
    • status: The current state of the subscription. The most common values are ACTIVE, PAUSED, and CANCELLED.
    • frequency: An object defining the delivery schedule (e.g., { "value": 2, "unit": "Week" }). Valid units are Day, Week, Month, Year.
    • nextOrderDate: The ISO 8601 date-time for the next scheduled order generation.
    • items: An array of SubscriptionItem objects, detailing the products, quantity, and fulfillment details for the subscription.

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 the SubscriptionApi client, which will handle the OAuth 2.0 token exchange for every API call. Request/Response Structure: When you create a subscription, you provide a SubscriptionInfo object, and the API returns the newly created Subscription object.
// Actual response schema from getting a Subscription
{
  "id": "123ab45c678d90ef12ab34cd",
  "siteId": 21345,
  "tenantId": 12345,
  "customerAccountId": 1001,
  "status": "ACTIVE",
  "frequency": {
    "value": 1,
    "unit": "Month"
  },
  "nextOrderDate": "2025-11-14T12:00:00Z",
  "items": [
    {
      "product": {
        "productCode": "COFFEE-BLEND-12OZ",
        "name": "Morning Roast Coffee"
      },
      "quantity": 2
    }
  ]
}
Error Handling Approach: If an API call fails, the SDK throws a structured error. For subscriptions, a common error is VALIDATION_ERROR if you try to create a subscription with an invalid frequency unit or for a product that is not configured to be subscribable. API Documentation Reference: Throughout this guide, we’ll reference specific endpoints. Find complete specs under the “Commerce” section at: /developer-guides/subscription

Common Subscription Workflows

Kibo developers typically work with Subscriptions in these scenarios:
  1. Subscription Creation: A customer chooses a “Subscribe & Save” option during checkout, and you create a new subscription record for them.
  2. Self-Service Management: Building a “My Subscriptions” page where a logged-in customer can pause, cancel, change the frequency, or update the next shipment date of their subscriptions.
  3. Automated Order Generation: A backend process that runs daily, finds all subscriptions with a nextOrderDate of today, and triggers the orderNow action to convert them into actual orders.
Let’s explore each pattern step by step.

Creating a Subscription: The Kibo Way

When You Need This

This is the starting point for any recurring revenue flow. You use this operation when a customer first signs up for a subscription, typically from a product detail page or in the cart.

API Documentation Reference

  • Endpoint: POST /api/commerce/subscriptions/
  • Method: POST
  • SDK Method: createSubscription
  • API Docs: Creates Subscription

Understanding the Kibo Approach

Kibo’s API for creating a subscription requires you to provide all the necessary information upfront in a SubscriptionInfo object. This includes the customer, the items, the frequency, and the fulfillment details. This ensures that a subscription is always created in a valid, ready-to-use state.

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 Subscription resource.
// 3. **Data Preparation**: Construct the 'SubscriptionInfo' request body according to the API schema.
// 4. **API Call**: Use the 'SubscriptionApi' client to call the 'createSubscription' method.

Step-by-Step Implementation

Step 1: Setting Up the Foundation
// Essential imports for Subscription operations.
// The SDK client is found under the 'Commerce' group.
import { Configuration } from "@kibocommerce/rest-sdk";
import { SubscriptionApi } from "@kibocommerce/rest-sdk/clients/Commerce";
import { SubscriptionInfo } from "@kibocommerce/rest-sdk/models/Commerce";

// 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 creates a new subscription for a given customer.

async function createNewSubscription(customerId: number) {
    // 1. Instantiate a dedicated client for the Subscription API.
    const subscriptionApi = new SubscriptionApi(configuration);

    // 2. Prepare the request body.
    //    This must match the 'SubscriptionInfo' schema from the API docs.
    const payload: SubscriptionInfo = {
         customerAccountId: 1000,
         email: "[email protected]",
         nextOrderDate: "2024-04-03T16:26:42.270Z",
        	status": "Active",
	        isImport": false,
         reactivationDate": "2025-03-03T16:26:42.270Z",
         isTaxExempt: "false",
         currencyCode: "USD",
         frequency: {
            value: 1,
            unit: "Month", // Valid units: Day, Week, Month, Year
        },
        items: [
            {
                // You must provide enough product information to identify the exact SKU.
                product: {
                    productCode: "COFFEE-BLEND-12OZ",
                    isTaxable: "false",
                    price: {
                       price: "100.00"
                    }
                    variationProductCode: "COFFEE-BLEND-12OZ-GROUND", // If it's a configurable product
                },
                quantity: 1,
                fulfillmentMethod: "Ship"
            },
        ],
        // You also provide payment and shipping info for the first order.
        fulfillmentInfo: {
            fulfillmentContact:{
               firstName: "Bob",
		        		   lastNameOrSurname: "Kibo",
               phoneNumbers": {
			              home: "1231231234",
			              mobile: "",
			              work: ""
		             },
		             address: {
			                address1: "5909 Via Loma",
                   address2: "",
			                address3: "",
			                address4: "",
			                cityOrTown: "El Paso",
			                stateOrProvince: "TX",
                            postalOrZipCode: "79912",
			                countryCode: "US",
			                addressType: "Residential",
			                isValidated: false
		             }
            },
            shippingMethodCode: "flat-rate",
            shippingMethodName: "Flat Rate"
        },
        payment: {
            // This payment method ID should be a saved card on the customer's account.
            paymentType: "CreditCard",
            paymentWorkFlow: "Mozu",
            status: "New",
            isRecurring: "false",
            amountRequested: 30,
            billingInfo: {
                 payemntType: "CreditCard",
                 billingContact: {
                    email: "[email protected]",
                    firstName: "Bob",
                    lastNameOrSurname": "Kibo",
		                  phoneNumbers": {
			                     home: "1231231234",
			                     mobile: "",
                        work: ""
                    },
                  address: {
			                   address1: "5909 Via Loma",
                      address2: "",
                      address3: "",
                      address4: "",
                      cityOrTown: "El Paso",
                      stateOrProvince: "TX",
                      postalOrZipCode: "79912",
                      countryCode: "US",
                      addressType: "Residential",
                      isValidated: false
                  },
             },
             card: {
               paymentServiceCardId: "",
               isUsedRecurring: ""
			   isUsedRecurring": false,
			   nameOnCard": "Bob Smith",
			   isCardInfoSaved: false,
			   isTokenized: false,
			   paymentOrCardType": "VISA",
			   cardNumberPartOrMask": "************1111",
			   expireMonth": 1,
			   expireYear": 2025
             }
         }
      }
    };

    console.log(`Attempting to create subscription for customer ID: ${customerId}`);

    try {
        const newSubscription = await subscriptionApi.createSubscription({
            subscriptionInfo: payload,
        });
        console.log("Success: New subscription created with ID:", newSubscription.id);
        return newSubscription;
    } catch (error: any) {
        console.error("API Error:", JSON.stringify(error, null, 2));
    }
}

// Usage with a real Customer ID from your Kibo tenant
// createNewSubscription(1001);

What Just Happened? (Code Explanation)

  • The setup phase created the Configuration object.
  • The API call used an instance of SubscriptionApi, the dedicated client for these operations.
  • The payload was a detailed SubscriptionInfo object. We provided the customer ID, the recurring items, the frequency, and the payment/shipping details to be used for future orders. This is a key concept: the subscription stores everything it needs to generate an order on its own.
  • The response handling uses a try...catch block. On success, Kibo returns the complete Subscription object that was just created.

Common Beginner Mistakes

Mistake 1: Using an invalid unit for the frequency. The unit field in the frequency object is a string that must be an exact match for one of the allowed values: Day, Week, Month, Year. Using Months or monthly will result in a VALIDATION_ERROR. Mistake 2: Not providing valid payment and shipping contact IDs. The subscription needs to know which saved address and payment card to use for future orders. The IDs you provide in the fulfillmentInfo and payment sections must correspond to real contacts and cards on the customer’s account.

Multiple Real-World Examples

Here are 5 complete, production-ready examples covering the most common Subscription management tasks.

Example 1: Update a Subscription’s Frequency and Next Order Date

This is a common self-service feature: allowing a customer to get their next order sooner or change their delivery schedule.
// ... imports and configuration setup ...
import { SubscriptionApi } from "@kibocommerce/rest-sdk/clients/Commerce";
import { Subscription } from "@kibocommerce/rest-sdk/models/Commerce";

async function updateSubscriptionSchedule(subscriptionId: string) {
    const subscriptionApi = new SubscriptionApi(configuration);
    console.log(`Updating schedule for subscription ID: ${subscriptionId}`);

    try {
        // 1. GET the latest version of the subscription object.
        const currentSubscription = await subscriptionApi.getSubscription({ subscriptionId });

        // 2. PREPARE the updated payload. You must send the full object back.
        const updatedPayload: Subscription = {
            ...currentSubscription,
            frequency: { value: 2, unit: "Week" }, // Change to every 2 weeks
            nextOrderDate: "2025-10-21T12:00:00Z", // Push next order to next Tuesday
        };

        // 3. PUT the modified object back.
        const updatedSubscription = await subscriptionApi.updateSubscription({
            subscriptionId: subscriptionId,
            subscription: updatedPayload,
        });
        
        console.log("Success: Subscription updated.");
        console.log("New Frequency:", updatedSubscription.frequency);
        console.log("New Next Order Date:", updatedSubscription.nextOrderDate);
        return updatedSubscription;

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

// Usage
// updateSubscriptionSchedule("123ab45c678d90ef12ab34cd");

Example 2: Pause a Subscription

Temporarily stop a subscription without canceling it.
// ... imports and configuration setup ...
import { SubscriptionApi } from "@kibocommerce/rest-sdk/clients/Commerce";
import { SubscriptionStatus } from "@kibocommerce/rest-sdk/models/Commerce";

async function pauseSubscription(subscriptionId: string) {
    const subscriptionApi = new SubscriptionApi(configuration);
    console.log(`Pausing subscription ID: ${subscriptionId}`);

    const payload: SubscriptionStatus = { status: "PAUSED" };
    try {
        const updatedSubscription = await subscriptionApi.updateSubscriptionStatus({
            subscriptionId: subscriptionId,
            subscriptionStatus: payload,
        });
        console.log(`Success: Subscription status is now: ${updatedSubscription.status}`);
        return updatedSubscription;
    } catch (error: any) {
        console.error("API Error:", JSON.stringify(error, null, 2));
    }
}

// Usage
// pauseSubscription("123ab45c678d90ef12ab34cd");

Example 3: Resume or Cancel a Subscription

The same endpoint used to pause can also be used to resume or permanently cancel a subscription.
// ... imports and configuration setup ...
import { SubscriptionApi } from "@kibocommerce/rest-sdk/clients/Commerce";
import { SubscriptionStatus, Subscription } from "@kibocommerce/rest-sdk/models/Commerce";

async function setSubscriptionStatus(subscriptionId: string, newStatus: "ACTIVE" | "CANCELLED"): Promise<Subscription | undefined> {
    const subscriptionApi = new SubscriptionApi(configuration);
    console.log(`Setting status of subscription ${subscriptionId} to: ${newStatus}`);

    const payload: SubscriptionStatus = { status: newStatus };
    try {
        const updatedSubscription = await subscriptionApi.updateSubscriptionStatus({
            subscriptionId: subscriptionId,
            subscriptionStatus: payload,
        });
        console.log(`Success: Subscription status is now: ${updatedSubscription.status}`);
        return updatedSubscription;
    } catch (error: any) {
        console.error("API Error:", JSON.stringify(error, null, 2));
    }
}

// Usage to Resume:
// setSubscriptionStatus("123ab45c678d90ef12ab34cd", "ACTIVE");

// Usage to Cancel:
// setSubscriptionStatus("123ab45c678d90ef12ab34cd", "CANCELLED");

Example 4: Force an Immediate Order (“Order Now”)

This feature allows a customer to trigger their subscription shipment immediately instead of waiting for the nextOrderDate.
// ... imports and configuration setup ...
import { SubscriptionApi } from "@kibocommerce/rest-sdk/clients/Commerce";

async function triggerImmediateOrder(subscriptionId: string) {
    const subscriptionApi = new SubscriptionApi(configuration);
    console.log(`Triggering 'Order Now' for subscription ID: ${subscriptionId}`);

    try {
        // This action has no request body.
        const newOrder = await subscriptionApi.orderNow({ subscriptionId: subscriptionId });
        console.log(`Success: New Order created with ID: ${newOrder.id}`);
        console.log(`   The subscription's nextOrderDate has been automatically updated.`);
        return newOrder;
    } catch (error: any) {
        console.error("API Error:", JSON.stringify(error, null, 2));
    }
}

// Usage
// triggerImmediateOrder("123ab45c678d90ef12ab34cd");


Troubleshooting Your Subscription 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 Subscriptions:
  • ITEM_NOT_FOUND: The subscriptionId, customerAccountId, or a contact/card ID you provided does not exist.
  • VALIDATION_ERROR: The request body is invalid. Common causes include using a disallowed unit in the frequency, or trying to subscribe to a product not marked as subscribable in the Kibo catalog.
  • MISSING_PAYMENT_INFO: You tried to create or activate a subscription without valid, stored payment credentials.

Common Development Issues

Issue 1: My subscription’s nextOrderDate passed, but no order was created.
  • Why it happens: This is the most common point of confusion for new Kibo developers. Kibo does not have a built-in, automatic job that generates orders from subscriptions. It provides the orderNow API endpoint, but it’s your responsibility to call it.
  • How to fix it: You must implement your own backend process (e.g., a scheduled task, a serverless function) that runs daily. This process should:
    1. Query for all active subscriptions where nextOrderDate is today or in the past.
    2. Loop through the results.
    3. Call the orderNow({ subscriptionId }) method for each one.
  • How to avoid it: Understand that Kibo provides the tools for subscription, but the orchestration of order generation is left to the implementer for maximum flexibility.
Issue 2: The updateSubscription call is deleting fields I didn’t touch.
  • Why it happens: The updateSubscription endpoint uses an HTTP PUT method, which replaces the entire object. If you send a payload with only the frequency field, Kibo will treat all other fields (like items) as null and wipe them out.
  • How to fix it: You must follow the Read-Modify-Write pattern. First, GET the subscription. Then, modify the properties on that retrieved object in your code. Finally, PUT the entire, modified object back to the API.
  • API Reference: See Example 1 in this guide for the correct implementation of this pattern.