Twilio Conversations
SMS and WhatsApp bots on the Twilio Conversations API, with the conversation history kept on Twilio and files in both directions.
Twilio Conversations runs a Session per Twilio conversation on the TwilioSms and TwilioWhatsApp Channels. A new conversation starts a Session at the Process's WhatsApp start or SMS start, whose {message} holds the user's first message; every message after it continues the Session, and the conversation is closed on Twilio when the Session ends. Choose it when you want the conversation kept on Twilio: agent handoff with Twilio Flex, several participants, or the Conversations chat SDK. For a plain 1:1 bot, Twilio Messaging is simpler and cheaper (see Which Twilio module?).
The Module is part of the Twilio family and is configured on its own: it has its own Environment configuration with its own Twilio credentials, so fill them here even if you already set them on the Twilio Module. To send one-shot messages or one-time codes from the same Process, add the Twilio Module as well.
How it works
| Channel | Started when | What happens |
|---|---|---|
TwilioSms | An SMS starts a new Twilio conversation | A new Session starts on the TwilioSms Channel, at the Process's SMS start |
TwilioWhatsApp | A WhatsApp message starts a new Twilio conversation | Same path as SMS; the Session starts on the TwilioWhatsApp Channel (the inbound address carries the whatsapp: prefix), at the Process's WhatsApp start |
The same OutputTask saying "Hi there!" is delivered as an SMS or a WhatsApp message, depending on which Channel the Session started on.
Inbound SMS and WhatsApp arrive through a single unified webhook, POST /api/TwilioModule/Conversations/event/{token} (where {token} is the Environment's token). At Deployment time the Module configures your Twilio Conversation Service's post-event webhook with the filters onConversationAdded and onMessageAdded, both pointing at that one URL:
onConversationAdded(a new conversation starts) → a new Session starts, bound to that Twilio conversation. Its start waits for the first message.onMessageAdded(a message is added to the conversation) → the Session resumes with the message. The first one goes into the start's{message}; every later one answers theInputTaskthe Session is waiting at.
The user's first message produces both events, so it starts the Session and fills {message}; the flow's first InputTask then waits for the user's next message. Events for the same conversation are always processed in order, one at a time, even when several arrive in quick succession.
Quick start (5 minutes, SMS)
The fastest path to seeing an inbound SMS run a Process:
- Create a Twilio account. Note the Account SID (
AC...) and Auth Token on the Console dashboard. - Buy a Twilio number that can send SMS: Console → Phone Numbers → Buy a Number.
- Find your Conversations Service. Console → Conversations → Manage → Services → "Default Conversations Service" → note the SID (
IS...). Make sure your phone number is attached to a Messaging Service (MG...), and the Messaging Service is integrated with this Conversations Service ("Autocreate a Conversation" enabled). - Configure the Module. Open Environments, select your Environment, and open the Twilio Conversations configuration:
- Top section: Account SID + Auth Token.
- SMS Configuration section: paste the Conversations Service SID into Conversation Service SID and the Messaging Service SID into Messaging Service SID. Toggle Auto-Deploy SMS Webhook = on.
- Deploy a Process that depends on Twilio Conversations, with
SMS start → OutputTask("You said: {message.text}") → InputTask(Prompt="Anything else?", Variable="reply") → EndTask("Thanks, bye!"). The Deployment log shows a row like:"SMS post-event webhook updated on Conversation Service IS… → https://…/api/TwilioModule/Conversations/event/… (filters: onConversationAdded, onMessageAdded)"
- Text your Twilio number. The bot answers "You said: …" with the text you sent, then asks "Anything else?" and waits for your next message. Done.
For WhatsApp, fill in the WhatsApp Configuration section's Conversation Service SID too (usually the same service), and connect your WhatsApp sender to the same Messaging Service.
Webhook URLs are built from the platform's public engine address and registered on Twilio automatically by the Auto-Deploy toggles: you never have to construct them by hand.
Configuration
Configuration is per Environment: each Environment supplies its own Twilio credentials and service SIDs. Open Environments, select the Environment, and open the Twilio Conversations configuration. The form has the credentials at the top followed by two sections, SMS and WhatsApp, each with its own Auto-Deploy Webhook toggle.
Core Credentials
| Field | Type | Required | Description |
|---|---|---|---|
| Account SID | string (secret) | Yes | Twilio Account SID (AC...) |
| Auth Token | string (secret) | Yes | Twilio Auth Token, also used to verify the signature of Twilio's requests |
| Webhook Signing Secret | string (secret) | No | Optional override key for webhook signature checks |
| Validate Signatures | boolean | No (default: on) | Verify the signature on every inbound Twilio webhook |
The four fields are the same in every Module of the family; they are described on the Twilio Module page: Account SID, Auth Token, Webhook Signing Secret, Validate Signatures.
SMS Configuration
Inbound SMS routing via Conversations, plus the Messaging Service your numbers belong to.
| Field | Type | Required | Description |
|---|---|---|---|
| Conversation Service SID | string | For inbound SMS | Twilio Conversations Service (IS...) |
| Messaging Service SID | string | No | Twilio Messaging Service (MG...) your numbers belong to |
| Auto-Deploy SMS Webhook | boolean | No (default: off) | Register the Conversations webhook on every Deployment |
| Clear Messaging Service Inbound Webhook on Deploy | boolean | No (default: off) | Remove a Messaging Service webhook that would bypass Conversations, on every Deployment |
Conversation Service SID
Twilio Conversations Service SID, starts with IS. Required for inbound SMS routing. Example: ISxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.
Where to find it: Twilio Console → Conversations → Manage → Services → your service SID.
It is also used to close the conversation when an SMS Session ends, and to fetch the files users send.
Do not confuse the Conversation Service SID (IS...) with the Messaging Service SID (MG...). Twilio rejects the wrong one with error 50051 Service sid not provided, and the Deployment rejects it up front with a clear error.
Messaging Service SID
Twilio Messaging Service SID, starts with MG: the service your SMS numbers and WhatsApp senders belong to. Default: empty.
Where to find it: Twilio Console → Messaging → Services → your service.
The Module does not send through it (replies go into the conversation). It is used by Clear Messaging Service Inbound Webhook on Deploy, and by a check on every Deployment: when the service sends its inbound messages to the Twilio Messaging Module, they never reach Conversations, and the Deployment reports an error (see One number, one mode). Fill it in so this check can run.
Auto-Deploy SMS Webhook
When on, every Deployment updates the Conversation Service's post-event webhook with filters onConversationAdded and onMessageAdded, pointing at this Environment's unified event URL (.../api/TwilioModule/Conversations/event/{token}). Default: off.
Requires Conversation Service SID to be set and start with IS.
Both events land on the same URL; the engine routes each by its event type and processes events for the same conversation in order.
Clear Messaging Service Inbound Webhook on Deploy
When on, every Deployment clears the Messaging Service's Inbound Request URL and disables its Use webhook on number option, so inbound messages go to the Conversation Service autocreate path. This matters when the Messaging Service has a stale per-service or per-number webhook that would hijack inbound traffic, including a webhook set by the Twilio Messaging Module. Shared setting: applies to SMS and WhatsApp, because both use the same Messaging Service. Requires Messaging Service SID. Default: off.
Symptom that tells you to enable this: inbound WhatsApp replies arriving at the voice webhook URL, at a per-number legacy URL, or at a Twilio Messaging Environment, instead of at the /Conversations/event/… endpoint.
If the Messaging Service is used by a Twilio Messaging Environment, turning this on takes its numbers away from that Environment: its Sessions stop receiving messages.
WhatsApp Configuration
Inbound WhatsApp routing via Conversations.
| Field | Type | Required | Description |
|---|---|---|---|
| Conversation Service SID | string | For WhatsApp | Twilio Conversations Service (IS...) |
| Auto-Deploy WhatsApp Webhook | boolean | No (default: off) | Register the Conversations webhook on every Deployment |
| Migrate Active Conversations on Deploy | boolean | No (default: off) | Close running WhatsApp conversations so they re-bind to a new sender |
| Migration Message | string | No | Text posted into each conversation before the migration closes it |
Conversation Service SID
Twilio Conversations Service SID, starts with IS. Same place to find as SMS: Twilio Console → Conversations → Manage → Services.
In most setups this is identical to the SMS Conversation Service SID. Fill it in anyway: the TwilioWhatsApp Channel uses it to close the conversation when a Session ends and to send files.
Auto-Deploy WhatsApp Webhook
Same semantics as Auto-Deploy SMS Webhook. Needed when your WhatsApp Conversations Service is different from the SMS one; otherwise the SMS auto-deploy already covers WhatsApp. When both toggles are on and both sections hold the same SID, the Deployment updates the service once. Default: off.
Migrate Active Conversations on Deploy
Twilio pins each conversation to the sender address it started with, so moving to a new WhatsApp sender number has no effect on conversations that were already running. With this flag on, the Deployment walks every active conversation in the Conversation Service, posts the configured Migration Message (if any), and closes the conversation. On the user's next inbound message, Twilio creates a fresh conversation bound to the new sender. Default: off.
Only WhatsApp conversations are touched: SMS conversations in a shared service are left alone. The Deployment log reports how many conversations were closed, skipped and failed.
Toggle it off once the migration wave is done, otherwise every subsequent Deployment re-closes active conversations.
Migration Message
Plain text posted into every active WhatsApp conversation right before it is closed by the migration. Default: empty (close silently). The author shown to users is system.
Example: We're updating our WhatsApp service. Please send us a new message to continue the conversation.
Tasks
The Module has its own start for each Channel. Everything else uses the standard Standard Module Tasks, which behave Twilio-natively on these Channels: you don't pick anything special in the Process Editor; the right behavior is selected automatically from the Session's Channel.
Start tasks
A Process answers a Channel with that Channel's start: the WhatsApp start for TwilioWhatsApp, the SMS start for TwilioSms. Put one or both in the Process (one start per Channel); the engine begins each Session at the start serving its Channel. The start binds the Session to the conversation when it opens, then writes the user's first message into one variable, {message} by default:
| Reference | Value |
|---|---|
{message.text} | The text of the first message (empty when it carried only files) |
{message.files} | Its files, as a file list: {message.files.count}, {message.files.0}, {message.files.names} (see Files) |
{message.from} | The user's address (whatsapp:+39… on WhatsApp), also {execution.channelSender} |
{message.to} | Your Twilio address in the conversation (the proxy address), also {execution.channelRecipient} |
{message.messageId} | Twilio's message SID (IM…) |
{message.profileName} | Always empty on Conversations: Twilio does not send the WhatsApp profile name on this path (Twilio Messaging does) |
{message.channel} | TwilioWhatsApp or TwilioSms (Chat in test mode) |
{message.isTest} | true when the start ran in the test chat |
The first InputTask after the start waits for the user's next message: read the opening message from {message}.
Processes built before the Twilio starts, with the shared Start (Chat / API) event, keep working: their first message still answers the first InputTask. Replace the start with the Channel's own start to get {message}.
TwilioConversationsWhatsAppStartTask (Channel: TwilioWhatsApp)
Starts the Process when a WhatsApp message opens a new Twilio conversation.
| Property | Type | Required | Example | Description |
|---|---|---|---|---|
| Message variable | Store variable | Yes | {message} | Receives the first message of the conversation (fields above). Default: {message}. |
TwilioConversationsSmsStartTask (Channel: TwilioSms)
Starts the Process when an SMS opens a new Twilio conversation. Same property and fields as the WhatsApp start.
Test mode
In the portal's test chat both starts run in test mode ({message.isTest} = true), and the rest of the flow runs on the chat. Test data with a text (the assistant's chat_start_session with testData, for example { "from": "+393331234567", "text": "Hello" }) fills {message} at once; without one the chat asks "Test mode: write the WhatsApp message that starts the conversation" (or the SMS) and your next chat message, with its files, becomes {message}. On any other Channel the starts refuse to run (error 8025, StartWrongChannel).
WhatsApp buttons
The TwilioWhatsAppButtonsTask (a Task of Twilio Messaging: add that Module to the Process to find it) also runs in TwilioWhatsApp Sessions: it posts an approved WhatsApp template with buttons into the conversation and waits for the answer. On Conversations Twilio passes only the tapped button's title, not its ID: {choice.payload} and {choice.text} both hold the title and {choice.tapped} is false, so route on the button titles.
Channel override Tasks
- Start (Chat / API), the shared
StartTask(legacy): binds the Session to the Twilio conversation without a{message}variable; the first message answers the firstInputTask. Use the Channel's own start in new Processes. - InputTask posts its prompt into the conversation, then waits for the user's next message. On these Channels the Prompt Message is required (Twilio rejects empty messages); an
InputTaskwithout a prompt stops the Session with an error. The message text goes to Store result in, its files to Store files in (see Files). - OutputTask posts its text into the conversation, with the files of its Attachments field (see Files).
- EndTask ends the Session. It sends nothing: a farewell is an OutputTask right before the End Event.
Files
Both Channels carry files in both directions, with the platform's file variables.
Files the user sends
Set Store files in on the InputTask (for example files). The variable receives a file list:
| Reference | Value |
|---|---|
{files.count} | Number of files received with the message (0 when none) |
{files.0} | The first file, ready for an OutputTask's Attachments, a Create file / Read file step, or any Task that takes a file |
{files.0.name} | Its name (for example IMG_0042.jpg); files without a name get file-1.jpg, file-2.pdf, ... |
{files.names} | All names, comma-separated |
Limits per message: at most 5 files and 10 MB in total. Extra or oversized files, and files that fail to download, are skipped with a warning in the Session log; the text still arrives. A message with only a file (no text) is accepted: the text variable is empty.
The files of the first message are in the start's {message.files}, with the same limits. (Behind the legacy shared Start they reach the first InputTask, like its text.)
Files the bot sends
The OutputTask's Attachments is a file field: list the files separated by commas, Session files such as ref-session:{report} (a file made by Create file) or ref-session:{email.attachments}, or saved files such as ref-storage:terms.pdf (ref-workspace: for the Workspace). See File fields. At most 10 files and 15 MB per message.
WhatsApp (TwilioWhatsApp) sends real attachments, one file per message:
- When the first file is an image or a video, the text is its caption. Otherwise the text is sent first, on its own, and each file follows as a separate message.
- WhatsApp accepts JPEG and PNG images up to 5 MB, and up to 16 MB: OGG, AMR, 3GPP, AAC and MPEG audio, MP4 video, PDF, DOC, DOCX, PPTX and XLSX. Anything else (HTML, CSV, ZIP, plain text, larger files) is sent as a download link instead (see SMS below).
- Each file is uploaded to the Conversation Service's media store, so the WhatsApp Conversation Service SID must be filled in; without it, the step stops with error 4008.
SMS (TwilioSms) always sends files as download links (MMS works only on US and Canadian numbers): the text, then one line per file, name: link. For example:
Here is your report.
report.html: https://…
Links work for 7 days. They need the platform's public engine address; when it is not configured, the files are left out with a warning and the text is sent alone.
The conversation history (Sessions page) keeps the text and the files, not the links.
Conversation lifecycle
Every SMS or WhatsApp Session is bound to one Twilio conversation. When the Session ends, whether it completes normally or stops with an error, the Module automatically closes that conversation on Twilio's side. Closing it is what makes the user's next inbound message create a fresh conversation and therefore start a new Session.
This cleanup runs for every Session, including ones that end without an explicit EndTask. It needs the Channel's Conversation Service SID (SMS or WhatsApp section) in the configuration.
Common recipes
Auto-reply to inbound SMS with AI
A user texts your Twilio number; the Process answers their question with an AI-generated reply on the same conversation.
- SMS start: the Session starts automatically on the
TwilioSmsChannel when the user texts your number (with Auto-Deploy SMS Webhook on, no manual webhook setup is needed). The question is in{message.text}. - OpenAIChatCompletionTask (OpenAI Module): Prompt =
{message.text}, output variable =answer. - OutputTask: Message =
{answer}. - EndTask: the Session ends and the conversation closes, so the user's next text starts a fresh Session.
Receive a photo, send back a PDF
- WhatsApp start: a photo sent with the first message is
{message.files.0}. - SwitchTask on
{message.files.count}:0goes to anInputTaskwith Prompt =Please send a photo of the damage.and Store files in =files(a SwitchTask on{files.count}loops back to it with a reminder). - Process the file (
{message.files.0}or{files.0}) and build the PDF, for example as{claimPdf}. - OutputTask on
TwilioWhatsApp: Message =Here is your claim summary., Attachments =ref-session:{claimPdf}. The user receives the text, then the PDF as a WhatsApp document. OnTwilioSmsthe same step sends the text with a download link.
Troubleshooting
"50051 Service sid not provided" on Deployment
The Conversation Service SID field has a Messaging Service SID (MG...) instead of a Conversations Service SID (IS...). Move the MG... value into the Messaging Service SID field, then look up the correct IS... in Console → Conversations → Manage → Services.
"Messaging Service MG… sends inbound messages to the Twilio Messaging module"
The Messaging Service's Inbound Request URL points at a Twilio Messaging Environment, so inbound messages never reach Conversations. Use another Messaging Service (with its own numbers) for this Environment, or turn on Clear Messaging Service Inbound Webhook on Deploy to move the service to Conversations (the Twilio Messaging Environment then stops receiving its messages). See One number, one mode.
WhatsApp messages hit the voice webhook (or vice-versa)
Twilio's WhatsApp Sandbox has its own webhook URL in Console → Messaging → Try it out → Send a WhatsApp Message → Sandbox Settings. If you ever pasted a voice URL there, sandbox WhatsApp bypasses Conversations. Point that field at /api/TwilioModule/Conversations/event/{token} or leave it blank and rely on the Conversations integration.
Webhook returns 403
See Webhook returns 403. Remember that Twilio Conversations needs its own credentials in its own configuration.
The Deployment log shows nothing from Twilio Conversations
Neither Auto-Deploy SMS Webhook nor Auto-Deploy WhatsApp Webhook is on, so the deploy does not touch Twilio. Either turn the relevant toggle on, or configure the webhook manually in the Console using the Webhook URLs table.
Inbound SMS doesn't trigger any workflow
Three things to check, in order:
- The phone number is attached to a Messaging Service.
- The Messaging Service is integrated with a Conversations Service ("Autocreate a Conversation" enabled) and has no Inbound Request URL of its own (see Gotchas).
- The Conversations Service's post-event webhook points at your
/api/TwilioModule/Conversations/event/{token}URL with both filtersonConversationAddedandonMessageAdded.
If you have Auto-Deploy SMS Webhook on, step 3 is done for you on every Deployment.
A Session ends with error 8025 (StartWrongChannel)
The Session's Channel has no start of its own in the Process, and its only start belongs to another Channel (for example an SMS conversation but the Process has only the WhatsApp start). Add the start of that Channel.
The Session stops at the first InputTask
The InputTask has no Prompt Message. On the Conversations Channels the prompt is required: add one.
"sending files needs the channel's Conversation Service SID"
An OutputTask on TwilioWhatsApp sends an attachment but the WhatsApp Configuration section has no Conversation Service SID. Fill it in (usually the same SID as the SMS section).
Reference
Webhook URLs
These are the endpoints the Auto-Deploy toggles register for you. You only need this table if you configure Twilio manually. {token} is the Environment's token, shown in the Deployment log.
| Inbound | Engine route | Where it's configured |
|---|---|---|
Conversations events, SMS and WhatsApp (onConversationAdded + onMessageAdded) | POST /api/TwilioModule/Conversations/event/{token} | The Conversation Service's post-event webhook, with both filters selected. This is the endpoint for all inbound SMS/WhatsApp, set automatically by Auto-Deploy SMS/WhatsApp Webhook. |
Channels
| Channel | Purpose |
|---|---|
TwilioSms | Bidirectional SMS over Twilio Conversations |
TwilioWhatsApp | Bidirectional WhatsApp over Twilio Conversations |
WhatsApp notes
The WhatsApp rules (sandbox onboarding, the 24-hour session window, template approval) apply here too: see WhatsApp notes. Replies sent by an OutputTask are free-form messages, so they only reach the user within 24 hours of their last message.
Gotchas
Messaging Service Inbound Request URL hijacking inbound messages
If the Messaging Service has an Inbound Request URL set, it overrides the Conversation Service autocreate. Inbound WhatsApp / SMS never reaches the /Conversations/event/… endpoint: it hits whatever URL the Messaging Service points at (in one observed case, the voice webhook on the same phone number; or the endpoint of a Twilio Messaging Environment). Enable Clear Messaging Service Inbound Webhook on Deploy to wipe the override on every Deployment.
Replies come from the Messaging Service pool
Replies in an SMS conversation are posted without an explicit sender: Twilio picks the outbound number from the Messaging Service's sender pool. See Short-code entries in the Messaging Service pool and Italian SMS carrier rewrites.
Best practices
- Keep Validate Signatures on: disable it only briefly for local debugging.
- Fill both Conversation Service SIDs: even when SMS and WhatsApp share one service, the WhatsApp section's SID is what closes WhatsApp conversations and sends WhatsApp files.
- Fill the Messaging Service SID: it lets every Deployment check that the service still routes to Conversations.
- Turn migration off after the wave: leave Migrate Active Conversations on Deploy enabled only while you're actually changing the WhatsApp sender; otherwise every Deployment closes live conversations.
Error codes
See the full reference for resolutions:
| Code | Name | Description |
|---|---|---|
| 3005 | TaskInvalidData | An InputTask without a prompt, an OutputTask with neither text nor files, or an unusable attachment |
| 4001 | ModuleException | Twilio rejected a message or a media upload |
| 4008 | ModuleMissingConfiguration | Account SID, Auth Token, or the Conversation Service SID needed for files is not set |
| 4009 | ModuleInputEmpty | An inbound message had neither text nor files |
| 4012 | ModuleInvalidData | An inbound webhook payload was malformed (missing conversation identifier) |
| 8025 | StartWrongChannel | A Twilio Conversations start ran on another Channel: add that Channel's start |
| 4020 | WebhookSignatureInvalid | An inbound webhook failed the signature check |
| 4022 | ModuleLogWarning | Files skipped (over the limits, failed download) or links that could not be built |
| 9007 | PlanStorageLimitReached | The Workspace storage is full, so files could not be stored |