twilio__sms service sends text messages: the one-time passcode, the appointment reminder, the short question a person is expected to answer. Two steps ship, both authored on the same shared REST foundation every other integration uses, and both authenticated by selecting a credential rather than by pasting a key into a step.
The two steps
A send addresses one recipient. A flow that texts a list does it by running the step per record, which is what keeps one bad number from failing everybody else’s message — and what lets the quiet-hours gate hold one recipient without holding the rest.
There is no media step and no media parameter in either direction: these steps send text and receive text. An inbound picture message is not silently dropped into your flow as an empty reply — media is simply out of scope for this service.
What a send takes
Anything over a single segment — 160 characters of plain GSM text, or 70 if the text contains an emoji, a curly quote or any other non-GSM character — is split by the carrier and billed per segment. The step reports how many segments were used.
What a send reports
No output field can carry a phone number, and that is structural rather than a convention. Step outputs are persisted into the platform’s run history, which outlives whatever retention rule governed the record the number came from — a number there could never be redacted afterwards. Twilio echoes both numbers back on a successful send and again on every webhook; those are read here only to produce the three fields above. The same rule governs failure messages: a refusal names the reason and the provider’s error code, never the number.
Recipients
Theto value must already be a plus sign, a country code and a national number, with no spaces, dashes or brackets: +15551234567. This step performs no normalization and guesses no country. Anything else is refused as RECIPIENT_NOT_E164 before any request leaves, so a malformed number costs no message, no money and no pacing budget.
That strictness is deliberate. A step that guessed would have to guess a country, and a wrong guess is a message to a stranger.
So normalize before the send, not inside it. The toE164 formatter exists for exactly this and belongs in a record transform step, where it runs over the whole record set and reports each unnormalizable row by name while the rest carry on:
"to": "{{ input.record.phone_e164 }}" — and every number reaching it is already in the shape it requires.
Both sender forms
sender accepts either of the two forms Twilio itself models, and the step works out which one you gave from its shape:
Exactly one form is supplied per message. Anything that is neither is refused as
SENDER_FORM_UNRECOGNIZED before the message is sent.
The choice is not purely stylistic — the two forms differ in what comes back afterwards:
Acceptance is not delivery
A successful send means Twilio accepted the message. It does not mean anyone received it. This is the one thing on this page most worth internalising, because every ordinary surface reads clean: the step succeeds, the execution reachescompleted, and the batch reaches completed — for a message a carrier may have dropped seconds later.
Delivery is reported afterwards, by Twilio, on a status callback that the platform records against the Job that sent the message: the provider’s final status (delivered, undelivered or failed), its error code where it names one, and when that answer last moved. The record only ever moves forward, so a late sent arriving behind a failed cannot walk the truth backwards.
Two practical consequences:
- Do not read
completedas “delivered” on a dashboard, in a downstream flow, or in a report you hand to someone else. It means accepted for sending — and for a bare-number send there may be nothing further to know. - A reply-waiting step acts on a delivery failure when it gets one. A
failedorundeliveredoutcome ends the wait immediately with a named reason, rather than parking the flow for hours on a person who was never texted — but see the sender caveat in when the message never arrives, because a reply-waiting step cannot use a Messaging Service.
The recorded delivery outcome is not yet published on the jobs API. It is written and it is monotonic; what is missing is a read surface. Until one ships, keep
provider_message_id from the step’s output — it is the handle that finds the message in your own Twilio account’s message log, where the same answer is visible whichever sender form you used.When a send is refused
Every unsuccessful send fails with a named reason rather than a status number, because the remedies are completely different:
Every one of these is non-retryable, and none of them repeats the number involved.
30034 is permanent, not transient. It fails every message from that number until the campaign is approved, so a flow that sees it is not having a bad minute — it is telling you a registration is missing. It also arrives in two shapes: as a refusal on the send when Twilio catches it up front, and as an undelivered delivery outcome afterwards when the carrier is the one that rejects it.Connecting Twilio
The credential is abasic_auth credential carrying your Twilio Account SID and your account auth token — the pair Twilio shows on its console dashboard:
Twilio subaccounts each produce their own record. A subaccount carries its own Account SID, so a customer running one subaccount per brand gets one tenant record per subaccount. That is correct rather than a duplicate: each subaccount has its own throughput budget, and metering two independent budgets against one ledger would over-send on both.
Registration is yours
Concretely, before your first US send: register the brand and the campaign in your own Twilio account, and attach the sending number or the Messaging Service to the approved campaign. Until that is done, sends from that number fail withCAMPAIGN_UNREGISTERED or come back undelivered with error 30034, and nothing on this platform can change that.
Opt-outs are Twilio’s too. STOP handling is provider-native, so a recipient who opts out is refused by Twilio on the next send and surfaced here as RECIPIENT_SUPPRESSED. There is no parameter, flag or override that reaches past it, and there is no separate GetDialed suppression list to keep in step.
Pacing, and sending exactly once
Sends are paced, and the pacing is the dispatcher’s job rather than the step’s. Every message draws from theSMS rate bucket on your Twilio Account record, spends it in the interactive lane, and waits rather than failing when the budget is spent. A text is user-visible latency — somebody is waiting on the passcode — which is why it sits in the lane reserved for work a person is watching, and never behind a bulk record load.
SMS is also the clearest case for the minimum interval: what a long-code A2P sender is really constrained by is the gap between two consecutive messages, not a per-hour count. The catalog seeds a deliberately conservative floor and the live, per-account value lives on the Account record.
Next steps
Quiet hours
When a text may legally be sent, how the recipient’s clock is worked out, and what a held message means.
Human in the loop
Pause a flow until somebody texts back, and how a reply is matched to the step that asked.
Credentials
Create the Twilio credential these steps select.
Dispatch pacing
How sends are paced against your account’s budget, and what a pause holds.