Skip to main content
The 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.
Text messages to US numbers are gated by law, not by preference. A marketing message is held until the recipient’s own local sending window is open, and marketing is what a step gets when it says nothing — the example above included. A step that genuinely sends something the recipient asked for and is waiting on declares traffic_class: "transactional", which an administrator must authorise. Read quiet hours before you author your first send: a held message is the single most likely reason a text you expected to go out immediately did not.

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

The to 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:
The send step then references the normalized column — "to": "{{ input.record.phone_e164 }}" — and every number reaching it is already in the shape it requires.
Do not chain | toE164 inside the send step’s own to expression. A send step’s parameters are resolved by the flow engine rather than by the step, and the phone-number formatter cannot run there: a task written that way does not fail cleanly — it stalls, with no error on the run and no message sent. Normalize the value in a record transform first, as above, or supply an already-E.164 value.This is a current limitation and it is being fixed. Until it is, treat the send step’s to as a plain reference to a value that is already correct.

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:
A Messaging Service cannot be used for send_and_wait. The service chooses a number from its pool at the moment the message goes out, so the reply comes back from a number the platform could never have registered a wait against — and the flow would sit until its timeout on an answer the person really gave. A reply-waiting step configured with a Messaging Service is refused before anything is sent, by name. Name one of your own numbers instead.

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 reaches completed, 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.
Delivery outcomes only arrive for messages sent through a Messaging Service. The callback is registered on the Messaging Service itself; a Twilio phone number has no equivalent setting, and a send step deliberately cannot supply a callback URL of its own — a URL a step could set would be one nobody could verify a signature against, and it would put a secret into flow definitions.So a send whose sender is a bare E.164 number still receives inbound replies — those arrive on the number — but produces no delivery outcome at all. Its delivery status stays empty, which means no answer was ever reported, not delivered and not failed.If delivery truth matters to a flow, send it through a Messaging Service.
Two practical consequences:
  • Do not read completed as “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 failed or undelivered outcome 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 a basic_auth credential carrying your Twilio Account SID and your account auth token — the pair Twilio shows on its console dashboard:
GetDialed calls Twilio with the pair, reads back which Account it belongs to, and files the credential under that account — you never look up or type a provider identifier, and a credential that cannot authenticate never becomes a record. See platform tenancy for what that record owns.
It must be the account auth token, not an API Key secret. A Twilio API Key — the credential whose identifier starts with SK — sends messages perfectly well, so the mistake is invisible on the send path. What it cannot do is verify the signature Twilio puts on inbound replies and delivery callbacks, because Twilio signs those with the account auth token and nothing else. The symptom is that replies never resume a waiting flow and delivery outcomes never arrive, which reads like a routing bug and is not one.
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

Customer SMS is bring-your-own registration.To send SMS to US phone numbers through a GetDialed flow, you supply your own SMS provider credentials and your own registered A2P 10DLC brand and campaign. GetDialed does not register 10DLC on your behalf. Unregistered US A2P traffic is blocked by the carriers, and when that happens your provider returns that error directly — GetDialed surfaces the provider’s response unchanged.
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 with CAMPAIGN_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 the SMS 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.
A send is never retried automatically. Both steps declare retry_safety: at_most_once in the catalog, which routes them to a single-attempt policy: if the answer to a send is lost in transit, the platform does not send again to find out.Twilio publishes no idempotency key, so there is no second line of defence at the provider’s end — the single-attempt declaration is the whole of the guarantee. That is the right trade here: a duplicate text cannot be recalled, it is charged for twice, and sending one to a consumer who did not ask for it is a compliance incident rather than a cosmetic defect.

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.