# Engagement Dashboards (Stream) API V3 — Developer Guide

**Applies to:** Sprinklr Engagement Dashboard Stream APIs V3 — `POST /api/v3/streams/feed` (Inbound Column Stream) and the Stream Cursor API
**V2 API reference:** [Engagement Dashboard](https://dev.sprinklr.com/engagement-dashboard) · [Inbound Column Stream](https://dev.sprinklr.com/inbound-column-stream) · [Engagement Dashboards — Blueprint](https://dev.sprinklr.com/engagement-dashboards-blueprint)

## 1. Overview

The Engagement Dashboard **Stream** API lets you fetch the data flowing through a particular column configured in an Engagement dashboard, care console, or agent console. The column can be outbound or inbound. Once a column is created in Sprinklr it is assigned a unique **stream ID**, which is the key you pass to the API to extract data[doc:turn3doc4].

Two concepts underpin every call[doc:turn3doc1]:

- **Column** — engagement dashboard content is organized and displayed as columns. Each column's content depends on its configuration and selected source. Sprinklr supports two column types: **outbound** and **inbound**.
- **Stream** — the data that resides within a column. Streams are fetched using the unique stream ID associated with that column.


Useful properties of dashboards worth knowing before you integrate[doc:turn3doc1]:

- Engagement dashboards are distinguishable by one or more columns of data.
- Every engagement dashboard has a unique name and id.
- Dashboards offer loosely coupled integration — a change to a column filter is reflected in the exported dashboard data.
- **Dashboard and column ids are consistent across Production and Sandbox environments.**


### 1.1 Operations covered by this guide

| Operation | Method and path | operationId | Declared 200 schema |
|  --- | --- | --- | --- |
| Fetch stream feed by stream id (Inbound Column Stream) | `POST /api/v3/streams/feed` | `StreamApiV3_fetchFeed` | `APIResponse` |
| Resume stream feed from cursor (Stream Cursor API) | `GET /api/v3/streams/feed` *(spec)* — documented as `GET /api/v3/stream/cursor/{nextPageCursor}` | `StreamApiV3_fetchFeedByCursor` | `APIResponse` |


For more details refer to the [Stream V3 API Reference](/apis/sprinklr-v3/stream).

### 1.2 The dashboard-to-stream workflow

Reading stream data is the last step of a four-stage flow[doc:turn3doc1]:

1. **Configure the dashboard.** Create an engagement dashboard from the Modern Engagement module, then add a column based on the desired source.
2. **Fetch dashboard details.** Use *Fetch All Engagement Dashboards* to list every dashboard in your environment, or *Fetch Engagement Dashboard by Name* for a specific one. **Dev Note:** the dashboard name must be URL-encoded[doc:turn3doc1].
3. **Extract the stream id.** In the Read Dashboard response, look for `columnOrder` (the list of all stream ids for the dashboard's columns) and `columns` (an array whose individual entries carry the stream id under the `id` field)[doc:turn3doc1].
4. **Fetch the column stream.** Call `POST /api/v3/streams/feed` with that stream id, then page with the Stream Cursor API.


```
Read Dashboard API  ──►  streamId
        │
        ▼
POST /api/v3/streams/feed?streamId={streamId}
        │  { start, rows, sortField, sortOrder }
        ▼
{ data: { entities: [...], hasMore: true, nextPageCursor: "6a86…" }, errors: [] }
        │
        ▼  while hasMore
Stream Cursor API  ──►  next page of entities
```

### 1.3 Response envelope shape

Both operations return the standard Sprinklr envelope with a stream-specific `data` object:

```
{
  "data": {
    "entities":       [ … ],     // array of message objects
    "hasMore":        true,      // whether more data is available
    "nextPageCursor": "6a86cade6904d47b4502c9ee"   // present only when hasMore is true
  },
  "errors": []
}
```

`nextPageCursor` appears **only when more data is available**. In the documented cursor-call example, `hasMore` is `false` and `nextPageCursor` is absent — that is the signal to stop paging.

## 2. Base URLs and environments

```
https://api3.sprinklr.com/{env}/api/v3/streams/feed?streamId={streamId}
https://api3.sprinklr.com/{env}/api/v3/stream/cursor/{nextPageCursor}
```

`env` — default `prod`. Allowed values:

`prod`, `prod0`, `prod2`, `prod3`, `prod4`, `prod5`, `prod6`, `prod8`, `prod11`, `prod12`, `prod15`, `prod16`, `prod17`, `prod18`, `prod19`, `prod21`, `prod25`

The API was QA-verified in **QA6** and additionally **verified in Prod0** on 2026-08-05.

> **Note.** Because dashboard and column ids are consistent across Production and Sandbox[doc:turn3doc1], a stream id captured in Sandbox will generally address the same column in Production. Environment selection is made purely through `{env}`.


## 3. Authentication and common headers

All 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 | Purpose | 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` | Declares the request body media type | `POST`, `PUT`, `PATCH` |
| `Accept` | `application/json` | Determines the acceptable response type from the server | All requests |


## 4. Read operations

All payloads below are **illustrative**. Credentials are placeholders; identifiers are reproduced from the supplied operation documents.

### 4.1 Fetch inbound column stream — `POST /api/v3/streams/feed`

Fetches stream data for inbound columns available in engagement dashboards with Sprinklr Modern Engagement.

> **Note.** If more data is available, the response carries a cursor as `nextPageCursor`, which you pass to the Stream Cursor API to fetch the next set of data.


**Query parameter**

| Parameter | Required/Optional | Description | Type |
|  --- | --- | --- | --- |
| `streamId` | Required | Stream id of the inbound column to fetch data from. You can fetch the stream id using the Read Dashboard API. | String |


> **Discrepancy.** The operation document marks `streamId` as Required and shows it as a query parameter (`/streams/feed?streamId`). The spec declares **no parameters whatsoever** on `POST /streams/feed`. See §10, item 2.


**Request body** — required, media type `application/json`, schema `StreamRequestDTO`.

Fields documented in the operation document:

| Parameter | Required/Optional | Description | Type |
|  --- | --- | --- | --- |
| `start` | Required | Starting index for retrieving records; used as the pagination offset. Example: `0`. | Integer |
| `rows` | Required | Number of records to be returned in the response. Example: `2`. | Integer |
| `sortField` | Required | Field name used to sort the results. Example: `CHANNEL_CREATED_TIME`. | String |
| `sortOrder` | Required | Sorting direction. `ASC` for ascending, `DESC` for descending. Example: `ASC`. | String |


Additional fields present in the `StreamRequestDTO` schema but **not** covered by the operation document:

| Property | Type | Description (from the spec) |
|  --- | --- | --- |
| `sinceTime` | integer (int64) | *No description in the spec.* Time-window lower bound, epoch ms. |
| `untilTime` | integer (int64) | *No description in the spec.* Time-window upper bound, epoch ms. |
| `sinceId` | string | *No description in the spec.* |
| `untilId` | string | *No description in the spec.* |
| `start` | integer (int32) | Number of records to skip. `0` for none. |
| `rows` | integer (int32) | Number of records to return. |
| `sortField` | `SortKey` | Field to sort the data. |
| `sortOrder` | `Order` | Order in which data has to be sorted. |


**Example — request**

```bash
curl --location 'https://api3.sprinklr.com/{env}/api/v3/streams/feed?streamId={streamId}' \
--header 'Authorization: Bearer {{accessToken}}' \
--header 'Key: {{apiKey}}' \
--header 'Content-Type: application/json' \
--header 'accept: application/json' \
-d '{
  "start": 0,
  "rows": 2,
  "sortField": "CHANNEL_CREATED_TIME",
  "sortOrder": "ASC"
}'
```

**Example — response** (abridged; one of two `entities` shown in full)

```json
{
  "data": {
    "entities": [
      {
        "sourceType": "ACCOUNT",
        "sourceId": 66073052,
        "content": {
          "text": "@__sri_hari_77_ replying to a comment",
          "isRichText": false
        },
        "channelMessageId": "18030998506826658",
        "channelType": "INSTAGRAM",
        "accountType": "INSTAGRAM",
        "channelCreatedTime": 1704988068000,
        "senderProfile": {
          "name": "Restricted data",
          "channelType": "INSTAGRAM",
          "channelId": "Restricted data",
          "permalink": "Restricted data",
          "avatarUrl": "Restricted data",
          "followers": 7,
          "following": 2,
          "username": "Restricted data",
          "unSubscribed": false,
          "deleted": false,
          "snCreatedTime": 0,
          "snModifiedTime": 0,
          "statusCount": 355,
          "accountSpecificInfos": [
            { "accountId": 600000341 },
            { "accountId": 66073052 }
          ]
        },
        "receiverProfile": {
          "name": "Sri Hari",
          "channelType": "INSTAGRAM",
          "channelId": "__sri_hari_77_",
          "permalink": "https://scontent.cdninstagram.com/v/t51.2885-19/…",
          "avatarUrl": "https://instagram.com/__sri_hari_77_",
          "followers": 187,
          "following": 0,
          "username": "__sri_hari_77_",
          "verified": false,
          "unSubscribed": false,
          "deleted": false,
          "snCreatedTime": 0,
          "snModifiedTime": 0,
          "statusCount": 0,
          "accountSpecificInfos": []
        },
        "mentionedProfiles": [
          {
            "channelType": "INSTAGRAM",
            "channelId": "__sri_hari_77_",
            "followers": 0,
            "following": 0,
            "username": "__sri_hari_77_",
            "deleted": false,
            "snCreatedTime": 0,
            "snModifiedTime": 0,
            "statusCount": 0,
            "accountSpecificInfos": []
          }
        ],
        "permalink": "https://www.instagram.com/p/C19ypmoSOqz/c/18029851801830230/r/18030998506826658",
        "language": "en",
        "messageId": "ACCOUNT_66073052_1704988068000_INSTAGRAM_306_18030998506826658",
        "postId": 130996273,
        "brandPost": true,
        "createdTime": 1781274222523,
        "modifiedTime": 1781274223960,
        "textEntities": {
          "message": [
            { "indices": [0, 15], "screenName": "__sri_hari_77_" }
          ]
        },
        "insights": {},
        "workflow": { "campaignId": "66000002_2" },
        "enrichments": {},
        "conversationId": "3277998865097747123_62647274115",
        "parentMessageId": "ACCOUNT_66073052_1704987978000_INSTAGRAM_37_18029851801830230",
        "autoImported": true,
        "autoResponse": false
      }
    ],
    "hasMore": true,
    "nextPageCursor": "6a86cade6904d47b4502c9ee"
  },
  "errors": []
}
```

**Response parameters**

| Parameter | Description | Type |
|  --- | --- | --- |
| `nextPageCursor` | The `nextPageCursor` you get in the response will be used in the Stream Cursor API to fetch the next set of data. | String |


The `entities` array carries **MESSAGE** objects. Refer to the MESSAGE response definitions on the developer portal for the full field reference — the operation documents link to it rather than reproducing it. §8.2 lists the fields observed in the supplied examples.

### 4.2 Stream Cursor API — fetch the next page

Fetches the next set of available data.

**Documented endpoint**

```
GET https://api3.sprinklr.com/{env}/api/v3/stream/cursor/{nextPageCursor}
```

> **Note.** The `{nextPageCursor}` above is the one you receive in the response from either the Outbound or Inbound API. Before using this API call you must first call the Outbound/Inbound API to receive a `nextPageCursor` in the response.


**Path parameter**

| Parameter | Required/Optional | Description | Type |
|  --- | --- | --- | --- |
| `nextPageCursor` | Required | The cursor you received in the response of the Outbound/Inbound API call. Example: `"nextPageCursor": "602cc4ngh67dg345tnb703fe"` | String |


**Request parameters** (as tabulated in the operation document)

| Parameter | Required/Optional | Description | Type |
|  --- | --- | --- | --- |
| `nextPageCursor` | Required | Specifies the cursor used to retrieve the next page of results from the stream. | String |
| `Cookie` | Optional | Specifies the session cookie associated with the API request. Example: `JSESSIONID={session-id}` | String |


**Example — request**

```bash
curl --location 'https://api3.sprinklr.com/{env}/api/v3/stream/cursor/{nextPageCursor}' \
--header 'Authorization: Bearer {{accessToken}}' \
--header 'Key: {{apiKey}}' \
--header 'Content-Type: application/json' \
--header 'accept: application/json'
```

**Example — response** (abridged)

```json
{
  "data": {
    "entities": [
      {
        "sourceType": "ACCOUNT",
        "sourceId": 66073052,
        "content": {
          "text": "@__sri_hari_77_ replying to the comment",
          "isRichText": false
        },
        "channelMessageId": "17896145114952536",
        "channelType": "INSTAGRAM",
        "accountType": "INSTAGRAM",
        "channelCreatedTime": 1705996114000,
        "permalink": "https://www.instagram.com/p/C2b1gW1AUt0/c/18115322911346405/r/17896145114952536",
        "language": "en",
        "messageId": "ACCOUNT_66073052_1705996114000_INSTAGRAM_306_17896145114952536",
        "postId": 130996277,
        "brandPost": true,
        "createdTime": 1781274226146,
        "modifiedTime": 1781274229063,
        "insights": {},
        "workflow": { "campaignId": "66000002_2" },
        "enrichments": {},
        "conversationId": "3286455673096850292_62647274115",
        "parentMessageId": "ACCOUNT_66073052_1705996091000_INSTAGRAM_37_18115322911346405",
        "autoImported": true,
        "autoResponse": false
      }
    ],
    "hasMore": false
  },
  "errors": []
}
```

> **Note.** You will receive another cursor if more data is available to fetch. Use the id given in `nextPageCursor` to make another GET call. In the example above `hasMore` is `false` and no `nextPageCursor` is returned — paging is complete.


### 4.3 Paging pattern

```
1. POST /api/v3/streams/feed?streamId={streamId}
     body: { start: 0, rows: N, sortField: …, sortOrder: … }
2. Read data.hasMore
     false → done
     true  → take data.nextPageCursor
3. GET  the Stream Cursor API with that cursor
4. Repeat from step 2 until hasMore is false
```

Do **not** advance `start` across cursor calls — `start` is the offset for the *initial* feed request only; subsequent pages are addressed entirely by the cursor.

## 5. Response format and status codes

### 5.1 Success envelope

Both operations declare `200` with schema **`APIResponse`**:

```
APIResponse = {
  data:     object,
  errors:   Error[],
  metadata: ResponseMetadata
}
```

`data` is declared as an untyped `object`, so the `entities` / `hasMore` / `nextPageCursor` structure shown in §1.3 and §4 is **not** described by any schema in `sprinklr-v3.yaml`. It is documented only by the worked examples. The `metadata` member of `APIResponse` does not appear in any documented example response. See §10, item 6.

### 5.2 Error responses

Both operations declare the shared error responses:

| Status | Name | Body |
|  --- | --- | --- |
| 400 | Bad Request | `ErrorResponse` |
| 401 | Unauthorized | `ErrorResponse` |
| 403 | Forbidden | `ErrorResponse` |
| 404 | Not Found | `ErrorResponse` |


`ErrorResponse` is `{data: object|null, errors: Error[], metadata: object}`.

```json
{
  "data": null,
  "errors": [
    {
      "id": "5f9b2c1a4e6d7b8c9a0f1e2d",
      "code": 4004,
      "message": "stream.not.found"
    }
  ],
  "metadata": {}
}
```

`Error` requires `id` (24-character hexadecimal ObjectId), `code` (integer), and `message` (dotted error key, e.g. `account.not.found`).

Because a successful response also carries an `errors` array (empty on success), always check both the HTTP status **and** that `errors` is empty before consuming `data`.

## 6. V2 → V3 migration

### 6.1 Endpoint mapping

| V2 | V3 | Notes |
|  --- | --- | --- |
| `POST /api/v2/stream/{streamId}/feed`[doc:turn3doc2][doc:turn3doc6] | `POST /api/v3/streams/feed?streamId={streamId}` | **Breaking.** The resource segment is pluralized (`stream` → `streams`) and `streamId` moves from a **path segment** to a **query parameter**. |
| `GET /api/v2/stream/cursor/{cursorId}`[doc:turn3doc6] | `GET /api/v3/stream/cursor/{nextPageCursor}` *(per the operation document)* | Path shape preserved; the parameter is named `nextPageCursor` in the V3 document versus `cursorId` in the V2 reference. **Not present in the OpenAPI document** — see §10, item 1. |


### 6.2 Field mapping

| V2 | V3 | Change |
|  --- | --- | --- |
| `streamId` (path segment) | `streamId` (query parameter) | **Breaking** — request URLs must be rebuilt. |
| `start` — Required, Integer. "The start offset to tell the client where to begin pulling data. The default is 0."[doc:turn3doc2] | `start` — Required, Integer. "Specifies the starting index for retrieving records and is used as the pagination offset." | Same field. The V2 page documents a **default of 0**; the V3 document does not mention a default, and the spec describes it as "Number of records to skip. 0 for none". |
| `rows` — Required, Integer. "The number of rows to be fetched, starting from the start."[doc:turn3doc2] | `rows` — Required, Integer. "Specifies the number of records to be returned in the response." | Unchanged. |
| `sortField` — Required, String. "Key on the basis of which you want to sort the data."[doc:turn3doc2] | `sortField` — Required, String. | Unchanged. V2 publishes the supported value list (§8.1); the V3 document gives only the `CHANNEL_CREATED_TIME` example. |
| `sortOrder` — **Optional**, String[doc:turn3doc2] | `sortOrder` — **Required**, String | **Changed requiredness** in the documents. The spec marks nothing required. See §10, item 3. |
| — | `sinceTime`, `untilTime`, `sinceId`, `untilId` | New in the `StreamRequestDTO` schema; undocumented in both V2 and V3 operation documents. |
| `nextPageCursor` (response) | `nextPageCursor` (response) | Unchanged. |
| `hasMore` (response) | `hasMore` (response) | Unchanged. |
| `entities` (response, MESSAGE objects) | `entities` (response, MESSAGE objects) | Unchanged in structure. The V3 examples additionally show `isRichText`, `avatarUrl`, `statusCount`, `accountSpecificInfos`, `mentionedProfiles`, `parentMessageId`, and `autoResponse`; the V2 example shows `workflow.queues`, `workflow.spaceWorkflows`, and `workflow.customProperties`. These are channel- and configuration-dependent rather than version-dependent. See §10, item 7. |


The request body contract, the response envelope, and the cursor paging model are otherwise **unchanged** between V2 and V3. The migration is predominantly a URL rewrite.

### 6.3 Migration steps

1. Change the feed path from `/api/v2/stream/{streamId}/feed` to `/api/v3/streams/feed` — note the plural `streams`.
2. Move `streamId` out of the path and onto the query string: `?streamId={streamId}`.
3. Leave the request body as-is. `start`, `rows`, `sortField`, and `sortOrder` are unchanged.
4. Begin sending `sortOrder` explicitly — it is Optional in V2 but documented as Required in V3.
5. Change the cursor path from `/api/v2/stream/cursor/{cursorId}` to `/api/v3/stream/cursor/{nextPageCursor}`, subject to confirmation of the endpoint conflict in §10, item 1.
6. Leave response parsing as-is — `data.entities`, `data.hasMore`, and `data.nextPageCursor` are unchanged.
7. Keep the header set unchanged. Remove any `Cookie: JSESSIONID=…` header copied from sample cURLs.
8. Re-test against a non-production `{env}` first.
9. The other Engagement Dashboard endpoints — Fetch Engagement Dashboards, Fetch Engagement Dashboard by Name, Outbound Column Stream, Case Management Stream, Fetch UGC Stream Data — are **not** covered by this migration. Continue calling their V2 forms until their V3 status is confirmed (§10, item 9). In particular, the Read Dashboard call you use to obtain `streamId` remains a V2 endpoint: `GET /api/v2/monitoring/dashboard` and `GET /api/v2/monitoring/dashboard/find/name/{dashboardName}`[doc:turn3doc5][doc:turn3doc7].


## 7. Prerequisite — obtaining a stream id

The stream API cannot be called without a stream id, and no V3 endpoint supplies one. Use the V2 Read Dashboard APIs[doc:turn3doc5][doc:turn3doc7]:

| Purpose | Endpoint |
|  --- | --- |
| Fetch all engagement dashboards available in the partner environment/workspace | `GET https://api3.sprinklr.com/{env}/api/v2/monitoring/dashboard`[doc:turn3doc5] |
| Fetch a specific engagement dashboard by name | `GET https://api3.sprinklr.com/{env}/api/v2/monitoring/dashboard/find/name/{dashboardName}`[doc:turn3doc7] |


**Dev Note:** the dashboard name must be URL-encoded[doc:turn3doc1].

In the response, locate the stream id via[doc:turn3doc1]:

- `columnOrder` — the list of all stream ids for the columns in the dashboard.
- `columns` — an array containing the individual stream ids under the `id` field.


Documented use cases for these calls[doc:turn3doc5][doc:turn3doc7]:

- Fetch the names and column ids of all engagement dashboards available in the Sprinklr environment.
- Fetch column ids, which can then be used in the stream read API.
- Fetch up-to-date information on all engagement dashboards existing in the environment.


## 8. Reference

### 8.1 Supported `sortField` values

Published on the V2 Inbound Column Stream page[doc:turn3doc2]. `SortKey` carries no enum in the OpenAPI document, so this remains the only published list.

| `sortField` | Applies to |
|  --- | --- |
| `CHANNEL_CREATED_TIME` | `INBOX`, `COMMENTS`, `REPLIES`, `POSTS`, `PRIVATE_MESSAGES`, `EVENTS`, `SHARES`, `GROUP_POSTS`, `GROUP_COMMENTS`, `GROUP_REPLIES`, `FB_INSTA_AD_POST`, `FB_INSTA_AD_COMMENT`, `FB_INSTA_AD_REPLY`, `CHANNEL_SEARCH`, `SEARCH`, `SEARCH_WITH_FILTERS`, `RECEIVED_DIRECT_MESSAGES`, `MENTIONS`, `RETWEETS`, `MY_TWEETS`, `SENT_DIRECT_MESSAGES`, `TIMELINE`, `FILTERED_TIMELINE`, `FAV_TWEETS`, `LISTS`, `INSTAGRAM_MEDIA`, `INSTAGRAM_MEDIA_COMMENT`, `INSTAGRAM_STORY`, `INSTAGRAM_MENTION`, `INSTAGRAM_COMMENT_MENTION`, `INSTAGRAM_MEDIA_TAG`, `DIRECT_MESSAGES`, `TIK_TOK VIDEO POSTS` |


`sortOrder`: `ASC` (ascending) or `DESC` (descending). `Order` carries no enum in the spec.

### 8.2 Fields observed on `entities` items

The operation documents direct you to the portal's MESSAGE response definitions rather than tabulating fields. The following were observed in the supplied V3 examples and are listed for orientation only — **this is not an exhaustive or authoritative field list.**

| Field | Type (observed) | Notes |
|  --- | --- | --- |
| `sourceType` | String | e.g. `ACCOUNT` |
| `sourceId` | Number | Sprinklr account id |
| `content.text` | String | Message body |
| `content.isRichText` | Boolean |  |
| `content.attachment` | Object | Present in the V2 example with `type: "CAROUSEL"`, `disableManualResponse`, `reSubmittable`[doc:turn3doc2] |
| `channelMessageId` | String | Channel-native message id |
| `channelType` | String | e.g. `INSTAGRAM`, `FACEBOOK` |
| `accountType` | String | e.g. `INSTAGRAM`, `FBPAGE` |
| `channelCreatedTime` | Number (epoch ms) | Time the message was created on the channel |
| `senderProfile` / `receiverProfile` | Object | Profile block — `name`, `channelType`, `channelId`, `permalink`, `avatarUrl`, `followers`, `following`, `username`, `verified`, `unSubscribed`, `deleted`, `snCreatedTime`, `snModifiedTime`, `statusCount`, `accountSpecificInfos` |
| `mentionedProfiles` | Array of profile objects | Profiles mentioned in the message |
| `permalink` | String | Public URL of the message |
| `language` | String | ISO language code |
| `messageId` | String | Sprinklr universal message id |
| `postId` | Number |  |
| `brandPost` | Boolean |  |
| `createdTime` / `modifiedTime` | Number (epoch ms) | Sprinklr-side timestamps |
| `textEntities` | Object | Entity offsets, e.g. `message[].indices` and `message[].screenName` |
| `insights` | Object | Channel metrics, e.g. `POST_FB_IMPRESSIONS`[doc:turn3doc2] |
| `workflow` | Object | e.g. `campaignId`; the V2 example also shows `modifiedTime`, `customProperties`, `queues`, `spaceWorkflows`[doc:turn3doc2] |
| `enrichments` | Object |  |
| `conversationId` | String |  |
| `parentMessageId` | String | Present when the message is a reply |
| `autoImported` | Boolean |  |
| `autoResponse` | Boolean |  |


> **Privacy note.** Several profile fields in the supplied examples read `"Restricted data"`. Profile data returned by this API is subject to Sprinklr's data-governance controls, and the fields you actually receive depend on your workspace permissions. Handle profile data — names, usernames, avatar URLs, follower counts — as personal data.


### 8.3 Envelope fields

| Field | Type | Description |
|  --- | --- | --- |
| `data.entities` | Array | Message objects for the requested page. |
| `data.hasMore` | Boolean | Whether more data is available beyond this page. |
| `data.nextPageCursor` | String | Cursor for the next page. Present only when `hasMore` is `true`. |
| `errors` | Array of `Error` | Empty on success. |


## 9. Use cases and Best practices

**Use cases**

- **Mirror an agent console into an external tool.** Pull the same inbound column an agent sees, so a third-party desktop can display and act on the identical message set.
- **Export a channel-filtered message feed.** Because column filters are loosely coupled[doc:turn3doc1], changing the filter in Sprinklr changes what the API returns — without any code change.
- **Incremental ingestion.** Sort by `CHANNEL_CREATED_TIME` ascending and page with the cursor to load a column into a data warehouse in a deterministic order.
- **Backfill then tail.** Use a large `rows` value with cursor paging for the initial backfill, then poll a small first page on a schedule to pick up new messages.
- **Cross-environment parity checks.** Since dashboard and column ids are consistent across Production and Sandbox[doc:turn3doc1], the same stream id can be compared across `{env}` values.


**Best practices**

- **Never hardcode a stream id.** Resolve it at runtime from the Read Dashboard API so that dashboard reconfiguration does not break your integration.
- **Always follow the cursor**, and stop when `hasMore` is `false` or `nextPageCursor` is absent. Do not increment `start` to page — that is the initial-request offset only.
- **Sort deterministically.** Always send `sortField` and `sortOrder`. Paging an unsorted or non-deterministically sorted feed can duplicate or skip records.
- **Prefer ascending order for ingestion** so that newly arriving messages do not shift the pages you have already read.
- **Treat cursors as opaque and short-lived.** No expiry is documented — do not persist a cursor across long-running jobs, and re-issue the feed request if a cursor stops working.
- **Choose `rows` deliberately.** No maximum is documented (§10, item 8). Start conservatively and increase only after measuring.
- **Parse defensively.** `data` is an untyped object in the spec; the `entities` field set varies by channel and by workspace permissions. Do not assume any field is present.
- **Check `errors` even on HTTP 200.** The envelope carries an `errors` array on success responses too.
- **Do not send the `Cookie: JSESSIONID=…` header** seen in the captured samples, and never commit tokens or API keys to source control.
- **Test against a non-production `{env}` first.**