An abstraction that lets you write send(to, text) and pick a channel at the last moment is genuinely useful. It is also the source of most of the trouble that follows, because it invites you to believe the channels behind it are the same thing with different logos.
They are not. They differ on four axes that matter.
| Channel | Who may start | What silence tells you |
|---|---|---|
| SMS | Anyone registered, subject to consent | Nothing at all — delivery and success look identical |
| Only with a template, outside the 24-hour window | Read receipts, so silence is a real signal | |
| RCS | As SMS, plus operator deployment | Delivery and read, where the network supports it |
| Anyone, with the punishment deferred to a classifier | An open rate that has been unreliable for years |
Who is allowed to start#
On SMS, you may initiate — subject to registration and consent, but the protocol has no opinion. On WhatsApp, you may only initiate with a pre-approved template; a free-form message to someone who has not written to you recently will be refused. On email, initiation is free and the punishment for abusing it is delivered later, quietly, by a spam classifier.
Any system that models "sending a message" without modelling "am I allowed to start this conversation" will work in testing and fail in production.
What may be said#
SMS is 160 characters of text per segment, and the segment boundary is where surprise costs live — particularly with non-Latin scripts, where the per-segment character count drops sharply.
WhatsApp templates must be approved before use, and the category you file them under determines how they are billed. RCS carries rich cards and suggested replies, which changes what a "message" even means.
What happens on silence#
This is the axis people forget. On SMS, silence is indistinguishable from success. On WhatsApp you get read receipts. On email you get an open rate that has been unreliable since inbox providers started pre-fetching images.
The design consequence: a journey that waits for a reply must define what to do when nothing comes back, and the right wait is different per channel.
What it costs to be wrong#
Sending the wrong category on WhatsApp costs money and may get a template rejected. Sending unregistered A2P traffic in the United States means carriers drop it — since February 2025 they block unregistered 10DLC entirely. Sending promotional SMS in the UAE without the required prefix is a regulatory matter, not a deliverability one.
The abstraction is still worth having. Just build it so that channel-specific rules are enforced inside it rather than remembered by whoever calls it.
The abstraction that survives#
Given all of that, the useful shape is not "hide the channels" but "make the rules unavoidable".
Put the constraint check inside the abstraction, not in the caller. A send function that accepts an intent, a contact and a consent basis, and returns a refusal with a reason when the combination is not permitted, is doing real work. One that accepts a channel name and a string is a thin wrapper that has moved the problem somewhere less visible.
The test is simple. Ask what happens when someone calls it wrongly. If the answer is "it sends anyway and we find out from a complaint", the abstraction is decoration.
Channel capability is not static#
A second trap: capability is treated as a constant when it is a property of a moment.
Whether a number can receive rich messages depends on the handset, the operator and the market, and all three change. Whether a WhatsApp free-form reply will send depends on when the customer last wrote. Whether an SMS route is healthy depends on what happened in the last twenty minutes.
Any code that decides this once, at configuration time, will be wrong later. Decide at send time, cache briefly, and treat a stale answer as a reason to fall back rather than to fail.
A short worked example#
An order ships. You want to tell the customer.
- On SMS: allowed if you hold a basis and the campaign is registered in that market. Costs more in Arabic than in English because of the encoding. Arrives in seconds. You will never know whether it was read.
- On WhatsApp: requires a template unless the customer wrote to you in the last day. Costs by category. You get delivery and read receipts, and they can reply — which is often more valuable than the notification.
- On email: free, but competing with everything else in an inbox, and your open rate is not measuring what you think it is.
- On RCS: the same message with a tracking button, where the network supports it and the handset can receive it — and the SMS version anyway, for everyone else.
Four channels, one event, four different designs. That is the whole argument.
What to take away#
- Model who is allowed to start a conversation, not just how to send one.
- Silence means something different on every channel; decide what to do about it per channel.
- Capability is a runtime lookup, not a configuration value.
- Enforce channel rules inside the abstraction, or they will be enforced by a regulator.
Sources
Every claim worth checking, with somewhere to check it.
- Ten-Digit Long Code (10DLC) Registration GuidelinesMicrosoft Learn
- WhatsApp Business Platform pricingMeta for Developers