curl --request GET \
--url https://{host}/api/public/analytics/projects/{projectId}/conversation-history \
--header 'x-api-key: <api-key>'{
"success": true,
"totalRecords": 2,
"hasMore": false,
"offset": 0,
"limit": 100,
"messages": [
{
"id": "msg-01J4S8Y2",
"sessionId": "sess-01J4S8Y1",
"type": "incoming",
"role": "user",
"content": "Hello",
"channel": "web_chat",
"sourceChannel": "web_chat",
"environment": "production",
"channelUId": "contact-123",
"traceDimensions": {
"accountTier": "gold"
},
"traceId": "trace-123",
"attachmentIds": [],
"hasPII": false,
"metadata": {
"clientVersion": "1.4.2"
},
"sequence": 1,
"agentName": null,
"timestamp": "2026-08-05T14:30:00.000Z",
"createdAt": "2026-08-05T14:30:00.120Z",
"updatedAt": "2026-08-05T14:30:00.120Z"
},
{
"id": "msg-01J4S8Y3",
"sessionId": "sess-01J4S8Y1",
"type": "outgoing",
"role": "assistant",
"content": "How can I help?",
"channel": "web_chat",
"sourceChannel": "web_chat",
"environment": "production",
"channelUId": "contact-123",
"traceDimensions": {
"accountTier": "gold"
},
"traceId": "trace-124",
"attachmentIds": [],
"hasPII": false,
"metadata": {
"isLlmGenerated": true,
"responseProvenance": {
"schemaVersion": 1,
"kind": "llm",
"disclaimerRequired": true,
"usedLlmInternally": true
}
},
"sequence": 2,
"agentName": "Support Agent",
"timestamp": "2026-08-05T14:30:01.000Z",
"createdAt": "2026-08-05T14:30:01.100Z",
"updatedAt": "2026-08-05T14:30:01.100Z"
}
],
"nextCursor": null
}{
"error": {
"code": "<string>",
"message": "<string>",
"details": {}
},
"success": false,
"message": "<string>",
"required": "<string>"
}{
"error": {
"code": "<string>",
"message": "<string>",
"details": {}
},
"success": false,
"message": "<string>",
"required": "<string>"
}{
"error": {
"code": "<string>",
"message": "<string>",
"details": {}
},
"success": false,
"message": "<string>",
"required": "<string>"
}{
"error": {
"code": "<string>",
"message": "<string>",
"details": {}
},
"success": false,
"message": "<string>",
"required": "<string>"
}{
"error": {
"code": "<string>",
"message": "<string>",
"details": {}
},
"success": false,
"message": "<string>",
"required": "<string>"
}{
"error": {
"code": "<string>",
"message": "<string>",
"details": {}
},
"success": false,
"message": "<string>",
"required": "<string>"
}{
"error": {
"code": "<string>",
"message": "<string>",
"details": {}
},
"success": false,
"message": "<string>",
"required": "<string>"
}{
"error": {
"code": "<string>",
"message": "<string>",
"details": {}
},
"success": false,
"message": "<string>",
"required": "<string>"
}List conversation messages
Messages are ordered by time, then by message ID to keep the order stable. Different filters combine with AND, and multiple values in the same filter combine with OR.
A date-range query with no other filter that would span more than 10,000
sessions is rejected with 400, so narrow the range or add sessionIds for
busy projects.
curl --request GET \
--url https://{host}/api/public/analytics/projects/{projectId}/conversation-history \
--header 'x-api-key: <api-key>'{
"success": true,
"totalRecords": 2,
"hasMore": false,
"offset": 0,
"limit": 100,
"messages": [
{
"id": "msg-01J4S8Y2",
"sessionId": "sess-01J4S8Y1",
"type": "incoming",
"role": "user",
"content": "Hello",
"channel": "web_chat",
"sourceChannel": "web_chat",
"environment": "production",
"channelUId": "contact-123",
"traceDimensions": {
"accountTier": "gold"
},
"traceId": "trace-123",
"attachmentIds": [],
"hasPII": false,
"metadata": {
"clientVersion": "1.4.2"
},
"sequence": 1,
"agentName": null,
"timestamp": "2026-08-05T14:30:00.000Z",
"createdAt": "2026-08-05T14:30:00.120Z",
"updatedAt": "2026-08-05T14:30:00.120Z"
},
{
"id": "msg-01J4S8Y3",
"sessionId": "sess-01J4S8Y1",
"type": "outgoing",
"role": "assistant",
"content": "How can I help?",
"channel": "web_chat",
"sourceChannel": "web_chat",
"environment": "production",
"channelUId": "contact-123",
"traceDimensions": {
"accountTier": "gold"
},
"traceId": "trace-124",
"attachmentIds": [],
"hasPII": false,
"metadata": {
"isLlmGenerated": true,
"responseProvenance": {
"schemaVersion": 1,
"kind": "llm",
"disclaimerRequired": true,
"usedLlmInternally": true
}
},
"sequence": 2,
"agentName": "Support Agent",
"timestamp": "2026-08-05T14:30:01.000Z",
"createdAt": "2026-08-05T14:30:01.100Z",
"updatedAt": "2026-08-05T14:30:01.100Z"
}
],
"nextCursor": null
}{
"error": {
"code": "<string>",
"message": "<string>",
"details": {}
},
"success": false,
"message": "<string>",
"required": "<string>"
}{
"error": {
"code": "<string>",
"message": "<string>",
"details": {}
},
"success": false,
"message": "<string>",
"required": "<string>"
}{
"error": {
"code": "<string>",
"message": "<string>",
"details": {}
},
"success": false,
"message": "<string>",
"required": "<string>"
}{
"error": {
"code": "<string>",
"message": "<string>",
"details": {}
},
"success": false,
"message": "<string>",
"required": "<string>"
}{
"error": {
"code": "<string>",
"message": "<string>",
"details": {}
},
"success": false,
"message": "<string>",
"required": "<string>"
}{
"error": {
"code": "<string>",
"message": "<string>",
"details": {}
},
"success": false,
"message": "<string>",
"required": "<string>"
}{
"error": {
"code": "<string>",
"message": "<string>",
"details": {}
},
"success": false,
"message": "<string>",
"required": "<string>"
}{
"error": {
"code": "<string>",
"message": "<string>",
"details": {}
},
"success": false,
"message": "<string>",
"required": "<string>"
}fromDate and toDate together) or a list of sessionIds. A date-range query with no other filter may span a long list of sessions, so always use a filter and paginate as needed. A response that span more than 10,000 sessions is rejected with 400, so narrow the range or add sessionIds for busy projects.
For pagination, prefer cursor: pass the nextCursor value from the previous response. offset also works, and skip is an older name for offset that is still accepted. Use only one of the three in a single request.
Authenticate with the x-api-key header. The key must be bound to the project you are querying and have analytics:read permission.Authorizations
Project-bound API key. Do not send an Authorization header.
Path Parameters
1Query Parameters
Inclusive ISO 8601 timestamp. Required with toDate unless sessionIds is supplied.
Exclusive ISO 8601 timestamp. Required with fromDate unless sessionIds is supplied.
Filter to specific sessions. Pass up to 10,000 session IDs, comma-separated or by repeating the parameter. Can be used instead of a date range.
100001Filter to messages from specific end users, using the identity recorded for them on the channel. Pass up to 100 values, comma-separated or by repeating the parameter.
1001Filter to sessions from specific callers (ANI or provider caller id). Pass up
to 100 values, comma-separated (spaces after commas are allowed) or by
repeating the parameter; the filter is ANDed with every other filter. The
100-value limit counts entries before duplicates are merged; more returns
400 TOO_MANY_FILTER_VALUES. A value that itself contains a comma cannot be
queried, because commas always separate values.
Paste the value in any format the provider, a transcript, a CDR or a
spreadsheet shows. Each value is turned into the same match key the platform
stored at call time: invisible characters, fullwidth digits and Unicode
dashes are cleaned up, a national trunk (0) after the country code is
removed (+44 (0)20 7946 0958 matches +442079460958), and phone-like
values (including sip: and
tel: URIs, spaces, dashes, dots, slashes and parentheses) become E.164.
7 to 15 bare digits are treated as an international number (919876543210
matches +919876543210). Digits written in a national format keep their
digits (098765 43210 matches 09876543210). Other values, such as softphone
usernames, SDK ids or Anonymous, are matched as cleaned text. Matching is
exact and case-sensitive: anonymous does not match Anonymous, and
+14155550100 ext 12 does not match +14155550100. Non-ASCII digits such as
Arabic-Indic digits are not converted to ASCII. Keys longer than 128
characters are truncated, the same way they were when stored.
There is no country inference: a national number without its country code
(9876543210) does not match a stored +919876543210, and 00 or 011
dial prefixes are not converted. Send the full international form where
possible. Send + as %2B or use the POST twin; an unencoded + decodes to
a space, which is also accepted and gives the same match. Anonymous matches
every withheld-number call in the window.
An entry that is empty or has no letter or digit (for example
callerNumber=, a trailing comma, or only +), or an entry longer than 512
characters, returns 400 INVALID_CALLER_NUMBER. The value is
never returned in the response. When the project's reporting exposure config
disables the identity field group, a request with this parameter returns
403 CALLER_NUMBER_LOOKUP_DISABLED.
Coverage: this endpoint compares the caller value the provider stored on the
session record, as received, with the match key, the match key without +,
and the cleaned value as you sent it. It covers sessions recorded before this
filter shipped. Pasting the provider value exactly as stored matches,
unless the stored value itself contains invisible characters, fullwidth
digits, Unicode dashes or surrounding whitespace (those are cleaned from
what you send, so only its E.164 or bare-digit form can match). A
re-formatted copy matches here only when it shares the E.164 or bare-digit
form of the stored value: for example +44 20 7946 0958 does not match a
stored +44 (0)20 7946 0958, and 09876543210 does not match a stored
098765 43210, although /sessions matches both. Playground and Studio
debug sessions are never matched.
1001 - 512Filter by the channel the conversation took place on. Pass up to 100 values, comma-separated or by repeating the parameter.
Matched exactly as stored, with no alias handling. Unlike the sessions
endpoint, web and web_chat are different values here, and shorthand such
as sms or sdk_http matches nothing — use the exact channel name.
Not restricted to a fixed list, so an unrecognised value returns no results rather than an error.
100http_async, slack, line, msteams, whatsapp, messenger, instagram, twilio_sms, zendesk, telegram, genesys, genesys_open_messaging, ai4w, kore_agent_assist, email, voice_vxml, korevg, audiocodes, genesys_audio_connector, voice_pipeline, voice_realtime, voice, voice_twilio, voice_livekit, ag_ui, a2a, sdk_websocket, web_debug, web_chat, api, http Filter by environment. Pass up to 100 values, comma-separated or by repeating the parameter. Matched exactly as stored.
Not restricted to a fixed list, so an unrecognised value returns no results rather than an error, though in practice only the four values below are stored.
100dev, staging, production, working-copy Filter by a custom dimension recorded against the session. Replace key with
the dimension name — for example traceDimensions[tier]=gold. Repeat with
different keys to require all of them. Values match exactly.
Pass the nextCursor value from the previous response to get the next page.
Do not use together with offset or skip.
1Number of messages to skip before the first one returned. Do not use
together with cursor or skip.
x >= 0An older name for offset, kept for compatibility. Prefer offset. Do not
use together with cursor or offset.
x >= 0How many messages to return. A page may come back with fewer than requested
so the response stays within the 1 MiB size limit — check hasMore rather
than assuming a short page is the last one.
1 <= x <= 10000Sort order by time: asc for oldest first, desc for newest first.
asc, desc Response
Message page returned successfully.
- Option 1
- Option 2
Always true on a successful response.
Total number of messages matching your filters, across all pages.
x >= 0Whether more pages are available after this one.
The offset applied to this response.
x >= 0The page size applied to this response.
1 <= x <= 10000The messages on this page.
Show child attributes
Show child attributes
Opaque cursor for the next page.
Was this page helpful?