Skip to main content

Twilio Messaging

SMS and WhatsApp bots on Twilio Programmable Messaging: every message from a number starts or continues its Session, with files in both directions.

Twilio Messaging runs a Session per phone number on the TwilioMessagingSms and TwilioMessagingWhatsApp Channels. The first message from a number starts a Session at the Process's WhatsApp start or SMS start, which puts it in {message}; the next messages answer its InputTask steps, and when the Session ends the number's next message starts a new one. It is the simplest and cheapest way to build a 1:1 SMS or WhatsApp bot. If you need the conversation kept on Twilio (agent handoff with Twilio Flex, several participants, the Conversations chat SDK), use Twilio Conversations instead (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​

ChannelStarted whenWhat happens
TwilioMessagingSmsAn SMS arrives on a number of your Messaging Service and no Session is running for that numberA new Session starts on the TwilioMessagingSms Channel, at the Process's SMS start
TwilioMessagingWhatsAppA WhatsApp message arrives on a WhatsApp sender of your Messaging Service and no Session is running for that numberSame path as SMS; the Session starts on the TwilioMessagingWhatsApp Channel (the message was sent to a whatsapp: address), at the Process's WhatsApp start

A Process that answers both SMS and WhatsApp has both starts, usually joined right after them: the steps that follow read the same {message} fields whichever Channel the Session came from.

Inbound messages arrive at POST /api/TwilioModule/Messaging/inbound/{token} (where {token} is the Environment's token). At Deployment time the Module sets this URL as your Messaging Service's Inbound Request URL, so Twilio calls it for every message sent to the service's numbers and WhatsApp senders.

Sessions. One Session belongs to one number pair: the user's number and the number they wrote to (in this Environment). For each message:

  1. No Session running for the pair → a new Session starts, and the message is in the start's variable: {message.text}, {message.files}, {message.from}. The flow's first InputTask waits for the user's next message.
  2. A Session is running → the message answers the InputTask (or WhatsApp buttons step) the Session is waiting at.
  3. The Session ends (normally or with an error) → nothing to close on Twilio; the number's next message starts a new Session.

Messages from the same number are processed in order, one at a time, so two quick messages never start two Sessions. If the same user writes to two of your numbers, each pair has its own Session.

Replies (OutputTask, InputTask prompts) go out through the Twilio Messages API from the number the user wrote to, so the user always sees the same sender.

Quick start (5 minutes)​

  1. Create a Twilio account. Note the Account SID (AC...) and Auth Token on the Console dashboard.
  2. Get a sender: buy a Twilio number that can send SMS (Console → Phone Numbers → Buy a Number), and/or register a WhatsApp sender (or use the WhatsApp sandbox).
  3. Create a Messaging Service: Console → Messaging → Services → Create. Add your number to its Sender Pool (and your WhatsApp sender, if any). Note the service SID (MG...). Use a Messaging Service that no other Environment and no Twilio Conversations setup uses (see One number, one mode).
  4. Configure the Module. Open Environments, select your Environment, and open the Twilio Messaging configuration: fill Account SID + Auth Token, paste the service SID into Messaging Service SID, and toggle Auto-Deploy Inbound Webhook = on.
  5. Deploy a Process that depends on Twilio Messaging, with SMS start → OutputTask("You said: {message.text}") → EndTask("Thanks for texting us!") (use the WhatsApp start for WhatsApp, or both). The Deployment log shows a row like:

    "Messaging Service MG…: Inbound Request URL → https://…/api/TwilioModule/Messaging/inbound/…"

  6. Text your number (or send a WhatsApp message to your sender). The bot replies "You said: …" with your text, then "Thanks for texting us!". Your next message starts a new Session. Done.
info

Webhook URLs are built from the platform's public engine address and registered on Twilio automatically by the Auto-Deploy toggle: you never have to construct them by hand.

Configuration​

Configuration is per Environment: each Environment supplies its own Twilio credentials and Messaging Service. Open Environments, select the Environment, and open the Twilio Messaging configuration.

Core Credentials​

FieldTypeRequiredDescription
Account SIDstring (secret)YesTwilio Account SID (AC...)
Auth Tokenstring (secret)YesTwilio Auth Token, also used to verify the signature of Twilio's requests and to download the files users send
Webhook Signing Secretstring (secret)NoOptional override key for webhook signature checks
Validate SignaturesbooleanNo (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.


Messaging settings​

FieldTypeRequiredDescription
Messaging Service SIDstringFor auto-deployTwilio Messaging Service (MG...) holding your numbers and WhatsApp senders
Auto-Deploy Inbound WebhookbooleanNo (default: off)Point the Messaging Service's inbound webhook at this Environment on every Deployment

Messaging Service SID​

Twilio Messaging Service SID, starts with MG: the service that holds your SMS numbers and WhatsApp senders. Example: MGxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx. Default: empty.

Where to find it: Twilio Console → Messaging → Services → your service.

Without a Messaging Service, set each number's "A message comes in" webhook (and each WhatsApp sender's webhook) by hand to the URL in the Webhook URLs table.

Auto-Deploy Inbound Webhook​

When on, every Deployment sets the Messaging Service's Inbound Request URL to this Environment's endpoint (.../api/TwilioModule/Messaging/inbound/{token}) and turns off its Use webhook on number option, so the service URL is the one Twilio calls for every number of the service. Default: off.

Requires Messaging Service SID to be set and start with MG.

What the Deployment does depends on the URL the service has now:

Current Inbound Request URLResult
Empty, or already this EnvironmentSet (Deployment log: "Inbound Request URL → …")
Something else (a legacy URL, a voice webhook, ...)Replaced, and the log says so: inbound SMS and WhatsApp on its numbers now start Sessions here, and Twilio Conversations autocreate no longer receives them
Another Environment's Twilio Messaging endpointRefused with an error: the number belongs to that Environment. Use a separate Messaging Service per Environment, or turn off auto-deploy in the other Environment and clear its URL first

A Messaging Service sends inbound messages to its webhook instead of Twilio Conversations, so do not use the same service with the Twilio Conversations Module (see One number, one mode).


Tasks​

The Module has its own start for each Channel and a WhatsApp buttons step. 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 TwilioMessagingWhatsApp, the SMS start for TwilioMessagingSms. Put one or both in the Process (one start per Channel); the engine begins each Session at the start serving its Channel. Both write the message that started the Session into one variable, {message} by default:

ReferenceValue
{message.text}The text of the 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 number (whatsapp:+39… on WhatsApp), also {execution.channelSender}
{message.to}The number they wrote to, also {execution.channelRecipient}: every reply is sent from it
{message.messageId}Twilio's MessageSid
{message.profileName}The user's WhatsApp profile name (WhatsApp start only; empty on SMS)
{message.channel}TwilioMessagingWhatsApp or TwilioMessagingSms (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}, not from an InputTask.

Flows on the plain Start

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}.

TwilioMessagingWhatsAppStartTask (Channel: TwilioMessagingWhatsApp)​

Starts the Process when a WhatsApp message reaches a sender of the Messaging Service and no Session is running for that number.

PropertyTypeRequiredExampleDescription
Message variableStore variableYes{message}Receives the message that started the Session (fields above). Default: {message}.

TwilioMessagingSmsStartTask (Channel: TwilioMessagingSms)​

Starts the Process when an SMS reaches a number of the Messaging Service and no Session is running for that number. Same property and fields as the WhatsApp start; {message.profileName} is empty and MMS media (US and Canadian numbers) arrive in {message.files}.

Test mode​

In the portal's test chat both starts run in test mode ({message.isTest} = true, {message.channel} = Chat), 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": "I need an appointment", "profileName": "Anna" }): {message} is written at once and the flow goes on. On the WhatsApp start, from and to get the whatsapp: prefix when missing, like real WhatsApp addresses.
  • No text: the chat shows "Test mode: write the WhatsApp message that starts the conversation" (or the SMS); your next chat message, with its files, becomes {message}.

On any other Channel the starts refuse to run (error 8025, StartWrongChannel): add the start of that Channel to the Process.

TwilioWhatsAppButtonsTask (Channels: TwilioMessagingWhatsApp, TwilioWhatsApp)​

Sends a WhatsApp template with buttons and waits for the user's answer. Placed as a Message Intermediate Catch Event, like an InputTask. Runs in WhatsApp Sessions of Twilio Messaging (TwilioMessagingWhatsApp) and of Twilio Conversations (TwilioWhatsApp).

Use it for: confirmations (Confirm / Change), short menus, satisfaction ratings: anything where tapping beats typing. The step ends ok when the answer arrives; route on the answer with a SwitchTask.

Before you use it: create the template in Twilio Console → Messaging → Content Template Builder, with quick-reply buttons, and give each button an ID (the value the flow receives). WhatsApp delivers a template only once Meta has approved it: the builder shows the approval status. Approved templates are also the only messages that go through outside the 24-hour window.

PropertyTypeRequiredExampleDescription
Content SIDstringYesHXb5b62575e6e4ff6129ad7c8efe1f983eThe template's SID (HX + 32 characters), from the Content Template Builder. Supports {variable} placeholders.
Template variablesrowsNo1 = {customer.name}, 2 = {appointment.date}One row per template placeholder: the placeholder (1 for {{1}}) and its value. Values support {variable} placeholders and are sent as the template's Content Variables.
Preview texttextNoConfirm {appointment.date}? [Confirm] [Change]What the Session history shows for the message (the user sees the template), and what the test chat shows. Default: WhatsApp template HX….
Store answer inStore variableYes{choice}Receives the answer (below). Default: {choice}.

The answer:

ReferenceValue
{choice.payload}The tapped button's ID. When the user types instead of tapping, the text they wrote
{choice.text}The tapped button's title, or the text they wrote
{choice.message}The text of the reply as received
{choice.tapped}true when Twilio reported a tapped button

Example: a template Hi {{1}}, confirm your appointment on {{2}}? with buttons Confirm (ID confirm) and Change (ID change). Template variables 1 = {customer.name}, 2 = {appointment.date}; then a SwitchTask on {choice.payload}: confirm → confirmation message, change → reschedule path, default → "Please tap one of the buttons" and back to the buttons step.

In the test chat the step shows the preview text and takes what you type as the answer ({choice.payload} = your text). On a non-WhatsApp Channel it ends with error (3005): give SMS users a text alternative, for example a SwitchTask on {execution.channel} before it.

Channel override Tasks​

  • Start (Chat / API), the shared StartTask (legacy): binds the Session to the number pair without a {message} variable; its first message answers the first InputTask. Use the Channel's own start in new Processes.
  • InputTask sends its prompt, if any, then waits for the user's next message. The Prompt Message may be empty: the step just waits. The message text goes to Store result in, its files to Store files in (see Files).
  • OutputTask sends its text, 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.
WhatsApp 24-hour window

WhatsApp only delivers free-form messages within 24 hours of the user's last message; outside the window only approved templates go through (use TwilioSendWhatsAppTask with ContentSid). Twilio accepts the message and the failure happens afterwards on Twilio's side, so the step itself succeeds: check Twilio Console → Monitor → Logs → Messaging when a WhatsApp reply never arrives. See WhatsApp notes.

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:

ReferenceValue
{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. Twilio sends no file names on this path, so files are named by position and type: 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. Files are only downloaded from Twilio's own media address for your account; any other media URL is ignored with a warning.

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 (TwilioMessagingWhatsApp) 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).
  • Twilio fetches each file from a signed link to the platform, valid 1 day. The link needs the platform's public engine address; when it is not configured, the step stops with error 4008.
  • Twilio does not show file names for WhatsApp documents sent this way: the user sees the document without its original name.

SMS (TwilioMessagingSms) 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.

Common recipes​

Auto-reply to inbound SMS or WhatsApp with AI​

A user texts your number; the Process answers their question with an AI-generated reply.

  1. SMS start and/or WhatsApp start: the Session starts automatically when the user writes to your number (with Auto-Deploy Inbound Webhook on, no manual webhook setup is needed). The question is in {message.text}.
  2. OpenAIChatCompletionTask (OpenAI Module): Prompt = {message.text}, output variable = answer. With both starts, connect both to this step.
  3. OutputTask: Message = {answer}.
  4. EndTask: the Session ends, so the user's next message starts a fresh Session.

Receive a photo, send back a PDF​

  1. WhatsApp start: the opening message is in {message}; a photo sent with it is {message.files.0}.
  2. SwitchTask on {message.files.count}: 0 goes to an InputTask with Prompt = Please send a photo of the damage. and Store files in = files; continue with {files.0} from there.
  3. Process the file ({message.files.0} or {files.0}) and build the PDF, for example as {claimPdf}.
  4. OutputTask: Message = Here is your claim summary., Attachments = ref-session:{claimPdf}. On WhatsApp the user receives the text, then the PDF as a document; on SMS the text with a download link.

Confirm with WhatsApp buttons​

  1. WhatsApp start.
  2. TwilioWhatsAppButtonsTask: Content SID of an approved template Confirm your order {{1}}? with buttons Yes (ID yes) and No (ID no), Template variables 1 = {order.number}, Store answer in = {choice}.
  3. SwitchTask on {choice.payload}: yes → confirm the order; no → cancel; default → OutputTask("Please tap Yes or No.") and back to step 2.

Troubleshooting​

"Messaging Service MG… already sends inbound messages to another environment"​

Another Environment deployed Twilio Messaging with the same Messaging Service, so its numbers belong to that Environment. Use a separate Messaging Service (with its own numbers) per Environment, or turn off Auto-Deploy Inbound Webhook in the other Environment and clear the service's Inbound Request URL in Twilio Console → Messaging → Services → your service → Integration, then redeploy.

"Auto-deploy skipped: a Messaging Service SID (MG...) is required"​

Auto-Deploy Inbound Webhook is on but Messaging Service SID is empty or holds another kind of SID (for example a Conversations Service IS...).

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 arrived but the Process has only the WhatsApp start). Add the start of that Channel.

