Skip to contentSkip to navigationSkip to topbar
Page tools
Useful for sharing or LLM
Accelerate development with AI

On this page
Looking for more inspiration?Visit the

Aggregate conversation data with Conversation Insights


This guide walks through querying the Conversation Insights Metadata and Query resources to count conversations, add dimensions, and filter results. If you haven't worked with cubes, measures, and dimensions before, start with Core concepts.


Prerequisites

prerequisites page anchor

Complete the prerequisites:

(information)

Info

Insights data becomes available approximately 15 minutes after conversation events.


Before querying, use the Metadata API to see what measures and dimensions are available.

Send a GET request to the Metadata resource:

Retrieve available measures and dimensionsLink to code sample: Retrieve available measures and dimensions
1
// Download the helper library from https://www.twilio.com/docs/node/install
2
const twilio = require("twilio"); // Or, for ESM: import twilio from "twilio";
3
4
// Find your Account SID at twilio.com/console
5
// Provision API Keys at twilio.com/console/runtime/api-keys
6
// and set the environment variables. See http://twil.io/secure
7
// For local testing, you can use your Account SID and Auth token
8
const accountSid = process.env.TWILIO_ACCOUNT_SID;
9
const apiKey = process.env.TWILIO_API_KEY;
10
const apiSecret = process.env.TWILIO_API_SECRET;
11
const client = twilio(apiKey, apiSecret, { accountSid: accountSid });
12
13
async function fetchMetadata() {
14
const metadata = await client.insights.v3.metadata.fetch();
15
16
console.log(metadata.domain);
17
}
18
19
fetchMetadata();

The response includes a list of cubes, each with its available measures and dimensions. The following example only shows one measure and one dimension per cube. Make a request to the Metadata resource to retrieve the full list.

1
{
2
"domain": "Conversations",
3
"cubes": [
4
{
5
"name": "ConversationSummary",
6
"description": "One wide row per conversation with rolled-up metadata, sentiment, and custom measures",
7
"measures": [
8
{
9
"name": "ConversationSummary.Count",
10
"type": "number",
11
"aggregation": "countDistinct",
12
"description": "The count of unique conversations"
13
}
14
],
15
"dimensions": [
16
{
17
"name": "ConversationSummary.HasAIAgent",
18
"type": "boolean",
19
"description": "Whether the conversation involved at least one AI agent"
20
}
21
]
22
},
23
{
24
"name": "Conversation",
25
"description": "Contains aggregated conversation event data with participant details",
26
"measures": [
27
{
28
"name": "Conversation.Count",
29
"type": "number",
30
"aggregation": "countDistinct",
31
"description": "The count of unique conversations"
32
}
33
],
34
"dimensions": [
35
{
36
"name": "Conversation.ConversationId",
37
"type": "string",
38
"description": "The unique identifier for the conversation"
39
}
40
]
41
},
42
{
43
"name": "OperatorResult",
44
"description": "Contains results of various intelligence operators run on conversations",
45
"measures": [
46
{
47
"name": "OperatorResult.Count",
48
"type": "number",
49
"aggregation": "countDistinct",
50
"description": "The count of unique operator results"
51
}
52
],
53
"dimensions": [
54
{
55
"name": "OperatorResult.OperatorName",
56
"type": "string",
57
"description": "Name of the operator. Eg. Sentiment Analysis, Agent Introduction"
58
}
59
]
60
}
61
]
62
}

For an explanation of each cube and the full list of measures and dimensions, see Core concepts.


Query sentiment by agent type

query-sentiment-by-agent-type page anchor

Find out whether your AI agents and your human agents are producing different customer sentiment. This is the kind of question the ConversationSummary cube exists to answer: because it holds one row per conversation, a Conversation Intelligence operator result and orchestration data sit side by side and you can group by both at once.

