Skip to main content

Kibo Entities API Developer Guide

Understanding Entities in Kibo

In Kibo, “Entities” are a powerful, flexible way to store custom data that doesn’t fit into the standard Kibo objects like Products or Customers. Think of the Entities system as a lightweight, schema-less NoSQL database built directly into the Kibo platform. What makes it different from other platforms is its simplicity and direct integration. Instead of setting up an external database, you can create an Entity List (like a table or collection) and store Entities (like JSON documents or rows) within it. This is ideal for managing data for custom applications, such as a store locator, a blog, product lookbooks, or storing configuration data for an extension.

How This Domain Fits Into Kibo

The Entities domain is a foundational service within the Kibo platform that supports custom development. It doesn’t directly interact with core commerce flows like Orders or Carts, but it provides the data backbone for custom features you build on top of them.
  • Kibo Extensions: An extension might store its settings or metadata in an Entity List.
  • Storefront Applications (API Extension): A custom React component on your storefront could fetch data from an Entity List to display store hours, blog posts, or promotional content.
  • System Integrations: You can use Entities as a staging area for data being imported from or exported to external systems.

Prerequisites

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

What You’ll Learn

After completing this guide, you’ll understand:
  • How Kibo structures custom data using Entity Lists and Entities (based on official API specs).
  • The key patterns for creating, retrieving, and managing custom data objects (verified from apidocs.kibocommerce.com).
  • Common workflows like creating a custom data store and populating it with information.
  • How to avoid the most common beginner mistakes, like formatting the entityListFullName incorrectly.
  • How to read and navigate the official Entity Lists API documentation effectively.

Kibo Entities Fundamentals

How Kibo Organizes Entities Data

The system is straightforward, with two primary data structures:
  • EntityList: This is the container for your custom data. It’s defined by a unique name and a namespace, which together form its fullName (e.g., warrantypricing@my-tenant). It holds metadata about the data it contains.
  • Entity: This is an individual data record within an EntityList. It consists of a unique id and a item property, which can be any valid JSON object. This schema-less nature is what makes Entities so flexible.
The relationship is simple: an EntityList contains many Entities.

Namespace Architecture

Every Entity List uses a composite identifier in the format name@namespace:
  • Name: The specific data type the list holds (e.g., stores, zipcodes, vehicleMakes).
  • Namespace: The owner or context of the data (e.g., your tenant identifier or application prefix).
This design prevents data collisions between Kibo native applications, third-party extensions, and custom integrations. The fullName (e.g., warrantypricing@12345) is required for all subsequent data operations once the list is created.

Context Scope

When creating a list, you set a contextLevel that controls data visibility:
  • Tenant: Data is shared across all sites within the tenant (e.g., global reference data like vehicle make/model lookups).
  • Site: Data is siloed per site (e.g., site-specific store hours or location-based content).

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 EntityListsApi(configuration)). The clients will automatically handle the OAuth 2.0 token exchange behind the scenes for every API call. Request/Response Structure: When you get a list of entities, Kibo returns a paginated object. The actual data is always inside the items array.
// Actual response schema for GET /platform/entitylists/{entityListFullName}/entities
{
  "totalCount": 42,
  "pageSize": 20,
  "pageCount": 3,
  "startIndex": 0,
  "items": [
    {
      "id": "123-abc",
      "item": {
        "storeName": "Downtown Austin",
        "city": "Austin",
        "state": "TX"
      },
      "name": "Austin Store"
    }
  ]
}
Error Handling Approach: If an API call fails, the SDK throws a structured error object containing an errorCode that tells you exactly what went wrong.
// Actual error structure from Kibo
{
    "message": "Entity list with full name warrantypricing@my-tenant already exists.",
    "errorCode": "ENTITY_LIST_ALREADY_EXISTS",
    "correlationId": "a1b2c3d4-e5f6-4a7b-8c9d-12345fedcba"
}
Pagination: To manage large datasets, Kibo uses pageSize and startIndex parameters. API Documentation Reference: Throughout this guide, we’ll reference specific endpoints. Find complete specs at: /api-overviews/openapi_entities_overview

