> ## Documentation Index
> Fetch the complete documentation index at: https://docs.kibocommerce.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Entities API

> Custom data storage with entity lists for extensions and integrations

# 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.

```json theme={null}
// 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.

```json theme={null}
// 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.

| Slot                | Purpose        | Behavior                                                        |
| :------------------ | :------------- | :-------------------------------------------------------------- |
| **\_id**            | Primary Key    | Always indexed. Unique identifier.                              |
| **indexA – indexE** | Custom Filters | Optional. 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](/api-reference/entitylists/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

```typescript theme={null}
// 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**

```ts theme={null}
// 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**

```ts theme={null}
// 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](/api-reference/entities/add-entity)

```ts theme={null}
// 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](/api-reference/entities/get-entities)

```ts theme={null}
// 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](/api-reference/entities/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.

```ts theme={null}
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](/api-reference/entities/delete-entity)

```ts theme={null}
// 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 (`indexA`–`indexE`, `_id`, `createDate`, or `updateDate`).

### Supported Operators by Data Type

| Data Type             | Supported Operators                               | Notes                                                                               |
| :-------------------- | :------------------------------------------------ | :---------------------------------------------------------------------------------- |
| **String**            | `eq`, `ne`, `sw` (starts with), `cont` (contains) | Comparisons are case-insensitive.                                                   |
| **Integer & Decimal** | `eq`, `ne`, `lt`, `le`, `gt`, `ge`                | Supports precise matching and range queries.                                        |
| **Date**              | `eq`, `ne`, `lt`, `le`, `gt`, `ge`                | Works for custom date indexes and system fields like `createDate` and `updateDate`. |
| **Boolean**           | `eq`, `ne`                                        | Strict equality matching.                                                           |

> **Note:** The `in` (list inclusion) and `near` (geospatial) operators are not currently supported.

### Example Queries

| Scenario                        | Filter 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 date         | `createDate 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.

### Recommended Pattern

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.

```ts theme={null}
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

```typescript theme={null}
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.

```json theme={null}
{
  "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.
