{"slug": "augment-voice-calls-with-twilio-conversation-intelligence-using-node-js", "title": "Augment Voice Calls with Twilio Conversation Intelligence Using Node.js", "summary": "Twilio published a Node.js tutorial showing developers how to combine Conversation Intelligence, Conversation Orchestrator, and Conversation Memory to generate a short summary of each customer call, an analysis of the caller's sentiment, and an assessment of whether the agent followed preset guidelines, then persist that data to a SQLite database. The tutorial adds a new Express route that receives a POST webhook from Twilio after calls end, with the JSON request body carrying the summary, sentiment, and criteria-adherence results; it requires Node.js v22 or higher, a voice-capable Twilio phone number, an OpenAI API key, and ngrok to expose the local server.", "body_md": "# Augment Voice Calls with Twilio Conversation Intelligence Using Node.js\n\nTime to read:\n\n**Written by**\n\n## [Augment Voice Calls with Twilio Conversation Intelligence Using Node.js](#introduction)\n\n[You know how to build a voice AI agent from scratch](https://www.twilio.com/en-us/blog/developers/tutorials/product/ai-phone-agent-twilio-conversation-relay), one that supports speech recognition, text-to-speech, turn detection, and real-time audio streaming — all at low latency. But, what if you could also analyze customer conversations in real time *and* collect useful data insights for future calls?\n\nWith Twilio [Conversation Intelligence](https://www.twilio.com/docs/conversations/intelligence) and [Conversation Orchestrator](https://www.twilio.com/docs/conversations/orchestrator), backed by [Conversation Memory](https://www.twilio.com/docs/conversations/memory), you can!\n\nSpecifically, in this tutorial you’re going to learn how to use these three technologies together to retrieve a short summary of each customer call, and an analysis of the caller’s sentiment. What’s more, you’ll also see whether your agent followed the guidelines you set for it. All of this information will then be persisted to a SQLite database, so that you can make use of it later.\n\n## [Programming language support](#programming-language-support)\n\nThis tutorial is geared toward Node.js developers. If you would like to build this project in a different programming language, see the following options:\n\n## [Architecture](#architecture)\n\nAs this tutorial adds three new technologies to the previous application, here’s a quick overview of how the new functionality works.\n\nYou will add a new route which receives a POST (webhook) request from Twilio after customer calls end. The request body will be a JSON string that contains, among other things, a short summary of the call, an assessment of the caller’s sentiment, and how the agent adhered to a series of criteria. That information will be extracted from the request and then persisted to the application’s SQLite database.\n\nYou’re not going to do more with the received information. But, there are links at the end of the tutorial showing how you could continue building on the changes made in this tutorial, should you want to.\n\n## [Prerequisites](#prerequisites)\n\nTo follow along with the tutorial, you will need the following:\n\n- A free Twilio account — [Sign up for an account here](https://login.twilio.com/u/signup)\n- A voice-capable Twilio phone number\n- The [Owl Air phone agent](https://www.twilio.com/en-us/blog/developers/tutorials/product/ai-phone-agent-twilio-conversation-relay-node) from the previous tutorial, or any Conversation Relay-based agent built with Node.js and[Express](https://expressjs.com/)\n- [Node.js](https://nodejs.org/en) v22 or higher installed on your machine\n- An [OpenAI API key](https://platform.openai.com/api-keys)\n- [The SQLite Command Line Shell](https://sqlite.org/cli) , or your preferred database admin tool (which has SQLite support)\n- [Git](https://git-scm.com/)\n- [ngrok](https://ngrok.com/) or a similar tool, to expose your local server to Twilio\n\n## [Build the app](#build-the-app)\n\n### [Step 1: Set up Conversation Orchestrator and Conversation Memory](#toc-heading-2dd9f408-e7aa-4ad7-969c-24868a3403e4)\n\nBefore you can set up Conversation Intelligence, which does most of the work, you need to create a Memory Store and Conversation Configuration.\n\nTo do that, sign in to the [Twilio Console](https://1console.twilio.com/), and go to **Products & Services > Conversation Orchestrator >** [Conversation configurations](https://1console.twilio.com/go?to=/account/__account__/us1/conversation-orchestrator/configurations). There, click **Create a Conversation configuration**. On the **Name Configuration** step, enter a name and description, then click **Next**.\n\nOn the  **Messaging and chat traffic** step, click  **Next**. On the  **Voice traffic** step, scroll down and enable the  **Set up automatic capture** checkbox. From the  **Voice phone numbers** list, select your Twilio phone number, then click  **Next**.\n\nNow, on the  **Configure lifecycle** step click  **Next**. After that, on the  **Enable Conversation Memory** step, create a memory store, by clicking  **Create new memory store**, entering a name in the  **Memory store name** field, and clicking  **Save**.\n\nFrom the  **Memory store** list, select the memory store you just created, leave  **Turn on observations and summaries** enabled, and click  **Next**.\n\nFinally, on the  **Summary** step, review your settings and click  **Create Conversation configuration**. Copy the  **conversation configuration ID** for use later.\n\n### [Step 2: Set up Conversation Intelligence](#toc-heading-0403bc62-186e-4b64-a2da-b63931db0135)\n\nBefore you can complete this step, you need to make the application publicly accessible on the internet, as you’ll need the ngrok URL later in this section. In a new terminal window, run the command below to create a connection to the app on port 8000.\n\nNext, go to  **Products & Services > Conversation Intelligence >** [Intelligence configurations](https://1console.twilio.com/go?to=/account/__account__/us1/conversation-intelligence/configurations). There, click  **Create Intelligence configuration**. Add a name, description, and, in the  **Attach Conversation configurations** section, select the name of the Conversation configuration that you created in the previous step, and click  **Submit**.\n\nWith that done, in Intelligence configurations, click  **Create rule** next to the Intelligence configuration which you just created. Then, in the  **Add language operators** section, enable  **Sentiment**,  **Summary**, and  **Script-Adherence**, and click  **Next**.\n\nNow, in the  **Script-Adherence** section, at the bottom of the  **Set Parameters** step, add the following text into the  **script** field and click  **Next**.\n\nNow, on the  **Trigger and action** step, choose  **At conversation end** in the  **Trigger** section. Then, in the  **Action** section, paste your ngrok Forwarding URL plus “/intelligence-results” in the  **Webhook action** field (for example, `https://1234abcd.ngrok.app/intelligence-results`). Click  **Next**.\n\nIn the  **Add context** step, scroll down to the  **Conversation Memory** section and enable  **Enable Conversation Memory for this rule** and click  **Next**. In the  **Summary** step, click  **Create rule**.\n\n### [Step 3: Update the existing project structure](#toc-heading-3a21c689-da25-46d7-84d4-d9f994f1e797)\n\nNow, it’s time to start augmenting the Node.js code. But, before you can do that, you have to add a few new directories. In your terminal, change into the root directory of your Owl Air project (the directory containing  *package.json*), and run the following command.\n\nThe  *data/database* directory will store the application’s SQLite database and a SQL file defining the database’s schema. The  *handlers* directory will contain the code that handles the webhook request. The  *services* directory will contain helper code for talking to the database and to Twilio. The  *types/operator-results* directory will contain a series of plain JavaScript classes which will store and model the information extracted from the webhook received from Twilio; you’ll create those classes shortly.\n\n### [Step 4: Set up the application’s database](#toc-heading-44f02085-3437-41b2-b9c7-7a751d3db745)\n\nCreate a new file named  *dump.sql* in the  *data/database* directory and paste the following SQL into that file.\n\nThe instructions:\n\n- Enable SQLite’s [WAL (Write-Ahead Logging) mode](https://sqlite.org/wal) (which, among other benefits,*significantly* improves performance)\n- Enable foreign key support\n- Define three tables:\n-  **intelligence_results:** stores the core information about the conversation\n-  **operators:** stores the information extracted by Conversation Intelligence, such as the call summary and sentiment. The`operator_id` column gives each record a unique, auto-incrementing ID\n-  **operator_script_adherence_categories:** stores the script adherence information, linking it to the relevant record in`operators` through its`operator_id`\n\nIt’s not the most sophisticated schema, but it can store the information in a maintainable way.\n\nNow, use SQLite’s Command-Line Shell (or your preferred database management tool) to provision the database with the following command.\n\nThe command prints `wal`, confirming that WAL mode is enabled, and creates the database file  *data/database/database.sqlite3*.\n\n### [Step 5: Create the Conversation Intelligence route](#toc-heading-c902b2af-c0d7-4742-bd7d-62135fbba483)\n\n#### [Add the required packages](#add-the-required-packages)\n\nThe application needs two extra packages: [better-sqlite3](https://www.npmjs.com/package/better-sqlite3), to simplify interacting with the application’s SQLite database, and the [Twilio Node.js Helper Library](https://www.npmjs.com/package/twilio), to query Twilio’s Conversations API. It also uses [dotenv](https://www.npmjs.com/package/dotenv) to load your credentials from a  *.env* file. To install them, run the following command in your terminal, from the root directory of your project.\n\nIf your project already has `twilio` or `dotenv` installed, npm updates them to the latest version.\n\n#### [Create a route and handler for processing the webhook](#create-a-route-and-handler-for-processing-the-webhook)\n\nNext, create the handler which processes the webhook request. In the  *handlers* directory, create a new file named  *intelligence-results-handler.js*. You’ll add the code to this file in two parts.\n\nFirst, paste the code below into the file.\n\nThe `importOperator()` function converts one operator result from the webhook into an object. It instantiates a `Sentiment` object from the `result.label` element, a `Summary` object from the `result.text` element, and a `ScriptAdherence` object from the `result.categories` element. Each category is marked as met when its `criteria_met` value is `Succeeded`. If Conversation Intelligence sends an operator that the app doesn’t know about, the function returns `null` so the operator can be skipped.\n\nThe `?.` (optional chaining) and `??` (nullish coalescing) operators protect the code from missing data. For example, `data.result?.label ?? ''` returns an empty string instead of throwing an error if `result` or `label` is missing.\n\nNow, paste the code below at the bottom of the same file, after the `importOperator()` function.\n\nThe `createIntelligenceResultsHandler()` function is the central focus of the file. It receives the Twilio client and the database service as arguments, and returns an Express route handler that uses them. Passing dependencies in this way, instead of creating them inside the handler, keeps the handler small and makes it easy to swap in test versions later.\n\nThe returned handler is called when the “/intelligence-results” route is requested. It starts off by reading the JSON request body, which Express has already parsed into a JavaScript object, before progressively extracting the essential information from it. This is the conversation ID (the conversation’s unique identifier), and the details that Conversation Intelligence determined about the call, contained in the `operatorResults` element, using the `importOperator()` function.\n\nWith the relevant information collected, the handler makes a call to [the Conversations (v2) API](https://www.twilio.com/docs/api/intelligence/v3/conversations/fetch-conversation) to get the start and end time of the call (details which aren’t available in the received webhook data). The Twilio Node.js Helper Library returns these as JavaScript `Date` objects in the `createdAt` and `updatedAt` properties. Then, using the `DatabaseService`’s `recordCall()` function, the collated information is persisted to the SQLite database. If anything goes wrong, the error is logged to the terminal.\n\nFinally, the handler responds to Twilio with a JSON response, confirming that the webhook was received.\n\n#### [Create the plain JavaScript classes](#create-the-plain-javascript-classes)\n\nNow, it’s time to create the classes to store the Conversation Intelligence data. Start off by creating a file named  *summary.js* in  *types/operator-results*, and paste the code, below, into the file.\n\nThen, create a file named  *sentiment.js* in  *types/operator-results*, and paste the code, below, into the file.\n\nCreate another file, this time named  *script-adherence.js* in  *types/operator-results*, and paste the code, below, into the file.\n\nThe `categories` property holds a list of `Category` objects, one for each category in the script. Create a file named  *category.js* in  *types/operator-results*, and paste the code, below, into the file.\n\nFinally, create a file named  *index.js* in  *types/operator-results*, and paste the code, below, into the file.\n\nThis file gathers all four classes in one place. When other files call `require('../types/operator-results')`, Node.js loads this  *index.js* file automatically, so they can import every class with a single line.\n\nJavaScript doesn’t have interfaces, so there’s no equivalent of a shared interface that the classes must implement. Instead, `Summary`, `Sentiment`, and `ScriptAdherence` each provide a `getValue()` method. This makes it simpler to work with them in `DatabaseService`, which you’ll create shortly, as it can call `getValue()` on any of them without checking which class it is.\n\n#### [Update the application’s routing table](#toc-heading-e5048b0b-b6bd-495d-8c65-644fc6ed433f)\n\nNext, you need to register the new route with Express. To do that, open your project’s  *index.js* file in the root directory.\n\nIf the file doesn’t already load environment variables with dotenv, add the following line as the very first line of the file.\n\nThen, add the following `require` statements near the top of the file, below your existing `require` statements.\n\nYou’ll create the  *database-service.js* and  *twilio-client.js* files in the next two sections.\n\nNow, add the code below after the line where you create your Express app (`const app = express();`), and above the `app.listen()` call.\n\nThis code creates the Twilio client and the database service once, when the app starts, and passes them to the handler. The `path.join()` function builds the full path to the SQLite database file, so it’s found no matter which directory you start the app from.\n\nThe new route accepts only POST requests to the “/intelligence-results” endpoint, passing requests through the [express.json()](https://expressjs.com/en/api#express.json) middleware (which parses JSON request bodies into `req.body`) and then to the handler. Because `express.json()` is added to this route only, it doesn’t change how your existing routes, such as the one that returns TwiML, process their requests.\n\n#### [Create the database service](#create-the-database-service)\n\nIt’s time to create the database service which the handler uses to simplify persisting the retrieved webhook data into the application’s database. In  *services*, create a file named  *database-service.js*, and paste the code below into the file.\n\nHere’s what the code does:\n\n- The `formatDate()` helper function converts a JavaScript`Date` into a string such as`2026-09-18 01:10:41` , which is how the call start and end times are stored.\n- The `getOperatorType()` helper function returns the value stored in the`operator_type` column, based on which class the operator is an instance of.\n- The constructor opens the SQLite database, turns on foreign key support for the connection, and creates three [prepared statements](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-statement) , one for each table. A prepared statement is an SQL query with named placeholders, such as`@conversationId` , which better-sqlite3 fills in with values when you call`run()` . Using placeholders, instead of building SQL strings by hand, protects the database from[SQL injection](https://owasp.org/www-community/attacks/SQL_Injection) .\n- The `recordCall()` function inserts one record into`intelligence_results` , then one record into`operators` for each operator. For the`ScriptAdherence` operator, it uses the ID of the newly inserted operator record (`lastInsertRowid` ) to link each category to it in`operator_script_adherence_categories` .\n\nAll of the inserts are wrapped in a [transaction](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#transactionfunction---function), using `this.db.transaction()`. This means that either every record for the call is saved, or, if something goes wrong part way through, none of them are. That way, your database never contains half of a call’s results.\n\nYou may notice that `recordCall()` isn’t an `async` function. better-sqlite3 runs its queries synchronously, which keeps the code short. For a local SQLite database, the queries finish quickly enough that this won’t slow down your voice agent.\n\n#### [Create a Twilio REST client](#create-a-twilio-rest-client)\n\nNow, you need to create a Twilio REST client, as the application will need it to query the Conversations (v2) API to retrieve a conversation’s start and end time in the handler. To do that, in  *services*, create a file named  *twilio-client.js*, and in that file paste the code below.\n\nThe `createTwilioClient()` function creates a new Twilio client with your Account SID and Auth Token, which are read from environment variables. You’ll add these values to your  *.env* file in the next step.\n\n### [Step 6: Start the application](#toc-heading-d5cc39d8-c8a2-478d-9be7-6258bbace068)\n\nWith the code now complete, you need to make sure that the application has all of the credentials it needs. Open the  *.env* file in the root directory of your project (or create it, if it doesn’t exist), and make sure it contains the following four environment variables.\n\nReplace each `XXXXXX` placeholder with its respective value:\n\n- `TWILIO_ACCOUNT_SID` and`TWILIO_AUTH_TOKEN` : your Twilio**Account SID** and**Auth Token** , which you can find in the**Account Info** section of the[Twilio Console](https://console.twilio.com/)\n- `DOMAIN` : your ngrok Forwarding URL, which your Owl Air agent uses to build its WebSocket URL\n- `OPENAI_API_KEY` : your OpenAI API key, from the[OpenAI dashboard](https://platform.openai.com/api-keys)\n\nIf your Owl Air agent already uses different names for any of these values, keep the names that the rest of your code expects.\n\nSave the file. Then, start the application by running the command below in your terminal, from the root directory of your project.\n\nYou will see output similar to the example below, after the application starts. The exact message depends on the `console.log()` call in your existing `app.listen()` code.\n\n## [Test that the app works as expected](#test-that-the-app-works-as-expected)\n\nWith the application running, call your Twilio phone number. You should hear Hoot’s greeting within a second or two of the call connecting:\n\n“Thanks for calling Owl Air! I’m Hoot. I can help with flight status, baggage policy, loyalty points, or booking changes. Which of those can I help you with?”\n\nThen, like when you tested the first version of the app, try a few test questions to verify the full flow is working:\n\n-  **“What’s the baggage policy?”** : Hoot should describe carry-on and checked bag rules in natural spoken language.\n-  **“How do loyalty points work?”** : Hoot should explain the earn and redemption rates.\n-  **“Can I change my flight?”** : Hoot should give the change fee policy, with amounts spelled out in words.\n\nAfter the call finishes, using your database tool of choice, have a look at the records in the application's database. There, you should see a summary of the conversation, along with the related operator information.\n\n## [Conclusion](#conclusion)\n\nYou’ve now learned how to use [Conversation Intelligence](https://www.twilio.com/docs/conversations/intelligence), [Conversation Orchestrator](https://www.twilio.com/docs/conversations/orchestrator), and [Conversation Memory](https://www.twilio.com/docs/conversations/memory), as well as [the Conversations API (V2)](https://www.twilio.com/docs/api/conversations/v2), to retrieve a short summary of each call and an analysis of the caller’s sentiment, and persist the information to a SQLite database, so that you can make use of it later. What’s more, you also know whether your agent followed the guidelines you set for it.\n\nBut don’t stop there! Now that the application can store conversation information, why not add a route for viewing a summary of all stored conversations, and one for viewing individual conversation details?\n\nThen, I strongly encourage you to learn more about [Conversation Intelligence](https://www.twilio.com/docs/conversations/intelligence), [Conversation Orchestrator](https://www.twilio.com/docs/conversations/orchestrator), and [Conversation Memory](https://www.twilio.com/docs/conversations/memory), as well as [the Conversations API (V2)](https://www.twilio.com/docs/api/conversations/v2).\n\n *Dhruv Patel is a Developer on Twilio’s Developer Voices team. You can find Dhruv working in a coffee shop with a glass of cold brew or he can be reached at dhrpatel [at] twilio.com.*\n\n## Related Posts\n\n## Related Resources\n\nTwilio Docs\n\nFrom APIs to SDKs to sample apps\n\nAPI reference documentation, SDKs, helper libraries, quickstarts, and tutorials for your language and platform.\n\nResource Center\n\nThe latest ebooks, industry reports, and webinars\n\nLearn from customer engagement experts to improve your own communication.\n\nAhoy\n\nTwilio's developer community hub\n\nBest practices, code samples, and inspiration to build communications and digital engagement experiences.", "url": "https://wpnews.pro/news/augment-voice-calls-with-twilio-conversation-intelligence-using-node-js", "canonical_source": "https://www.twilio.com/en-us/blog/developers/tutorials/product/augment-voice-calls-twilio-conversation-intelligence-nodejs", "published_at": "2026-09-30 00:00:00+00:00", "updated_at": "2026-10-08 10:46:39.989882+00:00", "lang": "en", "topics": ["ai-agents", "natural-language-processing", "ai-products", "developer-tools"], "entities": ["Twilio", "Twilio Conversation Intelligence", "Twilio Conversation Orchestrator", "Twilio Conversation Memory", "Node.js", "Express", "OpenAI", "SQLite"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/augment-voice-calls-with-twilio-conversation-intelligence-using-node-js", "markdown": "https://wpnews.pro/news/augment-voice-calls-with-twilio-conversation-intelligence-using-node-js.md", "text": "https://wpnews.pro/news/augment-voice-calls-with-twilio-conversation-intelligence-using-node-js.txt", "jsonld": "https://wpnews.pro/news/augment-voice-calls-with-twilio-conversation-intelligence-using-node-js.jsonld"}}