Common Entities Workflows

Kibo developers typically work with Entities in these scenarios:
  1. Setting up a new data store: Creating a new EntityList to hold a specific type of custom data (e.g., a list for “Store Locations”).
  2. Populating data: Adding individual Entity objects to an EntityList (e.g., adding each store’s address and hours).
  3. Reading and displaying data: Fetching entities from a list to be used in a custom storefront component or an admin UI.
Let’s explore each pattern step by step.

Schema Definition & Indexing

While Entity Lists accept any JSON structure, you must define Indexes to enable efficient filtering and sorting.

The 5-Index Limit

Each Entity List has exactly 5 custom index slots (indexA through indexE), plus the standard _id primary key.
SlotPurposeBehavior
_idPrimary KeyAlways indexed. Unique identifier.
indexA – indexECustom FiltersOptional. Maps a JSON property to a sortable/filterable column.
Governance limits:
  • Data Types: Indexes support string, integer, decimal, date, and boolean.
  • Sorting: You can only sort by fields mapped to an index.
  • Filtering: Filtering by non-indexed fields triggers a full list scan. Acceptable for small lists (under 2,000 items), but will degrade performance or time out on larger datasets.

Optimization: Natural Keys

To save an index slot, use a meaningful “natural key” as the _id field instead of a generated GUID.
  • Inefficient: _id: "guid-123", email: "user@example.com" — requires indexA to search by email.
  • Efficient: _id: "user@example.com" — allows direct lookup by ID without consuming a custom index slot.

Create an Entity List: The Kibo Way

When You Need This

This is the very first step for storing any custom data. Before you can add individual records (Entities), you must create the container (the Entity List) that will hold them.

API Documentation Reference

Endpoint: POST /platform/entitylists Method: POST API Docs: Add Entitylist

Understanding the Kibo Approach

Kibo requires every EntityList to have a name and a namespace, which combine to form a unique fullName like list-name@namespace. This prevents naming collisions and helps organize data, especially in complex environments with multiple applications and tenants. You create the list once, and then you can add, update, or remove entities from it.

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 Entity Lists API.
// 3. **Data Preparation**: Construct the request body object for the new Entity List, defining its name and other properties.
// 4. **API Call**: Use the instantiated client to call the `createEntityList` method.

Step-by-Step Implementation

Step 1: Setting Up the Foundation
// Essential imports for Entities operations.
// The SDK is organized by API groups; we import the Configuration class and the EntityListsApi client.
import { Configuration } from "@kibocommerce/rest-sdk";
import { EntityListsApi } from "@kibocommerce/rest-sdk/clients/Entities";
import { EntityList } from "@kibocommerce/rest-sdk/clients/Entites/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 code will send a JSON object describing the new list to the Kibo API. The API will validate the information (e.g., check if a list with that fullName already exists), create the list, and return the complete EntityList object, including system-generated properties. Step 3: The Core Implementation
// Complete working example for creating an Entity List for store locations.
async function createEntityList(): Promise<EntityList> {
    console.log("Creating 'warrantypricing' entity list...");

    // 1. Instantiate a dedicated client for the Entity Lists API.
    const entityListsClient = new EntityListsApi(configuration);

    // 2. Prepare the request body.
    // The name and namespace will be combined to form the unique identifier.
    // Here, it will be 'warrantypricing@<your_account_namespace>'.
    const listPayload: EntityList = {
            "tenantId": 1111, // Replace with your tenant ID
            "nameSpace": "ABCD", // Replace with your dev account namespace
            "name": "warrantypricing",
            "contextLevel": "Tenant",
            "useSystemAssignedId": false,
            "idProperty": {
                "propertyName": "plu",
                "dataType": "string"
            },
            "indexA": {
                "propertyName": "plu",
                "dataType": "string"
            },
            "isVisibleInStorefront": false,
            "isLocaleSpecific": false,
            "isShopperSpecific": false,
            "isSandboxDataCloningSupported": true,
            "views": [
                {
                    "name": "Default",
                    "usages": [
                        "entityManager"
                    ],
                    "security": "public",
                    "fields": [
                        {
                            "name": "plu",
                            "type": "developerAccount",
                            "target": "string"
                        }
                    ]
                }
            ],
             "usages": [
                "entityManager"
            ],
        };

    // 3. Call the method on the client.
    try {
        const newList = await entityListsClient.createEntityList({
            entityList: listPayload,
        });
        console.log("Success! Created entity list:", newList.listFullName);
        return newList;
    } catch (error) {
        console.error("API Error creating entity list:", JSON.stringify(error, null, 2));
        throw error;
    }
}

