# Search API V3 — Developer Guide

- **Applies to:** Sprinklr Search API V3 (`/api/v3/search`)
- **V2 API reference:** [Search | Sprinklr Developer Portal](https://dev.sprinklr.com/search)


## 1. Overview

Search helps you quickly find and access Sprinklr entities — assets, campaigns, dashboards, cases, macros, rules, and tasks — saving time locating the data you want to view. The Search API exposes that capability programmatically: you post a filter expression against an entity type and receive a page of matching records plus a cursor for the next page.

Search V3 is **one path parameterized by entity type**, with three operations:

| Operation | Method | Path | operationId |
|  --- | --- | --- | --- |
| Search entities by entity type | `POST` | `/api/v3/search/{apiEntityName}` | `SearchApiV3_searchEntities` |
| Fetch the next page using a cursor | `GET` | `/api/v3/search/{apiEntityName}?cursor={cursorId}` | `SearchApiV3_searchEntitiesByCursor` |
| Search entities using query parameters | `GET` | `/api/v3/search/{apiEntityName}/query` | `SearchApiV3_searchEntitiesByQuery` |


### 1.1 How a search works

1. **`POST`** the filter to `/api/v3/search/{apiEntityName}`. The body carries `filter`, and optionally `timeFilter`, `sorts`, `page`, and field projection.
2. Sprinklr returns `data.results` (the page of records) and `data.cursor` (an opaque token, for example `id=6a062c9faffcb59a7b0a9e30`).
3. **`GET`** `/api/v3/search/{apiEntityName}?cursor={cursorId}` to fetch the next page. Repeat until results are exhausted.


> **The cursor is only valid for five minutes.** If it expires, regenerate one with a fresh `POST` search. Do not persist cursors across jobs or store them in a queue that may be drained slowly.


### 1.2 The filter model

Every search is driven by a single root `filter` object, which is recursive:

```
filter
├── type      → the operator (AND, OR, NOT, IN, EQUALS, GT, LT, EXISTS, …)
├── filters[] → nested Filter objects (used by the boolean operators)
├── key       → the field being filtered (used by the leaf operators)
└── values[]  → the values to match against key
```

Boolean operators (`AND`, `OR`, `NOT`) carry `filters[]`. Leaf operators (`IN`, `EQUALS`, `GT`, and so on) carry `key` and `values`.

`timeFilter` is a **separate, sibling object** to `filter`, not a filter type. It bounds the result set by a single time field with `since` and `until`.

## 2. Base URLs and environments

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

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

So the search resource is:

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

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 Search API calls are authenticated with OAuth 2.0. See [Developer Tools in Sprinklr](https://www.sprinklr.com/help/articles/developer-tools/developer-tools-in-sprinklr/692e8b39f0afa271d18a5929) for API key and secret generation, and the Authorize flow.

| Header | Value | Description | Required on |
|  --- | --- | --- | --- |
| `Authorization` | `******` | Credential used by the API to authenticate a user with the server | All requests |
| `Key` | `{{apiKey}}` | API key that authenticates the application with the server | All requests |
| `Content-Type` | `application/json` | Representation header that determines the type of data present in the request body | `POST` |
| `Accept` | `application/json` | Determines the acceptable response type from the server | All requests |


## 4. Search by entity

**`POST /api/v3/search/{apiEntityName}`**

Fetches data for an entity type using filters. The response carries a cursor you can pass to [Search by cursor](#6-search-by-cursor) to fetch the next set of data.

### 4.1 Path parameter

| Parameter | Required | Type | Description |
|  --- | --- | --- | --- |
| `apiEntityName` | Required | String | The type of entity you are searching for. See [§4.6](#46-supported-entity-types). |


### 4.2 Request body

| Parameter | Sub-parameter | Required | Type | Description |
|  --- | --- | --- | --- | --- |
| `filter` |  | Required | Object | Filter to apply on the entity type |
|  | `type` | Required | String | The filter type. Supported types: `AND`, `OR`, `NOT`, `IN`, `GT`, `GTE`, `LT`, `LTE`, `NIN`, `EQUALS`, `NOT_EQUALS`, `CONTAINS`. See [§4.3](#43-filter-types). |
|  | `filters` | Required | List[Filter] | The list of nested filters. The type of filter must match the provided `key` and `values`. |
|  | `key` | Required | String | The key on which you want to apply the filter |
|  | `values` | Required | List[String] | The value of the key. It must match the key. |
| `timeFilter` |  | Optional — **required for the `MESSAGE` entity type** | Object | The time range filter for the entities |
|  | `key` | Required | String | The key on which the time filter is applied, for example `channelCreatedTime` or `createdTime` |
|  | `since` | Required | Long (epoch ms) | Time since when you want to fetch the results |
|  | `until` | Required | Long (epoch ms) | Time until when you want to fetch the results |
| `sorts` |  | Required | List[Object] | Sorting information |
|  | `key` | Required | String | Key on the basis of which you want to sort the query |
|  | `order` | Optional | String | Sort in ascending or descending order — `ASC` or `DESC` |
| `page` |  | Required | Object | Pagination information |
|  | `start` | Optional | Integer | Specifies the starting index for pagination when retrieving records. Use this parameter to define the position from which the API should begin returning results. |
|  | `size` | Required | Integer | Size of the page to fetch in the response |
|  | `cursor` | Optional | String | Cursor token, as an alternative to the `id` query parameter on the cursor `GET` |
| `q` |  | Optional | String | Keyword to search in the asset |
| `includeCount` |  | Optional | Boolean | When set to `true`, the response includes the total number of records available for the specified entity type. This helps determine the overall dataset size when implementing pagination. **Supported only for the Case entity type.** |
| `includeFields` |  | Optional | List[String] | Fields to include in the search results |
| `excludeFields` |  | Optional | List[String] | Fields to exclude from the search results |
| `userId` |  | Optional | Long | Sprinklr user ID |
| `impersonator` |  | Optional | String | Impersonation identifier |


### 4.3 Filter types

| Filter type | Description |
|  --- | --- |
| `AND` | Similar to boolean AND. Added where more than one filter exists; returns values that meet all filter conditions. |
| `OR` | Similar to boolean OR. Added where more than one filter exists; returns values that meet at least one filter condition. |
| `NOT` | Similar to boolean NOT — returns values that do not match the applied filter conditions. |
| `IN` | Returns resources where the key matches any of the values mentioned in the list of values |
| `NIN` (not in) | Returns resources where the key does not match the values mentioned in the list of values |
| `EQUALS` | Returns resources where the given key is equal to the value(s) mentioned in the list of values |
| `NOT_EQUALS` | Returns resources where the given key is not equal to the value(s) mentioned in the list of values |
| `GT` (greater than) | Returns resources where the key is greater than the value(s) mentioned in the list of values |
| `GTE` (greater than or equal to) | Returns resources where the key is greater than or equal to the value(s) mentioned in the list of values |
| `LT` (less than) | Returns resources where the key is less than the value(s) mentioned in the list of values |
| `LTE` (less than or equal to) | Returns resources where the key is less than or equal to the value(s) mentioned in the list of values |
| `CONTAINS` | Returns resources where the given key contains the values mentioned in the list of values |
| `EXISTS` | Tests for presence of the key. Used by the `COMMENT` entity for `includeReplyOnComments` with a value of `true` or `false`. |
| `SEARCH` | Free-text search on the key. Supported on `MESSAGE.content.text` and `SOCIAL_ASSET.name`. |
| `BETWEEN` | Range match. Supported on the `OUTBOUND_MESSAGE` date and time keys. |


### 4.4 Example — search users in a workspace

```bash
curl --location 'https://api3.sprinklr.com/{env}/api/v3/search/USER' \
--header 'Authorization: ******' \
--header 'Key: {{apiKey}}' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--data '{
    "filter": {
        "type": "AND",
        "filters": [
            {
                "type": "IN",
                "key": "clientAttributes.clientId",
                "values": [
                    "66000002"
                ]
            }
        ]
    },
    "page": {
        "size": 1
    }
}'
```

**Response — `200 OK`**

```json
{
    "data": {
        "results": [
            {
                "schemas": [
                    "urn:ietf:params:scim:schemas:core:2.0:User",
                    "urn:scim:schemas:extension:sprinklrGlobalAttributes:2.0:User",
                    "urn:scim:schemas:extension:sprinklrClientAttributes:2.0:User",
                    "urn:scim:schemas:extension:sprinklrUserAssignmentConfig:2.0:User",
                    "urn:scim:schemas:extension:sprinklrUserVoiceConfig:2.0:User",
                    "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User"
                ],
                "id": "66016438",
                "externalId": "",
                "userName": "nitish.kumar@example.com",
                "name": {
                    "familyName": "Kumar",
                    "givenName": "Nitish"
                },
                "active": true,
                "locale": "en_US",
                "userPermissions": [
                    {
                        "entityType": "FOLDER",
                        "permissions": [
                            "LOCK_UNLOCK",
                            "CREATE",
                            "VIEW"
                        ]
                    }
                ],
                "accessibleClientIds": [],
                "globalAttributes": {},
                "clientAttributes": [],
                "meta": {
                    "resourceType": "User",
                    "createdTime": "2025-01-21 11:58:00",
                    "lastModified": "2026-05-14 19:50:08"
                }
            }
        ],
        "cursor": "id=6a062c9faffcb59a7b0a9e30"
    },
    "errors": []
}
```

`USER` results are **SCIM-shaped**: they declare `schemas`, and attributes are split across `globalAttributes` (partner level), `clientAttributes` (workspace level), and `meta`. Note that `meta.createdTime` and `meta.lastModified` are **formatted date strings** (`"2025-01-21 11:58:00"`), not epoch milliseconds — unlike every other entity in this guide.

### 4.5 Example — search cases with a compound filter

This request combines an `EQUALS` on `caseNumber` with a `GT` on `modifiedTime` under a single `AND`.

```bash
curl --location 'https://api3.sprinklr.com/{env}/api/v3/search/CASE' \
--header 'Authorization: ******' \
--header 'Key: {{apiKey}}' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--data '{
    "filter": {
        "type": "AND",
        "filters": [
            {
                "type": "EQUALS",
                "key": "caseNumber",
                "values": [
                    "6771665"
                ]
            },
            {
                "type": "GT",
                "key": "modifiedTime",
                "values": "1708873480000"
            }
        ]
    },
    "page": {
        "size": 10
    }
}'
```

**Response — `200 OK`** *(custom properties abridged)*

```json
{
    "data": {
        "results": [
            {
                "id": "67bdcfb28d6fd17e6994284d",
                "caseNumber": 6771665,
                "subject": "#6771665 Facebook Avinash Sprinklr",
                "description": "asd",
                "version": 74,
                "status": "New",
                "priority": "Medium",
                "caseType": "Complaint",
                "externalCase": {
                    "id": "500Ig000008xfSOIAY",
                    "caseNumber": "00005264",
                    "channelType": "SALESFORCE",
                    "permalink": "https://sprinklr-2a-dev-ed.develop.my.salesforce.com/500Ig000008xfSOIAY",
                    "createdTime": 1740493057000,
                    "modifiedTime": 1740494158000
                },
                "externalCaseInfo": {
                    "externalCases": []
                },
                "workflow": {
                    "customProperties": {
                        "_c_674db30dd6266d683949a697": ["50"],
                        "_c_65252be5825bb637b9857139": ["147258"],
                        "spr_uc_priority": ["Medium"]
                    },
                    "queues": []
                },
                "channelCustomProperties": [],
                "contact": {
                    "id": "FACEBOOK_110103441967171",
                    "name": "Addy Wayne Enterprise",
                    "channelType": "FACEBOOK",
                    "channelId": "110103441967171",
                    "fromSnUserId": "110103441967171"
                },
                "createdTime": 1740492722458,
                "modifiedTime": 1740838601419,
                "firstMessageId": "ACCOUNT_66005064_1740397109000_FACEBOOK_97_122199892268193678_930538535817351",
                "sentiment": -1,
                "latestProfileMessageAssociatedTime": 1740397109000,
                "conversationId": "240539989134747_122199892268193678",
                "firstMessageAssociatedTime": 1740397109000,
                "latestMessageAssociatedTime": 1740397109000,
                "totalProcessingClockTime": 19442359,
                "allEngagedUsersList": [
                    "66008592"
                ],
                "associatedFanMessageCount": 1,
                "associatedBrandMessageCount": 0,
                "associatedUserBrandMessageCount": 0,
                "deleted": false,
                "latestMessageId": "ACCOUNT_66005064_1740397109000_FACEBOOK_97_122199892268193678_930538535817351",
                "conversationIntentIds": []
            }
        ]
    },
    "errors": []
}
```

**`data.cursor` is absent from this response.** The result set (1 record) was smaller than the requested page size (10), so there is no next page. **Always check for the presence of `cursor` rather than assuming it exists** — see [§8](#8-caveats-and-best-practices).

Search results for `CASE` use the same `Case` schema returned by the Case API. See the [Case API V3 Developer Guide](/guides/developer-guides-v3/case-api-v3-developer-guide) for the full field reference.

### 4.6 Supported entity types

The following entity types are supported: `MESSAGE`, `CASE`, `USER`, `OUTBOUND_MESSAGE`, `CAMPAIGN`, `SUB_CAMPAIGN`, `COMMENT`, `SOCIAL_ASSET`, `TASK`, `AUDIENCE_ACTIVITY`, `PROFILE`, `CUSTOM_FIELD`, `TRANSACTION`.

### 4.7 Supported filter keys by entity type

#### `MESSAGE`

| Key | Supported types |
|  --- | --- |
| `sourceType` | `IN`, `NIN`, `EQUALS`, `NOT_EQUALS` |
| `content.title` | `IN`, `NIN`, `EQUALS`, `NOT_EQUALS`, `CONTAINS` |
| `content.text` | `IN`, `SEARCH` |
| `channelType` | `IN`, `NIN`, `EQUALS`, `NOT_EQUALS` |
| `deleted` | `IN`, `NIN`, `EQUALS`, `NOT_EQUALS` |
| `enrichments.sentiment` | `IN`, `NIN`, `EQUALS`, `NOT_EQUALS`, `GT`, `GTE`, `LT`, `LTE` |
| `workflow.customProperties` / `workflow.spaceWorkflows.customProperties` | `IN`, `NIN`, `EQUALS`, `NOT_EQUALS` |
| `workflow.campaignId` | `IN`, `NIN`, `EQUALS`, `NOT_EQUALS` |
| `postId` | `IN`, `NIN`, `EQUALS`, `NOT_EQUALS` |
| `brandPost` | `EQUALS`, `NOT_EQUALS` |
| `sourceId` | `IN`, `NIN`, `EQUALS`, `NOT_EQUALS` |
| `caseNumber` | `IN`, `EQUALS` |


**`MESSAGE` `timeFilter` keys:** `channelCreatedTime` and `createdTime`, each supporting `LT`, `LTE`, `GT`, `GTE`, `EQUALS`. A `timeFilter` is **required** for `MESSAGE`.

#### `CASE`

| Key | Supported types |
|  --- | --- |
| `id` | `IN`, `NIN`, `EQUALS`, `NOT_EQUALS`, `LT`, `LTE`, `GT`, `GTE` |
| `caseNumber` | `IN`, `NIN`, `EQUALS`, `NOT_EQUALS`, `LT`, `LTE`, `GT`, `GTE` |
| `externalCase.caseNumber` | `IN`, `NIN`, `EQUALS`, `NOT_EQUALS`, `LT`, `LTE`, `GT`, `GTE` |
| `externalCase.channelType` | `EQUALS`, `NOT_EQUALS`, `IN`, `NIN` |
| `modifiedTime` | `IN`, `NIN`, `EQUALS`, `NOT_EQUALS`, `LT`, `LTE`, `GT`, `GTE` |
| `createdTime` | `LT`, `LTE`, `GT`, `GTE`, `EQUALS` |
| `contact.channelId` | `IN`, `NIN`, `EQUALS`, `NOT_EQUALS` |
| `workflow.customProperties.{customPropertyId}` | `IN`, `NIN`, `EQUALS`, `NOT_EQUALS` |
| `workflow.queues.queueId` | `EQUALS`, `NOT_EQUALS`, `IN`, `NIN` |
| `deleted` | `EQUALS`, `NOT_EQUALS` |
| `latestProfileAUMSnCreatedTime` | `LT`, `LTE`, `GT`, `GTE`, `EQUALS` |


#### `USER`

| Key | Supported types |
|  --- | --- |
| `id` | `IN`, `NIN`, `EQUALS`, `NOT_EQUALS`, `LT`, `LTE`, `GT`, `GTE` |
| `userName` | `IN`, `NIN`, `EQUALS`, `NOT_EQUALS` |
| `active` | `IN`, `NIN`, `EQUALS`, `NOT_EQUALS` |
| `clientAttributes.userType` | `IN`, `NIN`, `EQUALS`, `NOT_EQUALS` |
| `clientAttributes.clientId` | `IN`, `NIN`, `EQUALS`, `NOT_EQUALS` |
| `clientAttributes.clientCustomProperties` | `IN`, `NIN`, `EQUALS`, `NOT_EQUALS` |
| `globalAttributes.partnerCustomProperties` | `IN`, `NIN`, `EQUALS`, `NOT_EQUALS` |
| `globalAttributes.federationId` | `IN`, `NIN`, `EQUALS`, `NOT_EQUALS` |
| `globalAttributes.passwordLoginDisabled` | `IN`, `NIN`, `EQUALS`, `NOT_EQUALS` |
| `meta.lastModified` | `LT`, `LTE`, `GT`, `GTE`, `EQUALS` |
| `meta.createdTime` | `LT`, `LTE`, `GT`, `GTE`, `EQUALS` |


#### `PROFILE`

| Key | Supported types |
|  --- | --- |
| `id` | `IN`, `NIN`, `EQUALS`, `NOT_EQUALS`, `LT`, `LTE`, `GT`, `GTE` |
| `channelType` | `IN`, `NIN`, `CONTAINS` |
| `channelId` | `IN`, `NIN`, `CONTAINS` |
| `contact.email` | `IN`, `NIN`, `EQUALS`, `NOT_EQUALS` |
| `contact.phoneNo` | `IN`, `NIN`, `EQUALS`, `NOT_EQUALS` |
| `profileWorkflow.customProperties` | `IN`, `NIN`, `EQUALS`, `NOT_EQUALS` |
| `profileWorkflow.profileSpaceWorkflows.customProperties` | `IN`, `NIN`, `EQUALS`, `NOT_EQUALS` |
| `profileWorkflow.profileLists` | `IN`, `NIN`, `EQUALS`, `NOT_EQUALS` |
| `profileWorkflow.profileSpaceWorkflows.profileLists` | `IN`, `NIN`, `EQUALS`, `NOT_EQUALS` |
| `createdTime` | `LT`, `LTE`, `GT`, `GTE`, `EQUALS` |
| `modifiedTime` | `LT`, `LTE`, `GT`, `GTE`, `EQUALS` |


#### `COMMENT`

| Key | Supported types |
|  --- | --- |
| `entityType` (`UNIVERSAL_CASE` / `MESSAGE_WORKFLOW` / `PROFILE_WORKFLOW`) | `IN`, `NIN`, `EQUALS`, `NOT_EQUALS` |
| `entityId` (case number, message ID, or profile ID) | `IN`, `NIN`, `EQUALS`, `NOT_EQUALS` |
| `includeReplyOnComments` | `EXISTS` → `true` / `false` |
| `createdTime` | `LT`, `LTE`, `GT`, `GTE`, `EQUALS` |


> **`entityId` and `entityType` are required filters when searching for a comment.**


#### `SOCIAL_ASSET`

| Key | Supported types |
|  --- | --- |
| `id` | `IN`, `NIN`, `EQUALS`, `NOT_EQUALS` |
| `assetType` | `IN`, `NIN`, `EQUALS`, `NOT_EQUALS` |
| `assetSource` | `IN`, `NIN`, `EQUALS`, `NOT_EQUALS` |
| `status` | `IN`, `NIN`, `EQUALS`, `NOT_EQUALS` |
| `taxonomy.clientCustomProperties` | `IN`, `NIN`, `EQUALS`, `NOT_EQUALS` |
| `taxonomy.partnerCustomProperties` | `IN`, `NIN`, `EQUALS`, `NOT_EQUALS` |
| `restricted` | `IN`, `NIN`, `EQUALS`, `NOT_EQUALS` |
| `validity.availableFrom` | `IN`, `NIN`, `EQUALS`, `NOT_EQUALS`, `LT`, `LTE`, `GT`, `GTE` |
| `validity.expiryTime` | `IN`, `NIN`, `EQUALS`, `NOT_EQUALS`, `LT`, `LTE`, `GT`, `GTE` |
| `shareConfigs.type` | `IN`, `NIN`, `EQUALS`, `NOT_EQUALS` |
| `templateType` | `IN`, `NIN`, `EQUALS`, `NOT_EQUALS` |
| `channels` | `IN`, `NIN`, `EQUALS`, `NOT_EQUALS` |
| `name` | `SEARCH` |


#### `CAMPAIGN` / `SUB_CAMPAIGN`

| Key | Supported types |
|  --- | --- |
| `id` | `IN`, `NIN`, `EQUALS`, `NOT_EQUALS` |
| `name` | `IN`, `NIN`, `CONTAINS` |
| `description` | `IN`, `NIN`, `CONTAINS` |
| `startDate` | `LT`, `LTE`, `GT`, `GTE`, `EQUALS` |
| `endDate` | `LT`, `LTE`, `GT`, `GTE`, `EQUALS` |
| `tags` | `IN`, `NIN`, `EQUALS`, `NOT_EQUALS` |
| `owner` | `IN`, `NIN`, `EQUALS`, `NOT_EQUALS` |
| `status` | `IN`, `NIN`, `EQUALS`, `NOT_EQUALS` |
| `archived` | `EQUALS`, `NOT_EQUALS` |
| `partnerCustomProperties` | `IN`, `NIN`, `EQUALS`, `NOT_EQUALS` |
| `clientCustomProperties` | `IN`, `NIN`, `EQUALS`, `NOT_EQUALS` |
| `createdTime` | `LT`, `LTE`, `GT`, `GTE`, `EQUALS` |
| `modifiedTime` | `LT`, `LTE`, `GT`, `GTE`, `EQUALS` |


#### `TASK`

| Key | Supported types |
|  --- | --- |
| `id` | `IN`, `NIN`, `EQUALS`, `NOT_EQUALS` |
| `taskStatus` | `IN`, `NIN`, `EQUALS`, `NOT_EQUALS` |
| `taskType` | `IN`, `NIN`, `EQUALS`, `NOT_EQUALS` |
| `title` | `IN`, `NIN` |
| `assetId` | `IN`, `NIN`, `EQUALS`, `NOT_EQUALS` |
| `assetType` | `IN`, `NIN`, `EQUALS`, `NOT_EQUALS` |
| `customProperties` | `IN`, `NIN`, `EQUALS`, `NOT_EQUALS` |
| `dueDate` | `LT`, `LTE`, `GT`, `GTE`, `EQUALS` |
| `createdTime` | `LT`, `LTE`, `GT`, `GTE`, `EQUALS` |
| `modifiedTime` | `LT`, `LTE`, `GT`, `GTE`, `EQUALS` |


#### `AUDIENCE_ACTIVITY`

| Key | Supported types |
|  --- | --- |
| `id` | `IN`, `NIN`, `CONTAINS` |
| `activityTime` | `IN`, `NIN`, `EQUALS`, `NOT_EQUALS`, `LT`, `LTE`, `GT`, `GTE` |
| `accountId` | `IN`, `NIN`, `CONTAINS` |
| `messageId` | `IN`, `NIN`, `CONTAINS` |
| `activityType` | `IN`, `NIN`, `CONTAINS` |
| `channelType` | `IN`, `NIN`, `CONTAINS` |


#### `OUTBOUND_MESSAGE`

| Key | Supported types |
|  --- | --- |
| `templateId` | `IN`, `NIN` |
| `status` | `IN`, `NIN` |
| `searchDetails.customProperties` | `IN`, `NIN` |
| `createdDate` / `createdTime` | `GTE`, `LTE`, `BETWEEN` |
| `scheduleDate` / `scheduleTime` | `GTE`, `LTE`, `BETWEEN` |
| `modifiedDate` / `modifiedTime` | `LTE`, `GTE`, `BETWEEN` |
| `publishedDate` / `publishedTime` | `GTE`, `LTE`, `BETWEEN` |


### 4.8 Filtering on custom properties

Custom properties are addressed by appending the custom field ID to the property path:

```json
"filters": [
    {
        "type": "IN",
        "key": "workflow.customProperties.5e4e5f3954e68b2a475c05b6",
        "values": [
            "Andhra Pradesh"
        ]
    }
]
```

Resolve custom field IDs by searching the `customField` entity — see [§4.9](#49-example--search-custom-fields).

### 4.9 Example — search custom fields

Use this to discover the custom field IDs you need for the custom-property filters above.

```bash
curl --location 'https://api3.sprinklr.com/{env}/api/v3/search/customField' \
--header 'Authorization: ******' \
--header 'Key: {{apiKey}}' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--data '{
    "filter": {
        "type": "AND",
        "filters": [
            {
                "type": "LT",
                "key": "createdTime",
                "values": "1708873480000"
            }
        ]
    },
    "page": {
        "size": 10
    }
}'
```

**Response — `200 OK`** *(one result shown)*

```json
{
    "data": {
        "results": [
            {
                "id": "65da2e3013e1fe4109b8c77d",
                "fieldName": "_c_65da2e3013e1fe4109b8c779",
                "label": "s12",
                "assetTypes": [
                    "USER"
                ],
                "type": "PICKLIST",
                "values": [
                    { "key": "a", "label": "a" },
                    { "key": "b", "label": "b" },
                    { "key": "c", "label": "c" }
                ],
                "enabled": true,
                "visibility": {
                    "globallyVisible": true,
                    "visibilityConfig": []
                },
                "permissions": [],
                "optionType": "GENERAL",
                "accessibleClientIds": [
                    66000004,
                    66000010,
                    66000050
                ],
                "createdTime": 1708797488387,
                "modifiedTime": 1779195803742
            }
        ],
        "cursor": "id=6a0cca9fa62597c1d8643b11"
    },
    "errors": []
}
```

### 4.10 Example — search comments on a case

```bash
curl --location 'https://api3.sprinklr.com/{env}/api/v3/search/COMMENT' \
--header 'Authorization: ******' \
--header 'Key: {{apiKey}}' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--data '{
    "filter": {
        "type": "AND",
        "filters": [
            {
                "type": "IN",
                "key": "entityType",
                "values": [
                    "UNIVERSAL_CASE"
                ]
            },
            {
                "type": "IN",
                "key": "entityId",
                "values": [
                    "829"
                ]
            },
            {
                "type": "EXISTS",
                "key": "includeReplyOnComments",
                "values": [
                    "false"
                ]
            }
        ]
    },
    "page": {
        "size": 2
    },
    "sorts": [
        {
            "key": "createdTime",
            "order": "ASC"
        }
    ]
}'
```

**Response — `200 OK`**

```json
{
    "data": {
        "results": [
            {
                "id": "64e5febee06bd348943340bc",
                "text": "<p class=\"export-block__parent\">note1</p>",
                "commentingUser": 66000101,
                "createdTime": 1692794558032,
                "modifiedTime": 1692794558032,
                "entityType": "CASE",
                "entityId": "829",
                "conversationId": "19:6d1c3966-4da1-4eb6-9ee7-b07d72756abf_b1fc70a6-3878-4635-9462-29564f60fa6d@unq.gbl.spaces",
                "externalComment": {
                    "channelType": "MICROSOFT_TEAMS"
                }
            },
            {
                "id": "64e5fec2e06bd3489433420f",
                "text": "<p class=\"export-block__parent\">note 100</p>",
                "commentingUser": 66000101,
                "createdTime": 1692794562227,
                "modifiedTime": 1692794562227,
                "entityType": "CASE",
                "entityId": "829",
                "conversationId": "19:6d1c3966-4da1-4eb6-9ee7-b07d72756abf_b1fc70a6-3878-4635-9462-29564f60fa6d@unq.gbl.spaces",
                "externalComment": {
                    "channelType": "MICROSOFT_TEAMS"
                }
            }
        ],
        "cursor": "id=6a06339daffcb59a7b0aa53f"
    },
    "errors": []
}
```

Two behaviors worth planning for:

1. **The request filters on `entityType: "UNIVERSAL_CASE"`, but the response returns `entityType: "CASE"`.** The filter value and the response value are not the same vocabulary. Do not round-trip the response value back into a filter.
2. **`text` is HTML**, not plain text. Sanitize it before rendering.


### 4.11 Example — search profiles with a sort

```bash
curl --location 'https://api3.sprinklr.com/{env}/api/v3/search/PROFILE' \
--header 'Authorization: ******' \
--header 'Key: {{apiKey}}' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--data '{
    "filter": {
        "type": "AND",
        "filters": [
            {
                "type": "IN",
                "key": "channelType",
                "values": [
                    "WHATSAPP_BUSINESS"
                ]
            }
        ]
    },
    "sorts": [
        {
            "key": "modifiedTime",
            "order": "DESC"
        }
    ],
    "page": {
        "size": 10
    }
}'
```

**Response — `200 OK`** *(one result, abridged)*

```json
{
    "data": {
        "results": [
            {
                "restricted": false,
                "id": "652425977e759fa6379f4085",
                "contact": {
                    "firstName": "Savy",
                    "lastName": "Sharma",
                    "fullName": "Savy Sharma",
                    "email": "savy.sharma@example.com",
                    "phoneNo": "+919876543210",
                    "website": [
                        "https://www.example.com/"
                    ],
                    "phoneDetails": [
                        { "phoneNo": "+919876543210" }
                    ],
                    "emailDetails": []
                },
                "profileWorkflow": {
                    "profileLists": [
                        332,
                        333,
                        331188,
                        1
                    ],
                    "customProperties": {
                        "_c_64cbf526fd8b0e259d72470a": ["20"],
                        "_c_64e8a89a9deb2c0c668e7a57": [],
                        "last_updated_by_user": ["66000015"]
                    },
                    "profileSpaceWorkflows": [
                        {
                            "profileLists": [1, 5],
                            "tags": [],
                            "spaceId": "66000002"
                        }
                    ]
                },
                "createdTime": 1696867735563,
                "modifiedTime": 1778789065730
            }
        ],
        "cursor": "id=6a0631b9affcb59a7b0aa4d1"
    },
    "errors": []
}
```

Note that `customProperties` can contain **empty arrays** for fields that exist but have no value set. Handle that case rather than assuming a non-empty list.

Profile records also carry the résumé-style arrays `works`, `profiles`, `demographics`, `organizations`, `certificates`, `recommendations`, `educations`, `languages`, `skills`, `courses`, `honors`, `patents`, `projects`, `publications`, `testScores`, and `voluntaryExp`. Use `includeFields` or `excludeFields` to keep responses small if you do not need them.

## 6. Search by cursor

**`GET /api/v3/search/{apiEntityName}?cursor={cursorId}`**

Fetches the next set of available data. Before using this call, run a [Search by entity](#4-search-by-entity) `POST` to receive the cursor in the response.

### 6.1 Parameters

| Parameter | In | Required | Type | Description |
|  --- | --- | --- | --- | --- |
| `apiEntityName` | Path | Required | String | The type of entity you are searching for. Must match the entity type used in the originating `POST`. |
| `cursor` | Query | Required | String | Cursor ID for pagination. This is the value you received as `cursor` in the `POST` response. Example: `cursor=5dd2699aaf47f50001d3299d` |


> **The cursor ID is only valid for five minutes.** If it expires, regenerate a new one with a fresh Search by entity call.


### 6.2 Reading the cursor value

The `Search by Entity` API response returns the cursor as a bare query fragment with `id=` prefix:

```json
"cursor": "id=6a062c9faffcb59a7b0a9e30"
```

**Extract the token, not the prefix.** Strip any leading `id=`.

### 6.3 Example

```bash
curl --location 'https://api3.sprinklr.com/{env}/api/v3/search/USER?cursor=6a062b7daffcb59a7b0a9ccb' \
--header 'Authorization: ******' \
--header 'Key: {{apiKey}}' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json'
```

**Response — `200 OK`**

The response has the identical envelope and record shape as the originating `POST`, with a **new** `cursor` for the following page:

```json
{
    "data": {
        "results": [
            {
                "schemas": [
                    "urn:ietf:params:scim:schemas:core:2.0:User",
                    "urn:scim:schemas:extension:sprinklrGlobalAttributes:2.0:User",
                    "urn:scim:schemas:extension:sprinklrClientAttributes:2.0:User"
                ],
                "id": "66070390",
                "userName": "user@example.com",
                "active": true,
                "locale": "en_US",
                "meta": {
                    "resourceType": "User"
                }
            }
        ],
        "cursor": "id=6a062c41affcb59a7b0a9dc6"
    },
    "errors": []
}
```

**Each page returns a fresh cursor.** Always use the cursor from the most recent response, never the original one.

### 6.4 Pagination loop

```javascript
const entity = 'USER';

let response = await post(`/api/v3/search/${entity}`, {
  filter: {
    type: 'AND',
    filters: [
      { type: 'IN', key: 'clientAttributes.clientId', values: ['66000002'] }
    ]
  },
  sorts: [{ key: 'meta.createdTime', order: 'ASC' }],
  page: { size: 100 }
});

const all = [];

// Accepts a bare token, "id=<token>", "cursor=<token>", or a full URL.
function extractCursorToken(cursor) {
  const withoutUrl = cursor.includes('?') ? cursor.slice(cursor.indexOf('?') + 1) : cursor;
  return withoutUrl.replace(/^(id|cursor)=/, '');
}

while (true) {
  if (response.errors && response.errors.length) {
    handleErrors(response.errors);
    break;
  }

  const data = response.data;
  if (!data || !data.results || data.results.length === 0) break;

  all.push(...data.results);

  // No cursor means there is no next page.
  if (!data.cursor) break;

  // The cursor expires after five minutes — do not queue it for later.
  const token = encodeURIComponent(extractCursorToken(data.cursor));

  // IN-12941 renames the parameter to `cursor`; the spec and collection still
  // show `id`. Send `cursor`, fall back to `id` until the build is confirmed.
  response = await get(`/api/v3/search/${entity}?cursor=${token}`);
  if (response.errors && response.errors.length) {
    response = await get(`/api/v3/search/${entity}?id=${token}`);
  }
}
```

## 7. Response format and status codes

### 7.1 The V3 envelope

```json
{
  "data": {
    "results": [ ... ],
    "cursor": "id=6a062c9faffcb59a7b0a9e30",
    "count": 42
  },
  "errors": []
}
```

| Field | Type | Description |
|  --- | --- | --- |
| `data.results` | Array | List of assets returned as part of the search. The record shape is the entity's own schema — `Case` for `CASE`, the SCIM user for `USER`, and so on. |
| `data.cursor` | String | Link to get the next set of results. **Absent when there is no next page.** Valid for five minutes. |
| `data.count` | Long | Total number of records available for the entity type. Returned only when `includeCount` is `true`, and supported only for the Case entity type. |
| `errors` | Array[Error] | Array of error objects. Empty when there were no errors. |


### 7.2 The error shape

On an error, the response contains **no `data` key at all** — only `errors`:

```json
{
    "errors": [
        {
            "id": "6a0633f7affcb59a7b0aa552",
            "code": 400,
            "message": "Invalid entity: LISTENING_TOPIC_BACKFILL"
        }
    ]
}
```

| Field | Type | Description |
|  --- | --- | --- |
| `id` | String | Request identifier. Quote this when raising a support ticket. |
| `code` | Integer | Error code, mirroring the HTTP status |
| `message` | String | Human-readable description of the failure |


### 7.3 Response codes

| HTTP code | Scenario | Description |
|  --- | --- | --- |
| `200 OK` | Success | Results returned, with a cursor when a next page exists |
| `400 Bad Request` | Invalid request | Unknown entity type, malformed filter, or an unsupported key/type combination. Also observed for transient backend failures, for example `"Please try again, cluster: test-inbound1-es"`. |
| `401 Unauthorized` | Authentication failed | Invalid or missing `Authorization` token |
| `403 Forbidden` | Insufficient permissions | The caller lacks read permission on the entity type |
| `404 Not Found` | Not found | The requested resource does not exist |


> **`400` is overloaded.** A `400` can mean either a permanent contract violation (`"Invalid entity: …"`) or a transient backend condition (`"Please try again, cluster: …"`). Inspect `errors[].message` before deciding whether to retry. Retrying an `"Invalid entity"` failure will never succeed.


## 8. V2 → V3 migration

### 8.1 Endpoint mapping

| V2 | V3 |
|  --- | --- |
| `POST /api/v2/search/{entityType}` (Search by Entity) | `POST /api/v3/search/{apiEntityName}` |
| `GET /api/v2/search/{entityType}?id={cursorId}` (Search by Cursor) | `GET /api/v3/search/{apiEntityName}?cursor={cursorId}` — **parameter renamed** |


## 9. Use cases

### 9.1 Export all cases modified since the last sync

Filter `CASE` on `modifiedTime` with `GT` and the timestamp of your previous run, sort by `modifiedTime` `ASC`, and page with the cursor. Sorting ascending on the same key you filter on keeps the watermark monotonic, so a mid-run failure can resume from the last record processed rather than restarting.

### 9.2 Discover custom field IDs before building a filter

Custom properties are keyed by generated IDs such as `_c_65da2e3013e1fe4109b8c779`, which you cannot guess. Search the `customField` entity ([§4.9](#49-example--search-custom-fields)), map `label` → `fieldName`, and cache the result. Then filter with `workflow.customProperties.{fieldName}`.

### 9.3 Pull the agent notes on a case

Search `COMMENT` with `entityType` and `entityId` — both are **required** for comment searches — sorted by `createdTime` `ASC` to get the notes in chronological order ([§4.10](#410-example--search-comments-on-a-case)). Remember that `text` is HTML and that the response `entityType` (`CASE`) differs from the filter value (`UNIVERSAL_CASE`).

### 9.4 Reconcile a user directory

Filter `USER` on `clientAttributes.clientId` for the workspace, page through with the cursor, and compare `userName` and `active` against your IdP. Use `excludeFields` to drop `userPermissions` — it is by far the largest part of a user record and is rarely needed for a directory reconciliation.

### 9.5 Find profiles on a specific channel

Filter `PROFILE` on `channelType` with `IN`, sorted by `modifiedTime` `DESC` to surface the most recently active profiles first ([§4.11](#411-example--search-profiles-with-a-sort)). Add `contact.email` or `contact.phoneNo` filters to resolve a specific customer.

### 9.6 Search messages within a time window

`MESSAGE` searches **require** a `timeFilter`. Set `key` to `createdTime` or `channelCreatedTime` and bound it with `since` and `until`:

```json
{
    "filter": {
        "type": "AND",
        "filters": [
            {
                "type": "NOT_EQUALS",
                "key": "brandPost",
                "values": ["true"]
            }
        ]
    },
    "timeFilter": {
        "key": "createdTime",
        "since": 1775939449000,
        "until": 1778790649000
    },
    "page": {
        "size": 10
    }
}
```

This example filters to inbound (non-brand) messages only. Narrow the window rather than widening the page size when a message search is slow.

### 9.7 Size a dataset before exporting it

For `CASE` searches only, set `includeCount: true` to have the response report the total number of matching records. Use it to decide whether to run an export at all, to estimate runtime, and to validate that your page loop retrieved everything.

*All JSON payloads in this guide are illustrative examples. Email addresses, user names, and phone numbers have been replaced with placeholder values. They are not real customer data and are not guaranteed production responses. All credentials are placeholders (`******`, `{{apiKey}}`) and must never be committed or logged.*