WhatsApp buttons: the template never arrives​

The template is not approved yet, or was rejected: check its status in the Content Template Builder. Twilio accepts the request and the failure shows in Twilio Console → Monitor → Logs → Messaging. A Content SID that is not HX + 32 characters stops the step with error 3005 before anything is sent.

Inbound messages don't start a Session​

  1. The number (or WhatsApp sender) is in the Messaging Service's sender pool.
  2. The last Deployment log shows "Inbound Request URL → …/Messaging/inbound/…" for that service. If a Twilio Conversations Environment with Clear Messaging Service Inbound Webhook on Deploy deployed after yours, it cleared the URL: the service can serve one mode only (One number, one mode).
  3. For the WhatsApp sandbox, the sandbox has its own webhook in Console → Messaging → Try it out → Send a WhatsApp Message → Sandbox Settings: set "When a message comes in" to your inbound URL.
  4. The webhook returns 403: see Webhook returns 403. Remember that Twilio Messaging needs its own credentials in its own configuration.

The Deployment log shows nothing from Twilio Messaging​

Auto-Deploy Inbound Webhook is off, so the deploy does not touch Twilio. Either turn it on, or set the webhook by hand using the Webhook URLs table.

"WhatsApp media needs Engine:PublicBaseUrl"​

The platform's public engine address is not configured, so Twilio has no link to fetch the file from. Contact support. Until then, send files on SMS or as text.

