Conversation metadata
Metadata lets you store your own key-value data on a conversation, where it stays for the life of that conversation and is available to anything that reads the conversation.
A conversation rarely carries everything you need to act on it. The account it belongs to might live only in your CRM. The intent a Conversation Intelligence operator resolves isn't known until partway through the conversation. The escalation tier you want this conversation to follow is a decision you make, not a fact Twilio knows. Metadata is where you put any of it.
Metadata is a field on the Conversation resource. Set it when you create a conversation, update it as the conversation progresses, or both.
Metadata on capture rules is a different field
Capture rules also take a metadata field, which holds channel-specific filters such as voice callType. That field controls which traffic a rule matches and is unrelated to the conversation metadata described on this page. See Ingestion modes.
Metadata suits three kinds of data.
| Use case | What you store | Examples | What it unlocks |
|---|---|---|---|
| External references | Your foreign keys into other systems | A CRM contact ID, a billing account, an agent ID | Reporting that lines up conversations with the records you already maintain |
| Derived state | A conclusion reached during the conversation, such as a real-time language operator result your application writes back | A resolved intent, a qualification status, a risk flag |
|
| Per-conversation configuration | A toggle that changes behavior for one conversation without a code change | An escalation tier, a prompt version | One set of logic that adapts per conversation, instead of one deployment per variant |
Metadata isn't a general-purpose datastore. Values are short strings, and each conversation holds at most eight keys, so store identifiers and small pieces of state rather than records or transcripts. If you need more room, keep the record in your own system and store its identifier in metadata. For the exact caps, see Limits.
Don't store sensitive information in metadata
Never put payment card details, credentials, government identifiers, or protected health information in metadata. Store an identifier that points to that data in a system built to hold it, and keep the data itself out of the conversation.
Include a metadata object when you create the conversation. Record what you already know at that point, such as which of your records the conversation concerns:
1// Download the helper library from https://www.twilio.com/docs/node/install2const twilio = require("twilio"); // Or, for ESM: import twilio from "twilio";34// Find your Account SID at twilio.com/console5// Provision API Keys at twilio.com/console/runtime/api-keys6// and set the environment variables. See http://twil.io/secure7// For local testing, you can use your Account SID and Auth token8const accountSid = process.env.TWILIO_ACCOUNT_SID;9const apiKey = process.env.TWILIO_API_KEY;10const apiSecret = process.env.TWILIO_API_SECRET;11const client = twilio(apiKey, apiSecret, { accountSid: accountSid });1213async function createConversationWithConfig() {14const conversation = await client.conversations.v2.conversations.create({15configurationId: "YOUR_CONFIGURATION_ID",16name: "Leasing inquiry",17metadata: {18property_id: "prop_8842",19campaign_id: "spring_2026",20},21});2223console.log(conversation.id);24}2526createConversationWithConfig();
Response
1{2"id": "conv_conversation_00000000000000000000000001",3"accountId": "ACaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",4"configurationId": "conv_configuration_00000000000000000000000001",5"status": "ACTIVE",6"name": "Leasing inquiry",7"createdAt": "2023-07-01T12:00:00Z",8"updatedAt": "2023-07-01T12:30:00Z",9"metadata": {10"property_id": "prop_8842",11"campaign_id": "spring_2026"12}13}
The conversation now carries the metadata you provided when creating it.
For the full conversation creation flow, including participants, see Create conversations programmatically.
A GET request returns metadata along with everything else about the conversation:
1// Download the helper library from https://www.twilio.com/docs/node/install2const twilio = require("twilio"); // Or, for ESM: import twilio from "twilio";34// Find your Account SID at twilio.com/console5// Provision API Keys at twilio.com/console/runtime/api-keys6// and set the environment variables. See http://twil.io/secure7// For local testing, you can use your Account SID and Auth token8const accountSid = process.env.TWILIO_ACCOUNT_SID;9const apiKey = process.env.TWILIO_API_KEY;10const apiSecret = process.env.TWILIO_API_SECRET;11const client = twilio(apiKey, apiSecret, { accountSid: accountSid });1213async function fetchConversation() {14const conversation = await client.conversations.v215.conversations("YOUR_CONVERSATION_ID")16.fetch();1718console.log(conversation.id);19}2021fetchConversation();
The following response is abbreviated to the fields most relevant to metadata:
1{2"id": "conv_conversation_01k1etk2y5f1y9fpe2epfdtvv2",3"configurationId": "conv_configuration_01k1etk2y5f1y9fpe2epfdtvv2",4"name": "Leasing inquiry",5"status": "ACTIVE",6"createdAt": "2026-09-18T14:30:00Z",7"updatedAt": "2026-09-18T14:32:11Z",8"metadata": {9"property_id": "prop_8842",10"campaign_id": "spring_2026"11}12}
To change metadata on an existing conversation, make a PATCH request with only the keys you want to change:
1// Download the helper library from https://www.twilio.com/docs/node/install2const twilio = require("twilio"); // Or, for ESM: import twilio from "twilio";34// Find your Account SID at twilio.com/console5// Provision API Keys at twilio.com/console/runtime/api-keys6// and set the environment variables. See http://twil.io/secure7// For local testing, you can use your Account SID and Auth token8const accountSid = process.env.TWILIO_ACCOUNT_SID;9const apiKey = process.env.TWILIO_API_KEY;10const apiSecret = process.env.TWILIO_API_SECRET;11const client = twilio(apiKey, apiSecret, { accountSid: accountSid });1213async function patchConversationById() {14const conversation = await client.conversations.v215.conversations("YOUR_CONVERSATION_ID")16.patch({17metadata: {18resolved_intent: "schedule_tour",19},20});2122console.log(conversation.id);23}2425patchConversationById();
PATCH merges your changes into the metadata already on the conversation:
- Include a key to add it, or to update it if it already exists.
- Set a key to
nullto remove it. - Leave a key out to keep its current value.
PUT replaces metadata instead of merging it
A PUT request replaces the conversation's metadata with exactly what you send, so every key you leave out is removed. Use PATCH to change individual keys and leave the rest in place.
The request above sends one key, and the conversation keeps the two it already had:
1{2"metadata": {3"property_id": "prop_8842",4"campaign_id": "spring_2026",5"resolved_intent": "schedule_tour"6}7}
Because unmentioned keys are preserved, sending an empty metadata object changes nothing. To clear metadata, set each key you want to remove to null.
A conversation only accepts metadata changes while it is open. Once it reaches the CLOSED state, a PATCH request returns a 400 response, so write any value you need before the conversation ends. For when that happens, see Conversation lifecycle.
To free a slot, set the key to null:
1// Download the helper library from https://www.twilio.com/docs/node/install2const twilio = require("twilio"); // Or, for ESM: import twilio from "twilio";34// Find your Account SID at twilio.com/console5// Provision API Keys at twilio.com/console/runtime/api-keys6// and set the environment variables. See http://twil.io/secure7// For local testing, you can use your Account SID and Auth token8const accountSid = process.env.TWILIO_ACCOUNT_SID;9const apiKey = process.env.TWILIO_API_KEY;10const apiSecret = process.env.TWILIO_API_SECRET;11const client = twilio(apiKey, apiSecret, { accountSid: accountSid });1213async function patchConversationById() {14const conversation = await client.conversations.v215.conversations("YOUR_CONVERSATION_ID")16.patch({17metadata: {18campaign_id: null,19},20});2122console.log(conversation.id);23}2425patchConversationById();
You can't rename keys in place. Remove the old key and set the new one in the same request, so the value is never missing between requests:
1// Download the helper library from https://www.twilio.com/docs/node/install2const twilio = require("twilio"); // Or, for ESM: import twilio from "twilio";34// Find your Account SID at twilio.com/console5// Provision API Keys at twilio.com/console/runtime/api-keys6// and set the environment variables. See http://twil.io/secure7// For local testing, you can use your Account SID and Auth token8const accountSid = process.env.TWILIO_ACCOUNT_SID;9const apiKey = process.env.TWILIO_API_KEY;10const apiSecret = process.env.TWILIO_API_SECRET;11const client = twilio(apiKey, apiSecret, { accountSid: accountSid });1213async function patchConversationById() {14const conversation = await client.conversations.v215.conversations("YOUR_CONVERSATION_ID")16.patch({17metadata: {18campaign_id: null,19campaign_ref: "spring_2026",20},21});2223console.log(conversation.id);24}2526patchConversationById();
Every metadata change emits a CONVERSATION_UPDATED webhook event. Your other systems can react to a write instead of polling for one.
The event carries the full conversation, metadata included, so a handler reads the new values directly off the payload without fetching the conversation first:
1{2"eventType": "CONVERSATION_UPDATED",3"timestamp": "2026-09-18T14:32:11.254049339Z",4"data": {5"id": "conv_conversation_01k1etk2y5f1y9fpe2epfdtvv2",6"metadata": {7"property_id": "prop_8842",8"resolved_intent": "schedule_tour"9},10"status": "ACTIVE"11}12}
Note the contrast with PATCH. A request sends only the keys you want to change, but the event carries the whole metadata object, so a handler always sees current state. To work out what changed, compare against the values you held before.
Metadata is also included on CONVERSATION_CREATED, so values you set at creation reach your handler in the first event about that conversation. For the full payload structure, see Webhooks.
- A conversation holds at most eight metadata keys.
- Keys can be up to 128 characters and must match the regular expression
^[a-zA-Z0-9._-]+$, which allows letters, numbers, periods, underscores, and hyphens. The API rejects any other character, including spaces. - Values must be strings of up to 512 characters. To store a number, such as a loan amount, write it as a string.
- Keys cannot be renamed in place. To change a key, remove it and set the new one in the same request.
- Twilio encrypts metadata values at rest. Even so, do not store sensitive information in metadata.
- Metadata cannot change after a conversation reaches the
CLOSEDstate. APATCHrequest to a closed conversation returns a400response. - Metadata belongs to a single conversation. Twilio does not share it between conversations or copy it to a new conversation when one ends.
- Create conversations programmatically: Create conversations and participants through the API.
- Conversation lifecycle: Learn how conversations move between states and when they close.
- Core concepts: Understand conversations, participants, and configurations.
- Build on-demand workflows: Learn how to trigger rules and handle their results programmatically.