Filter and paginate conversations
List a site's conversations over the REST API with filters on dates, channel, agent, language, 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://www.iadvize.ninja/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",
"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": "eyJhdCI6IjIwMjYt…"
}
}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://www.iadvize.ninja/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=eyJhdCI6IjIwMjYt…"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. |
feedback | positive or negative: at least one message of the conversation has a rating of that kind. |
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.1.
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. - 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. |
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. |
entryWidget | The engagement widget it was opened from, or null. |
pageContext | The page the conversation started from, as your tag declared it, or 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 two fields: messages, the ordered transcript, and metadata, the test context of a Playground conversation (null otherwise). The list never returns messages. 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 and agent versions, authenticated as your organization. Nine read-only tools and three ready-made prompts.