A WhatsApp reply never arrives​

Most often the 24-hour window has closed (the user's last message is older than 24 hours): only approved templates go through. The step succeeded because Twilio accepted the message; the failure shows in Twilio Console → Monitor → Logs → Messaging.

Reference​

Webhook URLs​

This is the endpoint the Auto-Deploy toggle registers for you. You only need this table if you configure Twilio manually. {token} is the Environment's token, shown in the Deployment log.

InboundEngine routeWhere it's configured
Inbound SMS and WhatsApp messagesPOST /api/TwilioModule/Messaging/inbound/{token}The Messaging Service's Inbound Request URL (Integration → Send a webhook), set automatically by Auto-Deploy Inbound Webhook. Without a Messaging Service: each number's "A message comes in" webhook, or the WhatsApp sender's webhook. Method POST.

The endpoint answers Twilio with an empty response; replies are sent separately through the Messages API.

Channels​

ChannelPurpose
TwilioMessagingSmsBidirectional SMS over Twilio Programmable Messaging
TwilioMessagingWhatsAppBidirectional WhatsApp over Twilio Programmable Messaging

Gotchas​

One Session per number pair​

A Session is tied to the user's number and the number they wrote to. A user who writes to two of your numbers has two independent Sessions; a user who writes again while a Session is running continues it rather than starting over. To let users restart, end the Session (an EndTask) at a natural point.

Italian SMS carrier rewrites​

Italian carriers may deliver your SMS from a shared short code, so the user cannot reply. See Italian SMS carrier rewrites.

Best practices​

  1. Keep Validate Signatures on: disable it only briefly for local debugging.
  2. One Messaging Service per Environment: the deploy refuses to take a service from another Environment, and a shared service would send every message to one Environment only.
  3. Read the opening message from {message}: the first InputTask waits for the user's next message, so ask its question only when you need more.
  4. End Sessions on purpose: a Session that never ends keeps catching the number's messages; finish each path with an EndTask.

Error codes​

See the full reference for resolutions:

CodeNameDescription
3005TaskInvalidDataAn OutputTask with neither text nor files, an unusable attachment, a WhatsApp buttons step with an invalid Content SID or outside a WhatsApp Session
4001ModuleExceptionTwilio rejected a message (invalid number, sender not in your account, ...)
4008ModuleMissingConfigurationAccount SID or Auth Token is not set, or WhatsApp media cannot be linked (no public engine address)
4009ModuleInputEmptyAn inbound message had neither text nor files
4012ModuleInvalidDataAn inbound message had no sender or recipient
8025StartWrongChannelA Twilio Messaging start ran on another Channel: add that Channel's start
4020WebhookSignatureInvalidAn inbound webhook failed the signature check
4022ModuleLogWarningFiles skipped (over the limits, failed download, foreign media URL) or links that could not be built
9007PlanStorageLimitReachedThe Workspace storage is full, so files could not be stored