Every SMS conversation is a state machine
The moment an SMS exchange has more than one step (you send, they reply, you respond based on the reply), you've built a state machine, whether you called it that or not. A two-way SMS flow like 'confirm your appointment (reply C), reschedule (reply R), or ask a question (reply Q)' has states, transitions, and inputs. Model it explicitly as a state machine and it stays maintainable as it grows; model it as a pile of if-statements keyed on 'what did they last say,' and it becomes SMS conversation flow spaghetti that breaks every time you add a branch.
The core elements
| Element | What it is | Example |
|---|---|---|
| State | Where the conversation currently is | awaiting_confirmation, rescheduling, done |
| Input | What the user replied | 'C', 'R', free text, no reply |
| Transition | State + input → next state + action | awaiting_confirmation + 'C' → confirmed, send receipt |
| Timeout | What happens if no reply arrives | After 24h in awaiting_confirmation → remind or close |
| Context | Data carried through the conversation | The appointment id, the user's number |
Everything about a conversation reduces to these five. A user's inbound message is an input; you look up their current state, apply the transition for that (state, input) pair, take the action (send a reply, update a record), and move to the next state. Store the state and context keyed on the user's normalized number in international format (E.164), and the whole flow becomes a lookup rather than a tangle of conditionals.
The details that trip up conversations
- Handle unexpected inputs gracefullyUsers reply things you didn't anticipate: 'yes' instead of 'C', a question mid-flow, gibberish. Every state needs a default transition: re-prompt with the valid options, or route to a human, rather than silently dropping the message.
- Always honor global keywordsSTOP and HELP work in every state, no matter where the conversation is — they're not part of your flow's branching, they override it. This is a compliance requirement, not a design choice. Per CTIA guidelines, STOP must be honored globally.
- Set state timeoutsConversations don't always finish. A user who never replies leaves a dangling state; expire it after a sensible window (a reminder, then close) so stale states don't accumulate or misfire when the user replies days later.
- Plan the human handoffWhen the flow can't handle an input (a genuine question, an edge case, repeated confusion), hand off to a person cleanly, passing the conversation context so the human isn't starting blind.
The unexpected-input case is the one teams skip: always define a fallback reply for anything that isn't YES, STOP, or a recognized keyword.
Building it reliably
On the infrastructure side, a conversation flow is inbound handling plus state storage plus outbound sends — the pieces are already familiar. Inbound replies arrive on your webhook (handle them idempotently — a retried inbound shouldn't advance the state twice), you look up and update state, and outbound responses are normal sends with retry. The state machine sits on top, deciding what to say based on where the conversation is. For A2P messaging, ensure your application complies with carrier requirements like 10DLC registration in the US or DLT registration in India. Additionally, be aware of the GSM-7 160-character segment limit when crafting messages; longer texts require concatenation using UDH headers.
SMSRoute is a no-KYC SMS API with crypto billing (BTC, ETH, USDT, XMR, LTC, and SOL) providing the two-way delivery — inbound numbers, webhooks, and outbound sends; the conversation logic lives in your application, which is exactly where it belongs. And a masked-number conversation is just this pattern with an identity layer on top. SMSRoute's published route pages list delivery from $0.004/message (premium direct-carrier corridors up to $0.035) with sub-100ms median submission and ~98.6% delivered success (smsroute.cc route pages, 2026).
Related reading
FAQ
How do I design a multi-step SMS conversation?
How should an SMS flow handle unexpected replies?
Do STOP and HELP work inside an SMS conversation flow?
How do I hand off an SMS conversation to a human?
Send your first SMS in 5 minutes
No KYC. Pay with BTC, ETH, USDT, XMR, LTC, and SOL. Live routes to 149 countries.
Get an API key →