What Just Happened? (Code Explanation)

  • The setup phase created the standard Configuration object.
  • We used an instance of EntityListsApi to interact with the API.
  • The payload defined the essential properties of our new data container. The listFullName (e.g., warrantypricing@12345) is the unique key we will use in all future API calls to interact with this list.
  • The createEntityList method sent this payload to Kibo, which created the empty list.

Common Beginner Mistakes

Mistake 1: Not understanding the entityListFullName. You don’t set the fullName directly. Kibo constructs it from the name and namespace you provide. In all subsequent calls (like adding an entity), you must use this full name (e.g., warrantypricing@12345). Mistake 2: Creating duplicate lists. The createEntityList call will fail if a list with the same fullName already exists. Your code should anticipate this, either by checking first or by handling the ENTITY_LIST_ALREADY_EXISTS error.

Delete an Entity List

When You Need This: When a list and all of its data are no longer needed. Endpoint: DELETE /platform/entitylists/{entityListFullName}
Warning: This operation is destructive and permanently deletes both the schema definition and all contained data. There is no undo.

Create and Manage Entities

This section covers the core operations for managing the actual data records within a list.

Add a New Entity

When You Need This: After creating a list, you need to populate it with data. Each piece of data is a new entity. API Documentation Reference:
  • Endpoint: POST /platform/entitylists/{entityListFullName}/entities
  • Method: POST
  • API Docs: Add Entity
// Example: Add a new store to our 'warrantypricing' list.
async function addEntity(listFullName: string, storeData: any) {
    const entityClient = new EntitiesApi(configuration);

    try {
        // The `item` property can be any valid JSON object. This is where your custom data goes.
        const newEntity = await entityClient.insertEntity({
            entityListFullName: listFullName,
            appDevHttpRequestMessage: {
                data: {},
                plu: 1111, // This is the required identifier declared when creating the entity list.
            } as any,
        });
        console.log(`Successfully added entity with ID: ${newEntity.id}`);
        return newEntity;
    } catch (error) {
        console.error("API Error adding entity:", JSON.stringify(error, null, 2));
        throw error;
    }
}

Get Entity from a List

When You Need This: When your application needs to read and display the custom data you’ve stored. API Documentation Reference:
  • Endpoint: GET /platform/entitylists/{entityListFullName}/entities
  • Method: GET
  • API Docs: Get Entities
// Example: Get all stores in Texas from our list.
async function getWarrantyPrice(plu: string, listFullName: string) {
    const entityClient = new EntitiesApi(configuration);

    try {
        const response = await entityClient.getEntity({
            entityListFullName: listFullName,
            id: plu,  
        });
        console.log(`Found warranty price data for ${plu}.`);
        return response;
    } catch (error) {
        console.error("API Error getting entities:", JSON.stringify(error, null, 2));
        throw error;
    }
}

Update an Entity

When You Need This: When an existing entity’s data has changed and needs to be replaced. API Documentation Reference:
  • Endpoint: PUT /platform/entitylists/{entityListFullName}/entities/{id}
  • Method: PUT
  • API Docs: Update Entity
