# Knowledge Base Category API V3 — Developer Guide

- **Applies to:** Sprinklr Knowledge Base Category (Folder) API V3 (`/api/v3/folder`)
- **V2 API reference:** [Search Folder (Category) | Sprinklr Developer Portal](https://dev.sprinklr.com/search-folder-category) · [Read Folder by Folder Id | Sprinklr Developer Portal](https://dev.sprinklr.com/read-folder-by-folder-id) · [Create/Update Knowledge Base Category | Sprinklr Developer Portal](https://dev.sprinklr.com/create-update-knowledge-base-category)


## 1. Overview

Knowledge Base **Categories** are the folders that organize Knowledge Base content into logical groups based on shared characteristics or properties. Every Knowledge Base article is placed into a category through its `folderMetadata.folderId`, so the Category API is the addressing layer that sits underneath the Articles API: you resolve a category ID here, then use it to create, filter, or export articles there.

In Sprinklr's data model, a Knowledge Base Category **is** a folder. The API resource is called `folder`, the UI calls it a *Category*, and the two terms refer to the same object. This guide uses **Category** for the concept and `folder` for the path, matching the reference pages.

Knowledge Base Category V3 publishes **two read operations**:

| Operation | Method | Path |
|  --- | --- | --- |
| Read Category by Category ID | `GET` | `/api/v3/folder?folderId=` |
| Search Categories | `POST` | `/api/v3/folder/search` |


### 1.1 The Category object

Every Category is returned as one **folder object**. The same object shape is returned by both the read and the search endpoint, organized in five layers.

| Layer | Fields | What it holds |
|  --- | --- | --- |
| Identity | `id`, `name`, `description` | The Category and its display name |
| Hierarchy | `parentId`, `path`, `userVisiblePath` | Where the Category sits in the tree |
| Classification | `assetClasses`, `tags`, `moduleTypes`, `assetMetadata`, `additional` | What kind of content the Category holds and how it is labelled |
| Access | `shareConfigs`, `grants`, `markPublic`, `confidential`, `disableChildSharing`, `canEdit`, `favourite` | Who can see and edit the Category |
| Audit | `clientId`, `ownerUserId`, `createdTime`, `modifiedTime`, `lastModifiedUserId`, `deleted` | Ownership and change history |


A sixth structure, `mappingDetails[]`, records the mapping between a Knowledge Base Category and Sprinklr **Community** categories and topics. It is present on the read endpoint's response and is the join key if you are syncing a Knowledge Base tree with a Community project.

### 1.2 The category hierarchy

Categories nest. Two fields express the hierarchy and they are not interchangeable:

| Field | Type | Contents |
|  --- | --- | --- |
| `parentId` | String | ID of the immediate parent Category. Empty string (`""`) for a top-level Category. |
| `path` | Array of Strings | **Ordered** list of Category IDs from the root down to and including this Category. |


For a top-level Category, `parentId` is `""` and `path` contains exactly one element — the Category's own ID:

```json
"id": "6a8fe530e3d386956167d3a5",
"parentId": "",
"path": [
  "6a8fe530e3d386956167d3a5"
]
```

For a nested Category, `path` gives you the full ancestry in one field, so you can build a breadcrumb without walking `parentId` upward call by call:

```json
"id": "67470fa0725be30f726a868f",
"parentId": "6715fb6edd12033df96bffe3",
"path": [
  "6715fb6edd12033df96bffe3",
  "67470fa0725be30f726a868f"
]
```

The last element of `path` is always the Category's own `id`. The second-to-last element is always `parentId`.

### 1.3 Addressing a Category

| Addressing mode | Parameter | Endpoint | When to use |
|  --- | --- | --- | --- |
| By Category ID | `folderId` | `GET /folder` | You already hold the Sprinklr Category ID |
| By search criteria | `assetClasses`, `query`, `scope` and others in the body | `POST /folder/search` | Discovery, tree walking, and bulk export |


**Steps to extract a Category ID from the Sprinklr UI:**

1. Search **Knowledge Base** from the universal search bar on the Sprinklr homepage.
2. Click the Knowledge Base category/folder you need the ID for.
3. The category/folder ID is the last segment of the browser URL.
4. For example, if the URL is `https://sprinklr.com/care/knowledge-base/categories/65aa485c36fd937eb0a1d815`, the Category ID is `65aa485c36fd937eb0a1d815`.


Using `POST /folder/search` is the programmatic alternative and is what you should build against — the UI method is for one-off lookups during development.

## 2. Base URLs and environments

**All API calls are sent to the production endpoint:**

```
https://api3.sprinklr.com/{env}/api/v3
```

So the Knowledge Base Category resource is:

```
https://api3.sprinklr.com/{env}/api/v3/folder
```

Replace `{env}` with your assigned environment identifier (`prod0`, `prod2`, `prod11`, and so on — see [APIs | Sprinklr Developer Portal](https://dev.sprinklr.com/apis) for the environment list).

## 3. Authentication and common headers

All Knowledge Base Category API calls are authenticated with OAuth 2.0. For generating the authorization token, see the [Authorize](https://dev.sprinklr.com/authorize) section on the developer portal. For generating the API key, see the [Getting Started](https://dev.sprinklr.com/api-key-and-secret-generation) guide.

| Header | Value | Purpose | Required on |
|  --- | --- | --- | --- |
| `Authorization` | `******` | Credential used by the API to authenticate a user with the server | All requests |
| `Key` | `api-key` | API key that authenticates the application with the server | All requests |
| `Content-Type` | `application/json` | Determines the type of data (media/resource) present in the request body | All requests |
| `Accept` | `application/json` | Determines the acceptable response type from the server | All requests |


The header table is identical on both V3 Category reference pages and on the V2 Category pages, so no header changes are needed when migrating. Note that both V3 pages list `Content-Type: application/json` even though `GET /folder` sends no body.

### 3.1 Permissions

The Create/Update Knowledge Base Category (V2) reference page states two prerequisites:

- You must have the **View** and **Create Category** permissions under the Knowledge Base module.
- The required Knowledge Base Category (folder) must be **shared with you**.


## 5. Read operations

### 5.1 Read Category by Category ID

Fetch the Knowledge Base Category (folder) details for the given Folder ID.

```
GET https://api3.sprinklr.com/{env}/api/v3/folder
```

#### Query parameters

| Parameter | Required/Optional | Description | Type |
|  --- | --- | --- | --- |
| `folderId` | Required | Refers to the unique identifier for the Knowledge Base Category. | String |


#### Request

```bash
curl --location 'https://api3.sprinklr.com/{env}/api/v3/folder?folderId=67470fa0725be30f726a868f' \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--header 'Authorization: ****** your Access Token}' \
--header 'Key: {Enter your API Key}'
```

#### Response

```json
{
  "data": {
    "id": "67470fa0725be30f726a868f",
    "name": "RKP Category Hierarchy - 2",
    "parentId": "6715fb6edd12033df96bffe3",
    "path": [
      "6715fb6edd12033df96bffe3",
      "67470fa0725be30f726a868f"
    ],
    "assetClasses": [
      "KNOWLEDGE_BASE_CONTENT"
    ],
    "tags": [],
    "confidential": false,
    "favourite": false,
    "assetMetadata": {},
    "mappingDetails": [
      {
        "mappedProjectId": "8538f0a0-5bdb-4f1d-91a3-98f8f3d3aaee",
        "mappedCategoryIds": [
          "681c7d0ef879d34473bd5d4b"
        ],
        "mappedTopicIds": []
      },
      {
        "mappedProjectId": "3284de07-b911-4e6e-a419-42f9cb3a357f",
        "mappedCategoryIds": [
          "68c14317e887085952c5016a"
        ],
        "mappedTopicIds": []
      }
    ],
    "markPublic": false,
    "disableChildSharing": false,
    "shareConfigs": [
      {
        "shareLevel": "CLIENT",
        "sharedWithIds": []
      },
      {
        "shareLevel": "CLIENT_GROUP",
        "sharedWithIds": []
      },
      {
        "shareLevel": "USER",
        "sharedWithIds": [
          "66066902"
        ]
      },
      {
        "shareLevel": "USER_GROUP",
        "sharedWithIds": []
      }
    ],
    "grants": [
      "CLIENT/66000002/OWNERSHIP",
      "USER/66011271/OWNERSHIP"
    ],
    "clientId": 66000002,
    "ownerUserId": 66011271,
    "createdTime": "Nov 27, 2024, 12:25:04 PM",
    "modifiedTime": "Sep 10, 2025, 09:21:27 AM",
    "lastModifiedUserId": 66011271,
    "deleted": false,
    "canEdit": false
  },
  "errors": []
}
```

`data` is a **single object** here, not an array and not a `result` wrapper. This differs from the search endpoint ([§6.1](#61-the-v3-envelope)).

#### Response schema

| Parameter | Sub-Parameter | Description | Type |
|  --- | --- | --- | --- |
| `data` |  | Search response containing results and metadata. | Object |
|  | `id` | Unique identifier of the Knowledge Base Category (folder). | String |
|  | `name` | Name of the Category. | String |
|  | `parentId` | ID of the parent Category, if available. | String |
|  | `path` | List of parent Categories. | Array of Strings |
|  | `assetClasses` | Asset classes associated with the Category, such as `KNOWLEDGE_BASE_CONTENT`. | Array of Strings |
|  | `tags` | Tags associated with the Category. | Array of Strings |
|  | `confidential` | Whether the folder is confidential. | Boolean |
|  | `favourite` | Indicates whether the category is marked as a favourite by the user. | Boolean |
|  | `assetMetadata` | Asset metadata associated with the Category. | Object |
|  | `mappingDetails` | Shows the details of mapping between the Knowledge Base Category and the Community categories and topics. | Array of Objects |
|  | `markPublic` | Whether the Category is publicly accessible. | Boolean |
|  | `disableChildSharing` | *(No description supplied on the reference page.)* | Boolean |
|  | `shareConfigs` | Details of Workspaces, Users, and User Groups that have access to the category. | Array of Objects |
|  | `grants` | *(No description supplied on the reference page.)* | Array of Strings |
|  | `clientId` | Client identifier. | Integer |
|  | `ownerUserId` | User ID of the content owner. | Integer |
|  | `createdTime` | Content creation timestamp. | String |
|  | `modifiedTime` | Last modification timestamp. | String |
|  | `lastModifiedUserId` | User ID of the last editor. | Integer |
|  | `deleted` | Indicates whether the content is deleted. | Boolean |
|  | `canEdit` | Indicates whether the current user can edit the content. | Boolean |
| `errors` |  | List of errors encountered during the search operation. Empty if no errors occur. | Array of Objects |


#### 5.1.1 The `mappingDetails` object

| Parameter | Description | Type |
|  --- | --- | --- |
| `mappedProjectId` | Project ID of the Community with which this Category is mapped. | String |
| `mappedCategoryIds` | Community Category IDs with which this Knowledge Base Category is mapped for the respective Community Project ID. | Array of Strings |
| `mappedTopicIds` | Community Topic IDs with which this Knowledge Base Category is mapped for the respective Community Project ID. | Array of Strings |


`mappingDetails` is an **array** because one Knowledge Base Category can be mapped into several Community projects at once — the example above shows a Category mapped into two projects, each with its own Community category ID.

#### 5.1.2 The `shareConfigs` object

| Parameter | Description | Type |
|  --- | --- | --- |
| `shareLevel` | Type of Sprinklr entity the Knowledge Base category is shared with. Supported values: `CLIENT` — shared with a workspace; `CLIENT_GROUP` — shared with a group of workspaces; `USER` — shared with individual users; `USER_GROUP` — shared with a user group. | String |
| `sharedWithIds` | Unique identifiers of the Sprinklr entities the Knowledge Base category is shared with. Each ID must correspond to the entity type set in `shareLevel`. | Array of Strings |


`shareConfigs` is returned as a **complete list of all four share levels**, including the ones with an empty `sharedWithIds`. In the example above only the `USER` level has an entry (`66066902`), and the other three are present but empty. Do not treat the presence of a `shareLevel` entry as evidence that the Category is shared at that level — check `sharedWithIds`.

### 5.2 Search Knowledge Base Categories

Search any existing Knowledge Base Category (folder) within Sprinklr using filtering conditions.

```
POST https://api3.sprinklr.com/{env}/api/v3/folder/search
```

#### Request body

| Parameter | Required/Optional | Description | Type |
|  --- | --- | --- | --- |
| `assetClasses` | Optional | List of asset classes to which the Category is associated. | List of Strings |
| `includeAssetCount` | Optional | If `true`, the asset count is also provided in the API response. | Boolean |
| `query` | Optional | Refers to the search query (if any). | String |
| `scope` | Optional | Refers to the scope of the search. Supported values: `ALL_FOLDERS`, `ONLY_TOP_LEVEL_FOLDERS` | String |
| `start` | Optional | Refers to the pagination information, i.e., the page you want to search. **Default**: `0` | Integer |
| `rows` | Optional | Refers to the number of results you want to display in one page. | Integer |


**Dev Notes**: For fetching all the folders available within Sprinklr's instance, send empty curly brackets { } in the API request.

#### Request

```bash
curl --location 'https://api3.sprinklr.com/{env}/api/v3/folder/search' \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--header 'Authorization: ****** your Access Token}' \
--header 'Key: {Enter your API Key}' \
--data '{
    "assetClasses": [
        "KNOWLEDGE_BASE_CONTENT"
    ],
    "includeAssetCount": false,
    "scope": "ONLY_TOP_LEVEL_FOLDERS",
    "start": 0,
    "rows": 1
}'
```

#### Response

```json
{
  "data": {
    "hasMore": true,
    "result": [
      {
        "id": "6a8fe530e3d386956167d3a5",
        "name": "Ashish Test Zendesk Migration",
        "parentId": "",
        "path": [
          "6a8fe530e3d386956167d3a5"
        ],
        "assetClasses": [
          "KNOWLEDGE_BASE_CONTENT"
        ],
        "tags": [],
        "confidential": false,
        "favourite": false,
        "assetMetadata": {},
        "markPublic": false,
        "disableChildSharing": false,
        "shareConfigs": [
          {
            "shareLevel": "GLOBAL"
          }
        ],
        "grants": [
          "USER/66066168/OWNERSHIP",
          "CLIENT/66000002/OWNERSHIP"
        ],
        "clientId": 66000002,
        "ownerUserId": 66066168,
        "createdTime": "Aug 27, 2026, 07:20:16 AM",
        "modifiedTime": "Aug 27, 2026, 07:20:16 AM",
        "lastModifiedUserId": 66066168,
        "deleted": false,
        "canEdit": false
      }
    ],
    "totalHits": 598,
    "selectAllSupported": false
  },
  "errors": []
}
```

The request asked for `"rows": 1`, so one Category is returned while `totalHits` reports `598` matches and `hasMore` is `true`.

#### Response schema

| Parameter | Sub-Parameter | Description | Type |
|  --- | --- | --- | --- |
| `data` |  | Search response containing results and metadata. | Object |
|  | `hasMore` | Indicates whether more results are available for pagination. | Boolean |
|  | `result` | List of Knowledge Base Categories that match the search criteria. | Array of Objects |
|  | `totalHits` | Total number of matching results for the search query. | Integer |
|  | `selectAllSupported` | *(No description supplied on the reference page.)* | Boolean |
| `errors` |  | List of errors encountered during the search operation. Empty if no errors occur. | Array of Objects |


#### 5.2.1 The `result` object

Each element of `data.result` is a folder object with the following fields.

| Parameter | Description | Type |
|  --- | --- | --- |
| `id` | Unique identifier of the Knowledge Base Category (folder). | String |
| `name` | Name of the Category. | String |
| `parentId` | ID of the parent Category, if available. | String |
| `path` | List of parent Categories. | Array of Strings |
| `assetClasses` | Asset classes associated with the Category, such as `KNOWLEDGE_BASE_CONTENT`. | Array of Strings |
| `tags` | Tags associated with the Category. | Array of Strings |
| `confidential` | Whether the folder is confidential. | Boolean |
| `favourite` | Indicates whether the category is marked as a favourite by the user. | Boolean |
| `assetMetadata` | Asset metadata associated with the Category. | String |
| `markPublic` | Whether the Category is publicly accessible. | Boolean |
| `disableChildSharing` | *(No description supplied on the reference page.)* | Boolean |
| `shareConfigs` | Details of Workspaces, Users, and User Groups that have access to the category. | Array of Objects |
| `grants` | *(No description supplied on the reference page.)* | Array of Strings |
| `clientId` | Client identifier. | Integer |
| `ownerUserId` | User ID of the content owner. | Integer |
| `createdTime` | Content creation timestamp. | String |
| `modifiedTime` | Last modification timestamp. | String |
| `lastModifiedUserId` | User ID of the last editor. | Integer |
| `deleted` | Indicates whether the content is deleted. | Boolean |
| `canEdit` | Indicates whether the current user can edit the content. | Boolean |


**The search `result` object is a subset of the read object.** It carries every field the read endpoint returns **except `description` and `mappingDetails`**. If you need the Community mapping or the description for a Category, you must follow up with `GET /folder?folderId=` for that Category. See [§11](#11-questions-for-the-api-owner) item 7.

## 6. Response format and status codes

### 6.1 The V3 envelope

Both Category endpoints return `data` and `errors`. **The shape of `data` differs between them:**

| Endpoint | `data` shape |
|  --- | --- |
| `GET /folder` | **Single folder object** directly under `data` |
| `POST /folder/search` | **Object** with `result`, `hasMore`, `totalHits`, `selectAllSupported` |


Read envelope:

```json
{
  "data": { "id": "...", "name": "..." },
  "errors": []
}
```

Search envelope:

```json
{
  "data": {
    "hasMore": true,
    "result": [ { } ],
    "totalHits": 598,
    "selectAllSupported": false
  },
  "errors": []
}
```

| Field | Type | Description |
|  --- | --- | --- |
| `data` | Object | Response payload. See the shape table above. |
| `data.result` | Array of Objects | Categories matching the search criteria (search endpoint only) |
| `data.hasMore` | Boolean | Indicates whether more results are available for pagination |
| `data.totalHits` | Integer | Total number of matching results for the search query |
| `data.selectAllSupported` | Boolean | No description is supplied on the reference page |
| `errors` | Array of Objects | List of errors encountered during the operation. Empty if no errors occur. |


> **Naming difference from the Articles API.** Knowledge Base **article** search returns its matches under `data.searchResults`; Knowledge Base **Category** search returns them under `data.result`. If you are writing a shared pagination helper across both APIs, parameterise the results key.


### 6.2 Response codes

These are the status codes declared for both Knowledge Base Category V3 operations in the OpenAPI specification.

| HTTP Code | Scenario | Description |
|  --- | --- | --- |
| `200 OK` | Success | Request completed and the payload was returned |
| `400 Bad Request` | BadRequest | Missing required parameters or invalid parameter combinations |
| `401 Unauthorized` | Unauthorized | Invalid or missing `Authorization` token |
| `403 Forbidden` | Forbidden | The caller lacks permission on the Knowledge Base Category |
| `404 Not Found` | NotFound | No Category matches the supplied `folderId` |


No `500` response is declared. Do not assume `2xx` means the operation fully succeeded — always inspect the `errors` array.

## 8. Supported values

### 8.1 `assetClasses`

`assetClasses` declares what kind of content a Category holds. For Knowledge Base Categories the documented value is:

| Value | Meaning |
|  --- | --- |
| `KNOWLEDGE_BASE_CONTENT` | The Category holds Knowledge Base content. Used in every documented request and response example on both V2 and V3. |


The V2 create/update page adds a hard constraint that applies to the Category you are writing: **if `moduleTypes` is empty, `assetClasses` must contain only `KNOWLEDGE_BASE_CONTENT`**, and at least one of `moduleTypes` or `assetClasses` must be non-empty.

> **Specification conflict.** The `AssetClass` enum in `sprinklr-v3.yaml` contains 486 values, and `KNOWLEDGE_BASE_CONTENT` is **not one of them**. The enum does contain the related `KNOWLEDGE_BASE_CONTENT_VARIABLE`, `KNOWLEDGE_BASE_CUSTOM_TEMPLATE`, `KNOWLEDGE_BASE_VERSION_HISTORY`, and `FOLDER`. Use `KNOWLEDGE_BASE_CONTENT` — it is what every published example on both API versions sends and receives — but be aware that specification-driven client generators will reject it. See [§11](#11-questions-for-the-api-owner) item 12.


### 8.2 `scope`

Controls how deep into the tree the search reaches.

| Value | Behavior |
|  --- | --- |
| `ALL_FOLDERS` | Search across every Category at every level of the hierarchy |
| `ONLY_TOP_LEVEL_FOLDERS` | Restrict results to root Categories — those whose `parentId` is `""` |


`ONLY_TOP_LEVEL_FOLDERS` is what you want when building a category tree lazily: fetch the roots first, then expand each level on demand.

### 8.3 `shareLevel`

Returned inside each `shareConfigs` element.

| Value | Meaning | Status |
|  --- | --- | --- |
| `CLIENT` | Shared with a workspace | Documented |
| `CLIENT_GROUP` | Shared with a group of workspaces | Documented |
| `USER` | Shared with individual users | Documented |
| `USER_GROUP` | Shared with a user group | Documented |
| `GLOBAL` | Appears in the Search Category and V2 save example responses, returned **without** a `sharedWithIds` key | **Undocumented** — see [§11](#11-questions-for-the-api-owner) item 5 |


### 8.4 `grants`

`grants` is an array of strings in the form `ENTITY_TYPE/ENTITY_ID/PERMISSION`. No description or value list is published for this field, but every documented example uses the `OWNERSHIP` permission with `CLIENT` and `USER` entity types:

```json
"grants": [
  "CLIENT/66000002/OWNERSHIP",
  "USER/66011271/OWNERSHIP"
]
```

The order of elements is not stable across examples — `CLIENT` appears first in the read example and `USER` first in the search example. Do not index into this array positionally; parse and match on the entity type. The full set of permission verbs is not documented ([§11](#11-questions-for-the-api-owner) item 13).

### 8.5 `FilterType_Filter`

Applies to the undocumented `filters` and `assetFilters` request fields ([§5.2](#52-search-knowledge-base-categories)). Declared in the specification only.

| Value |
|  --- |
| `FILTER` |
| `LIMIT` |
| `SEARCH` |
| `MATCH_PHRASE_PREFIX` |
| `ADHOC_SEARCH` |
| `EXPRESSION` |
| `GEO_DISTANCE` |
| `ADVANCED_QUERY` |
| `MATCH_NONE` |
| `MATCH_ALL` |


## 9. Use cases

### 9.1 Fetch every Knowledge Base Category in the instance

Send an effectively empty search scoped to Knowledge Base content and page through the results.

```bash
curl --location 'https://api3.sprinklr.com/{env}/api/v3/folder/search' \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--header 'Authorization: ****** your Access Token}' \
--header 'Key: {Enter your API Key}' \
--data '{
    "assetClasses": ["KNOWLEDGE_BASE_CONTENT"],
    "scope": "ALL_FOLDERS",
    "start": 0,
    "rows": 100
}'
```

Increment `start` while `data.hasMore` is `true`. `data.totalHits` tells you the total up front, so you can size the loop: with `598` total hits and `rows: 100`, expect six pages (`start` = 0 through 5).

### 9.2 Build a lazy-loading category tree for a UI

1. Fetch the roots: `scope: "ONLY_TOP_LEVEL_FOLDERS"`.
2. Render them; each has `parentId: ""` and a single-element `path`.
3. When the user expands a node, fetch `scope: "ALL_FOLDERS"` and keep only the results whose `parentId` equals the expanded node's `id`.


Alternatively, fetch `ALL_FOLDERS` once and build the whole tree client-side from `parentId` — with `totalHits` in the hundreds this is usually the cheaper option.

### 9.3 Render a breadcrumb without extra API calls

Do not walk `parentId` upward one request at a time. The `path` array already contains the full ancestry in root-to-leaf order:

```json
"path": ["6715fb6edd12033df96bffe3", "67470fa0725be30f726a868f"]
```

Fetch `ALL_FOLDERS` once, build an ID-to-name map from `data.result`, then map each `path` element through it. One call renders every breadcrumb on the page.

### 9.4 Find a Category by name

```bash
curl --location 'https://api3.sprinklr.com/{env}/api/v3/folder/search' \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--header 'Authorization: ****** your Access Token}' \
--header 'Key: {Enter your API Key}' \
--data '{
    "assetClasses": ["KNOWLEDGE_BASE_CONTENT"],
    "query": "Zendesk Migration",
    "scope": "ALL_FOLDERS",
    "start": 0,
    "rows": 20
}'
```

The fields that `query` matches against are not specified on the reference page. Treat name matching as the expected behavior and verify against your own data before relying on it for anything else.

### 9.5 Resolve a folder ID before creating an article

The Articles API requires `folderMetadata.folderId` on create, and folder IDs are otherwise only obtainable from the browser URL. Search by name to resolve the ID at runtime:

1. `POST /api/v3/folder/search` with `query` set to the Category name and `assetClasses: ["KNOWLEDGE_BASE_CONTENT"]`.
2. Take `data.result[0].id`.
3. Pass it as `folderMetadata.folderId` on `POST /api/v3/knowledgebase/article`.


This removes the hard-coded folder IDs that are the most common source of breakage when a Knowledge Base is restructured.

### 9.6 Report on how many articles sit in each Category

Set `includeAssetCount` to `true` in the search body:

```json
{
  "assetClasses": ["KNOWLEDGE_BASE_CONTENT"],
  "includeAssetCount": true,
  "scope": "ALL_FOLDERS",
  "start": 0,
  "rows": 100
}
```

The reference page states that the asset count "is also provided in the API response" but does not name the field it is returned in, and the documented example uses `includeAssetCount: false` so no count appears. Inspect the raw response in your environment to find the field name ([§11](#11-questions-for-the-api-owner) item 14).

### 9.7 Audit which Categories are publicly visible

Fetch `ALL_FOLDERS` and filter client-side on the access fields:

- `markPublic: true` — the Category is publicly accessible.
- `confidential: true` — the Category is marked confidential.
- `shareConfigs` containing `{"shareLevel": "GLOBAL"}` — shared instance-wide.


The search endpoint returns all three fields on every result, so a single paged sweep produces the whole audit.

### 9.8 Reconcile a Category tree migrated from a legacy Knowledge Base

Category upsert keys off `migrationDetails` on the **V2** write endpoint:

```bash
curl --location 'https://api3.sprinklr.com/{env}/api/v2/folder/save' \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--header 'Authorization: ****** Token}' \
--header 'Key: {API Key}' \
--data '{
    "name": "Billing FAQs",
    "assetClasses": ["KNOWLEDGE_BASE_CONTENT"],
    "migrationDetails": {
        "migratedId": "legacy-cat-4471",
        "migratedFrom": "ZENDESK"
    }
}'
```

Because `migratedId` + `migratedFrom` drive the upsert, the same call is safe to replay: the first run creates the Category, later runs update it. This makes a migration script idempotent without you tracking Sprinklr IDs.

### 9.9 Discover the Community mapping for a Category

`mappingDetails` is returned by the read endpoint only, so resolve the ID first, then read it:

1. `POST /api/v3/folder/search` to find the Category.
2. `GET /api/v3/folder?folderId=<id>`.
3. Read `data.mappingDetails[]` — each element gives one `mappedProjectId` with its `mappedCategoryIds` and `mappedTopicIds`.


A Category can be mapped into several Community projects at once, so always iterate the array rather than reading element `0`.

### 9.10 Determine whether the current user may edit a Category

Every folder object carries `canEdit`. Use it to grey out edit controls before the user attempts a write that would fail with `403`:

```json
"canEdit": false,
"ownerUserId": 66011271,
"grants": ["CLIENT/66000002/OWNERSHIP", "USER/66011271/OWNERSHIP"]
```

Note that `canEdit` is `false` in every published example, including for the owner in the V2 save response — so verify its semantics against your own environment before wiring it to UI state ([§11](#11-questions-for-the-api-owner) item 15).

### 9.11 Detect Categories changed since your last sync

Both endpoints return `modifiedTime` and `lastModifiedUserId`. No server-side date filter is documented for folder search, so:

1. Fetch `ALL_FOLDERS` on a schedule.
2. Compare each result's `modifiedTime` against the high-water mark from your previous run.
3. For changed Categories, follow up with `GET /folder?folderId=` if you need `description` or `mappingDetails`.


The undocumented `rangeConditionList` field in the specification may make this filterable server-side; confirm before designing around it.

## 10. Caveats and best practices

**Terminology**

- *Category* (UI) and *folder* (API) are the same object. The endpoint is `/folder`; the reference pages are titled "Knowledge Base Category."
- The Category API is the addressing layer for the Articles API. A Category ID resolved here is the `folderMetadata.folderId` you send there.


**Request construction**

- Every search parameter is optional, but the request body itself is `required: true` in the specification. Send `{}` to fetch everything rather than omitting the body.
- `folderId` is declared `required: false` in the specification but Required on the reference page. Follow the reference page and always send it.


**Hierarchy**

- `path` is ordered root-to-leaf and its last element is the Category's own `id`. Use it for breadcrumbs instead of walking `parentId`.
- A top-level Category has `parentId: ""` — an empty string, not `null` and not an absent key.
- `ONLY_TOP_LEVEL_FOLDERS` returns exactly the Categories where `parentId` is `""`.


**Pagination**

- Pages are **0-based**; `start` defaults to `0`.
- Use `hasMore` to decide whether to fetch another page and `totalHits` to size the loop up front.
- `rows` has no documented maximum. Test your own upper bound rather than assuming one.


**Values**

- `KNOWLEDGE_BASE_CONTENT` is what every example uses, but it is absent from the specification's 486-value `AssetClass` enum. Generated clients may reject it.
- The `Order_enums` schema backing `sortList.order` declares no values, so sort order strings are not discoverable from the specification.