1
// Download the helper library from https://www.twilio.com/docs/node/install
2
const twilio = require("twilio"); // Or, for ESM: import twilio from "twilio";
3
4
// Find your Account SID at twilio.com/console
5
// Provision API Keys at twilio.com/console/runtime/api-keys
6
// and set the environment variables. See http://twil.io/secure
7
// For local testing, you can use your Account SID and Auth token
8
const accountSid = process.env.TWILIO_ACCOUNT_SID;
9
const apiKey = process.env.TWILIO_API_KEY;
10
const apiSecret = process.env.TWILIO_API_SECRET;
11
const client = twilio(apiKey, apiSecret, { accountSid: accountSid });
12
13
async function createQueryResults() {
14
const query = await client.insights.v3.query.create({
15
domain: "Conversations",
16
query: {
17
measures: ["ConversationSummary.Count"],
18
dimensions: [
19
"ConversationSummary.HasAIAgent",
20
"ConversationSummary.Sentiment",
21
],
22
filters: [
23
{
24
expressions: [
25
{
26
op: "GT",
27
field: "ConversationSummary.CreatedDate",
28
values: ["2026-04-02"],
29
},
30
],
31
},
32
],
33
},
34
});
35
36
console.log(query.domain);
37
}
38
39
createQueryResults();

The response returns one row per combination, so you can compare sentiment in conversations with and without an AI agent directly:

1
{
2
"domain": "Conversations",
3
"items": [
4
{
5
"ConversationSummary.HasAIAgent": true,
6
"ConversationSummary.Sentiment": "positive",
7
"ConversationSummary.Count": "42"
8
},
9
{
10
"ConversationSummary.HasAIAgent": false,
11
"ConversationSummary.Sentiment": "positive",
12
"ConversationSummary.Count": "17"
13
}
14
],
15
"meta": {
16
"key": "items",
17
"nextToken": null,
18
"pageSize": 50,
19
"previousToken": null
20
}
21
}

On a silver cube the same question takes two queries and a join in your own application, because participant data and operator results live on separate rows. Conversation Insights writes the summary row when a conversation reaches the CLOSED state, so this cube covers completed conversations only.


Aggregate total conversation count

aggregate-total-conversation-count page anchor

To get the total conversation count, make a POST request to the Query endpoint.

1
// Download the helper library from https://www.twilio.com/docs/node/install
2
const twilio = require("twilio"); // Or, for ESM: import twilio from "twilio";
3
4
// Find your Account SID at twilio.com/console
5
// Provision API Keys at twilio.com/console/runtime/api-keys
6
// and set the environment variables. See http://twil.io/secure
7
// For local testing, you can use your Account SID and Auth token
8
const accountSid = process.env.TWILIO_ACCOUNT_SID;
9
const apiKey = process.env.TWILIO_API_KEY;
10
const apiSecret = process.env.TWILIO_API_SECRET;
11
const client = twilio(apiKey, apiSecret, { accountSid: accountSid });
12
13
async function createQueryResults() {
14
const query = await client.insights.v3.query.create({
15
domain: "Conversations",
16
query: {
17
measures: ["Conversation.Count"],
18
filters: [
19
{
20
expressions: [
21
{
22
op: "GT",
23
field: "Conversation.CreatedDate",
24
values: ["2026-04-02"],
25
},
26
],
27
},
28
],
29
},
30
});
31
32
console.log(query.domain);
33
}
34
35
createQueryResults();

The response includes your aggregated data:

1
{
2
"domain": "Conversations",
3
"items": [
4
{
5
"Conversation.Count": "2"
6
}
7
],
8
"meta": {
9
"key": "items",
10
"nextToken": null,
11
"pageSize": 50,
12
"previousToken": null
13
}
14
}

Query conversations grouped by dimensions. This example counts conversations by status:

1
// Download the helper library from https://www.twilio.com/docs/node/install
2
const twilio = require("twilio"); // Or, for ESM: import twilio from "twilio";
3
4
// Find your Account SID at twilio.com/console
5
// Provision API Keys at twilio.com/console/runtime/api-keys
6
// and set the environment variables. See http://twil.io/secure
7
// For local testing, you can use your Account SID and Auth token
8
const accountSid = process.env.TWILIO_ACCOUNT_SID;
9
const apiKey = process.env.TWILIO_API_KEY;
10
const apiSecret = process.env.TWILIO_API_SECRET;
11
const client = twilio(apiKey, apiSecret, { accountSid: accountSid });
12
13
async function createQueryResults() {
14
const query = await client.insights.v3.query.create({
15
domain: "Conversations",
16
query: {
17
measures: ["Conversation.Count"],
18
dimensions: ["Conversation.ConversationStatus"],
19
filters: [
20
{
21
expressions: [
22
{
23
op: "GT",
24
field: "Conversation.CreatedDate",
25
values: ["2026-04-02"],
26
},
27
],
28
},
29
],
30
},
31
});
32
33
console.log(query.domain);
34
}
35
36
createQueryResults();

The response groups results by status:

1
{
2
"domain": "Conversations",
3
"items": [
4
{
5
"Conversation.ConversationStatus": "ACTIVE",
6
"Conversation.Count": "15"
7
}
8
],
9
"meta": {
10
"key": "items",
11
"nextToken": null,
12
"pageSize": 50,
13
"previousToken": null
14
}
15
}

Aggregate a custom measure

aggregate-a-custom-measure page anchor

If you have registered a custom measure, it behaves like any built-in measure, with a Sum, Avg, Min, and Max aggregation for each. This example uses LeadScoreAvg to average a LeadScore measure by channel, so you can see which channels produce the highest-intent conversations:

1
// Download the helper library from https://www.twilio.com/docs/node/install
2
const twilio = require("twilio"); // Or, for ESM: import twilio from "twilio";
3
4
// Find your Account SID at twilio.com/console
5
// Provision API Keys at twilio.com/console/runtime/api-keys
6
// and set the environment variables. See http://twil.io/secure
7
// For local testing, you can use your Account SID and Auth token
8
const accountSid = process.env.TWILIO_ACCOUNT_SID;
9
const apiKey = process.env.TWILIO_API_KEY;
10
const apiSecret = process.env.TWILIO_API_SECRET;
11
const client = twilio(apiKey, apiSecret, { accountSid: accountSid });
12
13
async function createQueryResults() {
14
const query = await client.insights.v3.query.create({
15
domain: "Conversations",
16
query: {
17
measures: ["ConversationSummary.LeadScoreAvg"],
18
dimensions: ["ConversationSummary.Channels"],
19
filters: [
20
{
21
expressions: [
22
{
23
op: "GT",
24
field: "ConversationSummary.CreatedDate",
25
values: ["2026-04-02"],
26
},
27
],
28
},
29
],
30
},
31
});
32
33
console.log(query.domain);
34
}
35
36
createQueryResults();

Replace LeadScore with the name you chose when registering the measure. Conversations where the source operator didn't run return null, so they form their own group rather than counting toward another channel's average.


Query operator results to analyze intelligence operators run on your conversations. This example counts operator results grouped by operator name:

Query operator result count by operator nameLink to code sample: Query operator result count by operator name
1
// Download the helper library from https://www.twilio.com/docs/node/install
2
const twilio = require("twilio"); // Or, for ESM: import twilio from "twilio";
3
4
// Find your Account SID at twilio.com/console
5
// Provision API Keys at twilio.com/console/runtime/api-keys
6
// and set the environment variables. See http://twil.io/secure
7
// For local testing, you can use your Account SID and Auth token
8
const accountSid = process.env.TWILIO_ACCOUNT_SID;
9
const apiKey = process.env.TWILIO_API_KEY;
10
const apiSecret = process.env.TWILIO_API_SECRET;
11
const client = twilio(apiKey, apiSecret, { accountSid: accountSid });
12
13
async function createQueryResults() {
14
const query = await client.insights.v3.query.create({
15
domain: "Conversations",
16
query: {
17
measures: ["OperatorResult.Count"],
18
dimensions: ["OperatorResult.OperatorName"],
19
filters: [
20
{
21
expressions: [
22
{
23
op: "GT",
24
field: "OperatorResult.CreatedDate",
25
values: ["2026-04-02"],
26
},
27
],
28
},
29
],
30
},
31
});
32
33
console.log(query.domain);
34
}
35
36
createQueryResults();