Important: This is a full replacement of the document. Partial updates (PATCH) are not supported. You must read the existing entity, apply your changes, and rewrite the entire object.
async function updateEntity(listFullName: string, id: string, updatedData: any) {
    const entityClient = new EntitiesApi(configuration);

    try {
        const updated = await entityClient.updateEntity({
            entityListFullName: listFullName,
            id: id,
            appDevHttpRequestMessage: updatedData as any,
        });
        console.log(`Successfully updated entity with ID: ${id}`);
        return updated;
    } catch (error) {
        console.error("API Error updating entity:", JSON.stringify(error, null, 2));
        throw error;
    }
}

Delete an Entity

When You Need This: When a piece of data is no longer relevant and needs to be removed. API Documentation Reference:
  • Endpoint: DELETE /platform/entitylists/{entityListFullName}/entities/{id}
  • Method: DELETE
  • API Docs: Delete Entity
// Example: Delete a store by its unique entity ID.
async function deleteWarrantyEntity(plu: string, listFullName: string) {
    const entityClient = new EntitiesApi(configuration);

    try {
        const response = await entityClient.deleteEntity({
            entityListFullName: listFullName,
            id: plu,  
        });
        console.log(`Found warranty price data for ${plu}.`);
        return response;
    } catch (error) {
        console.error("API Error getting entities:", JSON.stringify(error, null, 2));
        throw error;
    }
}

Querying & Filtering Entities

Endpoint: GET /platform/entitylists/{entityListFullName}/entities Use the filter and sortBy query parameters to retrieve specific records. Fields used in filter or sortBy must be mapped to an index (indexAindexE, _id, createDate, or updateDate).

Supported Operators by Data Type

Data TypeSupported OperatorsNotes
Stringeq, ne, sw (starts with), cont (contains)Comparisons are case-insensitive.
Integer & Decimaleq, ne, lt, le, gt, geSupports precise matching and range queries.
Dateeq, ne, lt, le, gt, geWorks for custom date indexes and system fields like createDate and updateDate.
Booleaneq, neStrict equality matching.
Note: The in (list inclusion) and near (geospatial) operators are not currently supported.

Example Queries

ScenarioFilter Query
Filter by status (string)indexA eq 'Published'
Filter by ID prefix (string)indexA sw 'PROD-'
Filter by price range (decimal)indexB gt 19.99 and indexB lt 50.00
Filter by creation datecreateDate ge 2023-01-01T00:00:00Z
Sort by indexed field?filter=indexA eq 'active'&sortBy=indexB asc

Mixed Filtering (Indexed + Non-Indexed)

You can filter on any field within the JSON document, even if it is not explicitly indexed. However, you must include at least one filter on an indexed property to anchor the query. The anchor filter narrows the dataset using the index before the system scans the remaining JSON data. Example: Find items in the “Sales” department (non-indexed) that are also “active” (indexed):
filter=indexA eq 'active' and item.department eq 'Sales'

Integrating Entities with Other Kibo Domains

Entities + Storefront (Arc.js) Integration

This is the most common use case. An Arc.js application on your product detail page could make a client-side API call to an Entity List to pull in “Lookbook” data or “Buying Guide” content associated with that product. Because the data is schema-less, you can easily adapt it as your content needs change without requiring a developer to alter a database schema.

Entities + Extensions Integration

An extension often needs a place to store its configuration. For example, a custom shipping integration might need to store API keys, service URLs, and shipping method mappings. An EntityList is the perfect place to store this data. The extension can read its configuration from the list upon startup.

Bulk Import Strategy

