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

Send personalized bulk messages and emails with Cohorts


Twilio Cohorts lets you send bulk messages across SMS, RCS, WhatsApp, and email without maintaining a static recipient list. When you target a cohort, Twilio resolves each recipient's profile traits at send time. Every message can include dynamic values, such as a first name, net spend, or plan tier.

Use this guide to plan, build, and monitor a personalized cohort-based send.


Prerequisites

prerequisites page anchor

Before you begin, confirm you have the following:

  • A Twilio account with access to Twilio Cohorts, Bulk Messaging, and Email.
  • Your API key and secret, available from the Twilio Console.
  • A connected profile store containing the traits you want to personalize on. The traits referenced in your personalization expressions must exist on the profiles that match your cohort criteria.
  • Familiarity with cohort filter criteria. If you're new to Cohorts, start with Getting started with Twilio Cohorts.

Step 1: Define the audience with a cohort

step-1-define-the-audience-with-a-cohort page anchor

Create a cohort whose filter criteria describes the population you want to reach. For personalization to work, the criteria should reference traits and address fields that exist on the profiles you expect to match.

The following criteria targets active enterprise customers in the US:

profile.trait.Account.status == "active" && profile.trait.Account.plan == "enterprise" && profile.trait.Account.country == "US"

Save the cohort so you can reference it by cohortId at send time. For the full request payload, see Cohorts.


Step 2: Validate before you send

step-2-validate-before-you-send page anchor

Preview a sample of matching profiles to confirm the filter captures the population you expect. Project the same fields you plan to reference in your personalization so you can spot missing or malformed values before they end up in a customer message.

For the exact request, see Validate cohort criteria with snapshots.


Step 3: Send with runtime personalization

step-3-send-with-runtime-personalization page anchor

Reference the cohort by cohortId in your Bulk Messaging or Email request. Define each personalization value under variables as a Common Expression Language (CEL) expression wrapped in ${…}. Reference the same variable in the message content with Liquid {{ … }} syntax. For Liquid syntax, filters, and control flow, see Personalize message content.

Bulk Messaging (SMS) example

bulk-messaging-sms-example page anchor
1
curl -X POST 'https://comms.twilio.com/preview/Messages' \
2
-H 'Content-Type: application/json' \
3
-d '{
4
"from": {
5
"address": "<Your Purchased Twilio Phone Number>",
6
"channel": "SMS"
7
},
8
"to": [
9
{
10
"cohortId": "cmp_cohort_01h9d8r0vte3hz8tykdj329t7r",
11
"variables": {
12
"firstName": "${profile.trait.Contact.firstName}",
13
"netSpend": "${profile.trait.Metrics.revenue - profile.trait.Metrics.refunds}",
14
"planTier": "${profile.trait.Account.plan}"
15
}
16
}
17
],
18
"content": {
19
"text": "Hi {{ firstName | default: '\''there'\'' }}, thanks for being on our {{ planTier }} plan. Your net spend with us is {{ netSpend }}."
20
}
21
}' \
22
-u $TWILIO_API_KEY:$TWILIO_API_SECRET
1
curl -X POST 'https://comms.twilio.com/preview/Emails' \
2
-H 'Content-Type: application/json' \
3
-d '{
4
"from": {
5
"address": "support@example.com",
6
"name": "Support Team"
7
},
8
"to": [
9
{
10
"cohortId": "cmp_cohort_01h9d8r0vte3hz8tykdj329t7r",
11
"variables": {
12
"firstName": "${profile.trait.Contact.firstName}",
13
"planTier": "${profile.trait.Account.plan}"
14
}
15
}
16
],
17
"content": {
18
"subject": "Hi {{ firstName | default: '\''there'\'' }}, an update on your {{ planTier }} plan",
19
"html": "<p>Hi {{ firstName | default: '\''there'\'' }}, an update on your {{ planTier }} plan is available.</p>",
20
"text": "Hi {{ firstName | default: '\''there'\'' }}, an update on your {{ planTier }} plan is available."
21
}
22
}' \
23
-u $TWILIO_API_KEY:$TWILIO_API_SECRET

Twilio evaluates each expression against the matching profile at send time and substitutes the resulting values into the message content. To format numeric values, such as a currency amount or a rounded decimal, apply Liquid filters in the message content. For limits on personalization expressions and the requirements for the to array, see Limits and validation.


A missing trait and an empty trait behave differently, so protect against both.

If a profile doesn't have the trait key at all, the send fails for that recipient, and the failure appears in the Operation. The Liquid default filter doesn't cover this case. To exclude those profiles from the audience, add a presence check to the cohort criteria:

has(profile.trait.Contact.firstName) && profile.trait.Account.status == "active"

For more information, see Null handling in CEL.

If the trait key exists but has no value, the default filter supplies a fallback:

Hi {{ firstName | default: 'there' }}

Lock the recipient list before sending

lock-the-recipient-list-before-sending page anchor

If you need to review or audit the exact recipient list before sending, use a snapshot instead of the cohort ID. For example, you might do this for a regulated campaign, an approval workflow, or a scheduled send that must not shift with data changes. Generate the snapshot, review it, and then pass its cohortSnapshotId in your send request. Personalization variables still resolve against the profile data captured in the snapshot.