The response groups results by operator name:

1
{
2
"domain": "Conversations",
3
"items": [
4
{
5
"OperatorResult.Count": "168",
6
"OperatorResult.OperatorName": "Sentiment"
7
}
8
],
9
"meta": {
10
"key": "items",
11
"nextToken": null,
12
"pageSize": 1,
13
"previousToken": null
14
}
15
}

Filter by Knowledge Base

filter-by-knowledge-base page anchor

Query operator results that used a specific Knowledge Base. This example retrieves conversations and operator names filtered by Knowledge Base ID:

Query operator results filtered by Knowledge Base IDLink to code sample: Query operator results filtered by Knowledge Base ID
1
// Download the helper library from https://www.twilio.com/docs/node/install
2
const twilio = require("twilio"); // Or, for ESM: import twilio from "twilio";
3
4
// Find your Account SID at twilio.com/console
5
// Provision API Keys at twilio.com/console/runtime/api-keys
6
// and set the environment variables. See http://twil.io/secure
7
// For local testing, you can use your Account SID and Auth token
8
const accountSid = process.env.TWILIO_ACCOUNT_SID;
9
const apiKey = process.env.TWILIO_API_KEY;
10
const apiSecret = process.env.TWILIO_API_SECRET;
11
const client = twilio(apiKey, apiSecret, { accountSid: accountSid });
12
13
async function createQueryResults() {
14
const query = await client.insights.v3.query.create({
15
domain: "Conversations",
16
query: {
17
dimensions: [
18
"OperatorResult.ConversationId",
19
"OperatorResult.OperatorName",
20
"OperatorResult.KnowledgeBaseId",
21
],
22
filters: [
23
{
24
op: "AND",
25
expressions: [
26
{
27
op: "GT",
28
field: "OperatorResult.CreatedDate",
29
values: ["2026-04-10"],
30
},
31
{
32
op: "EQ",
33
field: "OperatorResult.KnowledgeBaseId",
34
values: ["know_knowledgebase_xxxxxxxxxxxxxxxxxxxxxxxxxxxx"],
35
},
36
],
37
},
38
],
39
},
40
});
41
42
console.log(query.domain);
43
}
44
45
createQueryResults();

The response returns operator results filtered by the specified Knowledge Base:

1
{
2
"domain": "Conversations",
3
"items": [
4
{
5
"OperatorResult.ConversationId": "conv_conversation_xxxxxxxxxxxxxxxxxxxxxxxxxxxx",
6
"OperatorResult.OperatorName": "Sentiment",
7
"OperatorResult.KnowledgeBaseId": "know_knowledgebase_xxxxxxxxxxxxxxxxxxxxxxxxxxxx"
8
}
9
],
10
"meta": {
11
"key": "items",
12
"nextToken": null,
13
"pageSize": 1,
14
"previousToken": null
15
}
16
}

Run queries asynchronously

run-queries-asynchronously page anchor

To run queries asynchronously, use the QueryJobs resource instead of the Query resource. If you don't need results immediately, such as a query that takes longer than a synchronous request allows, you can submit it and check back for results later.

1
curl -X POST "https://insights.twilio.com/v3/InsightsDomains/Conversations/QueryJobs" \
2
-u $TWILIO_API_KEY:$TWILIO_API_SECRET \
3
-H "Content-Type: application/json" \
4
-d '{
5
"domain": "Conversations",
6
"query": {
7
"dimensions": ["OperatorResult.ConversationId", "OperatorResult.OperatorName", "OperatorResult.OperatorLabel"],
8
"filters": [
9
{
10
"expressions": [
11
{
12
"op": "GT",
13
"field": "OperatorResult.CreatedDate",
14
"values": ["2026-01-01"]
15
}
16
]
17
}
18
]
19
}
20
}'

Twilio returns an operationId and a statusUrl. See Asynchronous queries for how to poll for completion and retrieve results.


To learn more about Conversation Insights, see the following resources:

  • Core concepts: Understand cubes, measures, and dimensions.
  • Query syntax: Learn how to construct complex queries with filters, ordering, and pagination.