Entity Lists do not provide a dedicated server-side bulk insert endpoint. High-volume data loading must be orchestrated client-side.
  1. Read source data from your file or external system.
  2. Split into chunks of 5 records each.
  3. Process each chunk using parallel async requests (Promise.all in JavaScript or Task.WhenAll in C#).
  4. Limit concurrency to 3–5 simultaneous requests to avoid 429 Too Many Requests errors.
  5. Wrap individual calls in try/catch blocks so a single failure does not halt the entire batch.
async function bulkImport(listFullName: string, records: any[]) {
    const entityClient = new EntitiesApi(configuration);
    const chunkSize = 5;

    for (let i = 0; i < records.length; i += chunkSize) {
        const chunk = records.slice(i, i + chunkSize);
        await Promise.all(
            chunk.map(record =>
                entityClient.insertEntity({
                    entityListFullName: listFullName,
                    appDevHttpRequestMessage: record as any,
                }).catch(err => console.error(`Failed to insert record ${record.id}:`, err))
            )
        );
        console.log(`Processed ${Math.min(i + chunkSize, records.length)} of ${records.length} records`);
    }
}

Best Practices

  1. Namespace: Always use a unique, identifiable namespace to prevent data collisions with other applications or tenants.
  2. Index selectively: Only index fields required for sorting or filtering. Avoid indexing fields just because they exist.
  3. Keep entities lean: Avoid storing large JSON blobs. Smaller documents improve read/write performance.
  4. Don’t use Entities as a search engine: Entity Lists are not designed for full-text search. For complex keyword matching, use the Kibo Catalog & Search APIs.
  5. Test schema changes in Sandbox first: Changing index definitions on live data requires re-processing. Validate your schema before deploying to production.
  6. Plan for the 5,000-record limit: Modifying indexed properties on lists with over 5,000 records will fail with a VALIDATION_CONFLICT error. Design your schema carefully upfront.

Troubleshooting Your Entities 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 Entities:
  • ENTITY_LIST_ALREADY_EXISTS: You tried to call createEntityList with a name and namespace that are already in use.
  • ENTITY_LIST_NOT_FOUND: The entityListFullName you provided in a request does not exist. Check for typos or incorrect formatting.
  • ENTITY_NOT_FOUND: The id you provided for a specific entity does not exist in the given list.
  • VALIDATION_ERROR: The request body is malformed, or you are trying to use a filter with invalid syntax.

Common Development Issues

Issue 1: All API calls are failing with ENTITY_LIST_NOT_FOUND.
  • Why it happens: You are probably formatting the entityListFullName parameter incorrectly. It is case-sensitive and must be in the format list-name@namespace. A common mistake is to only provide the list name.
  • How to fix it: When you create the list, save the returned listFullName property. Use this exact value in all subsequent calls. For example: warrantypricing@12345.
  • API Reference: All endpoints under /developer-guides/entities require this parameter.
Issue 2: My filter query is not returning any results, even though I know there is matching data.
  • Why it happens: The filter syntax can be tricky, especially for nested JSON. You must use dot notation to access properties within the item object (e.g., item.address.city). The filter is also case-sensitive.
  • How to fix it: Double-check your filter syntax against the Kibo documentation. Start with a simple filter (item.state eq 'TX') and build up to more complex ones. Log the exact data in Kibo to ensure there isn’t a case mismatch (e.g., ‘tx’ vs ‘TX’).
Issue 3: I’m getting a VALIDATION_CONFLICT error when trying to update an Entity List definition.
{
  "items": [],
  "errorCode": "VALIDATION_CONFLICT",
  "message": "Validation Error: VALIDATION_CONFLICT.additionalErrorDetails.cannotModifyIndexedProperties"
}
  • Why it happens: The Kibo platform prevents modifications to the id property or any indexed properties on Entity Lists that contain more than 5,000 records. This safeguard prevents timeouts during schema updates on large datasets.
  • How to fix it: Choose one of these approaches:
    1. Revert index changes: Ensure your update request does not modify the id or index configurations — keep them identical to the existing definition.
    2. Reduce list size: Delete items from the list until the count is below 5,000, perform the schema update, then re-populate.
    3. Create a new list: Create a new Entity List with the desired schema, migrate your data to it, then decommission the old list.