Filter and paginate conversations
List a site's conversations over the REST API with filters on dates, channel, agent, language, device, feedback, title keywords, labels and scores, and page through the result with a cursor.
GET /api/v1/sites/{siteId}/conversations returns a site's conversations, newest first, one page at a time. You narrow the list with query parameters and read the next page with a cursor. This page covers both. For the endpoint's schema, see List a site's conversations. To create an API key, see Authentication.
An example
Conversations from September on the Web channel where the need was not met, and where the shopper was frustrated or the assistant deflected a question it could have answered. limit=1 only keeps the response short.
curl -G "https://iadvize.ai/api/v1/sites/YOUR_SITE_ID/conversations" \
-H "Authorization: Bearer YOUR_API_KEY" \
--data-urlencode "channel=web" \
--data-urlencode "from=2026-09-01T00:00:00Z" \
--data-urlencode "to=2026-10-01T00:00:00Z" \
--data-urlencode "labels.all=resolution:unresolved" \
--data-urlencode "labels.any=visitor_frustrated:true|no_unjustified_fallback:fail" \
--data-urlencode "limit=1"The response (ids and values are illustrative, and the cursor is shortened):
{
"data": [
{
"id": "conv_01kvnwmagvf27rc9cze5rm9e1r",
"title": "Waterproof jacket for hiking",
"channel": "web",
"language": "en",
"device": "mobile",
"createdAt": "2026-09-12T08:41:09.123Z",
"updatedAt": "2026-09-12T08:44:13.123Z",
"siteId": "site_01kvnwmagvf27rc9cze5rm9e1r",
"agentId": "agt_01kvnwmagvf27rc9cze5rm9e1r",
"agentName": "Storefront assistant",
"agentVersion": 2,
"agentVersionId": "agtv_01kvnwmagwf27rc9d7zef4qksf",
"entryWidget": null,
"pageContext": {
"pageType": "product",
"productId": "jacket-123",
"categoryName": null,
"pageUrl": "https://shop.example/products/jacket-123"
},
"messageCount": 6,
"durationMs": 184000,
"feedback": { "positive": 0, "negative": 1 },
"productEngagement": { "impressions": 3, "clicks": 0 },
"labelStatus": "labeled",
"labelSummary": {
"resolution": "unresolved",
"reviewReason": "service",
"scores": { "quality": 0.9, "service": 0.8, "trust": 1 },
"failedCriteria": ["no_unjustified_fallback"]
}
}
],
"meta": {
"nextCursor": "eyJrIjpbIjIwMjYt…"
}
}To read the next page, send the same request with cursor set to meta.nextCursor. Keep every filter as it was. nextCursor is null on the last page.
curl -G "https://iadvize.ai/api/v1/sites/YOUR_SITE_ID/conversations" \
-H "Authorization: Bearer YOUR_API_KEY" \
--data-urlencode "channel=web" \
--data-urlencode "from=2026-09-01T00:00:00Z" \
--data-urlencode "to=2026-10-01T00:00:00Z" \
--data-urlencode "labels.all=resolution:unresolved" \
--data-urlencode "labels.any=visitor_frustrated:true|no_unjustified_fallback:fail" \
--data-urlencode "limit=1" \
--data-urlencode "cursor=eyJrIjpbIjIwMjYt…"Filters
Every filter is optional. Filters combine with AND. A parameter that is not in this table, or a single-valued parameter sent twice, is a 400 with the code invalid_request.
| Parameter | Matches |
|---|---|
from, to | The conversation's creation time. ISO 8601 with an offset, for example 2026-09-01T00:00:00Z. from is inclusive, to is exclusive. |
channel | web, hosted or playground. |
agentId | Conversations that ran through this agent, whatever its version. An agt_ id. |
language | The panel language recorded on the conversation: fr, en, de, it, es or nl. |
device | Conversations started on this device: desktop, mobile, tablet or unknown. unknown also covers every hosted and playground conversation and any with no recorded start. Safari on iPadOS presents a desktop browser: those iPads now count as tablet, while iPad conversations started before the detection shipped (see the changelog) stay desktop. |
feedback | positive or negative: at least one message of the conversation has a rating of that kind. |
openedFromStarter | true or false. true matches conversations the shopper opened from a starter question. false matches the others. |
singleShopperMessage | true or false. true matches conversations where the shopper wrote exactly one message. false matches every other count, zero included. Counts the shopper's own messages, not the assistant's. |
keywords | A word or phrase in the conversation title, case-insensitive. Repeat the parameter to require several terms: all of them must appear. Up to 20, 200 characters each. |
labels.all | The conversation's labels include every token listed. Repeat the parameter for several tokens. |
labels.any | Each occurrence is one group of tokens separated by |. The conversation must match at least one token in every group. Repeat for several groups. |
labels.none | The conversation's labels include none of the tokens listed. Tokens ending in :true only. Needs labels.all, labels.any, from or to alongside it. |
quality.min, quality.max | The quality score of the conversation's label record, from 0 to 1, bounds included. Same for service.* and trust.*. |
labelStatus | labeled, pending, failed or not_eligible. See the four statuses. |
A label token is <label>:<value>, for example resolution:unresolved, visitor_frustrated:true or grounded:fail. GET /api/v1/labels/vocabulary lists every token the filters accept. See Read labels over the API.
A few rules to know:
- Labels, scores and
labelStatus. Alabels.*or score filter only matches labeled conversations. Combining one with alabelStatusother thanlabeledis aninvalid_filter, and so is a token the vocabulary doesn't contain. not_applicablehas no token. A criterion can be filtered onpassorfail. A conversation where the criterion wasnot_applicablematches neither.- A score bound skips unscored conversations. A conversation with no value for that score never matches a bound on it.
- Labeling coverage. Conversations of all three channels (
web,hostedandplayground) are labeled. A label arrives 30 to 90 minutes after a conversation goes quiet, so a recent conversation readspending. A conversation whose last activity is more than 30 days old is not labeled. An empty result for a label filter is therefore not proof that a conversation doesn't exist. - Labels are read as of the published taxonomy. The filters and the
labelSummarycome from the current label of each conversation, under taxonomy v1.2.
Pages and cursors
- Order. Newest first, by creation time. A conversation resumed later keeps its place.
- Page size.
limitis an integer from 1 to 100, and defaults to 25. Anything else is a400. - The cursor is opaque. Don't parse it or build one. Pass
meta.nextCursorback ascursorexactly as you received it. - A cursor belongs to its filters. Send a cursor with different filters and you get
cursor_mismatch. To change the filters, start again without a cursor. The order in which you repeat a parameter (labels.all,keywords) doesn't matter.limitandcountaren't part of the listing, so you can change them between pages. - A cursor the API didn't issue gets
invalid_cursor. - A cursor from before a format change gets
invalid_cursor. The format is ours to change, and a cursor issued before a change isn't read after it: start again without a cursor and follow the newnextCursor. - An empty
cursor=is the same as no cursor.
Both errors are described on Errors and rate limits.
Count
Add count=true to get a total in meta.count:
{ "meta": { "nextCursor": null, "count": { "value": 120, "capped": false } } }The count is capped at 10,000. When the real total is larger, value is 10,000 and capped is true. The count ignores cursor and limit: it is the total for the filters on the request. meta.count is absent when you don't ask for it. The figures in this snippet are made up.
What each conversation carries
Every item, and the conversation returned by Get a conversation, has:
| Field | Content |
|---|---|
messageCount | Number of messages. |
shopperMessageCount | Number of messages the shopper wrote. The assistant's messages are not counted. messageCount counts all of them. |
openedFromStarter | true when the shopper opened the conversation from a starter question, false otherwise. The starter question itself is never returned. |
durationMs | Last activity minus creation time, in milliseconds. |
feedback | positive and negative: how many messages carry each rating. |
productEngagement | impressions and clicks on the product cards the conversation rendered. |
device | The shopper's device when they started the conversation: desktop, mobile, tablet or unknown. Never null. See the device filter above for what unknown covers. |
entryWidget | The engagement widget it was opened from, or null. |
pageContext | The page the conversation started from, as your tag declared it, or null. pageContext.pageType is home, product, category, content or blog; a conversation recorded with the removed checkout type reads null. See The page URL. |
labelStatus | labeled, pending, failed or not_eligible. |
labelSummary | For a labeled conversation: resolution, reviewReason, scores and failedCriteria (the criteria the judge failed). null for the other three statuses. |
The single conversation adds three fields: messages, the ordered transcript; metadata, the test context of a Playground conversation (null otherwise); and attributedOrders, the orders carrying the conversation's join key, each with its net amount and its status (credited, cancelled, out_of_window or excluded). attributedOrders is an empty array, never null, when no order is linked; the statuses are described in Orders linked to a conversation. The list never returns messages or orders. No field identifies the shopper.
The page URL
pageContext.pageUrl is the page's address without its query string, its fragment and its ; path parameters. Only the origin and the path are kept, so https://shop.example/products/jacket-123?utm_source=mail#reviews is returned as https://shop.example/products/jacket-123. An address with another scheme, such as javascript: or mailto:, is returned as null.
The path itself is left as your tag sent it, and it can still contain identifiers you put in your own URLs, such as an order number or a customer slug. Treat pageUrl as customer data: store and share it with the same care as the rest of your own data.
To read the full label record of one conversation, use GET /api/v1/sites/{siteId}/conversations/{id}/labels. It is described in Conversation labeling.
Errors and rate limits
The problem document every API error returns, the list of error codes, what causes each one, and how to handle rate limits.
Connect an MCP client to your dashboard
Point your own Claude or any MCP client at your AISA conversations, labels, reports, agent versions, pages and starter questions, authenticated as your organization. Forty-one tools, twenty-four of them write, and three ready-made prompts.