SMS Quality Checks
SMS Inspect analyzes a text-message template before you send it — checking segmentation and encoding (what you'll be billed for), opt-out language, and the sender number. It re-uses the exact send-path engine, so the numbers you see in Inspect are the numbers you'll actually be charged.
What it checks
SMS Inspect runs a single stage against your stored SMS template (body_nlt and the optional from_number), rendered against a sample contact. It covers three concerns:
- Cost & encoding — how many segments the message occupies, and whether a stray character forced the expensive Unicode encoding.
- Compliance — whether the message carries recognizable opt-out language.
- Configuration — whether a per-template sender number override is valid.
- Rendering — whether the plain-English NLT expressions resolve against a sample contact.
Every segmentation, encoding, and cost number reported by SMS Inspect is read directly from render_sms, the same engine that runs at delivery time. Inspect never re-derives these values — so the segment count you see is the segment count you're billed for.
Segments and encoding: why length is about cost
SMS is billed per segment, and how much text fits in a segment depends on the encoding:
| Encoding | When it's used | One segment | Concatenated part |
|---|---|---|---|
| GSM-7 | Standard alphabet | 160 characters | 153 characters |
| UCS-2 | Any emoji or non-GSM character | 70 characters | 67 characters |
The critical thing to understand: a single non-GSM character forces the whole message into UCS-2, dropping the per-segment budget from 160 to 70. The usual culprits are invisible — a smart quote (’ instead of '), an em dash, an ellipsis, or an emoji.
That means a 90-character message you expected to send as one segment can quietly become two or three segments — and cost two or three times as much — because of one auto-inserted curly quote.
SMS Inspect surfaces this with three findings:
| Finding | Severity | Fires when |
|---|---|---|
SMS_UCS2_FORCED | warning | A non-GSM character forced UCS-2 encoding. |
SMS_MULTI_SEGMENT | warning | The body occupies more than one segment. |
SMS_BODY_TOO_LONG | warning | The body occupies more than three segments (4+). |
If you see SMS_UCS2_FORCED and SMS_MULTI_SEGMENT together, fix the encoding first — replacing the smart quote with a straight quote often drops you back to GSM-7 and resolves the segment count in one move.
Opt-out language
Recurring marketing SMS in the US is governed by A2P 10DLC and TCPA. Recipients need a clear way to stop messages, and carriers can filter senders that don't provide it.
SMS_NO_OPT_OUT_LANGUAGE (advisory warning) fires when the rendered body contains none of the standard opt-out phrasings — reply STOP, text STOP, opt out, opt-out, a whole-word STOP, or unsubscribe.
This is advisory only — it never blocks a send, because the opt-out instruction may legitimately be carried elsewhere (for example, in an earlier message in the conversation). Synapse also honors the platform-level STOP keyword regardless of message text; this check is about the visible instruction A2P guidance expects.
Sender number
An SMS template can override the sending number with a from_number field. SMS_FROM_NUMBER_INVALID (warning) fires when that override is present but does not normalize to a valid E.164 number.
Leaving from_number unset is valid and never triggers the finding — the Twilio Messaging Service selects the sender automatically. The check only fires when you set an override to an invalid value.
NLT rendering
SMS Inspect renders the plain-English NLT in your body against a sample contact:
| Finding | Severity | Page |
|---|---|---|
SMS_NLT_REQUIRED_MISS | critical | SMS_NLT_REQUIRED_MISS |
SMS_NLT_UNRESOLVED_EXPRESSION | warning | SMS_NLT_UNRESOLVED_EXPRESSION |
A required miss suppresses the whole message for matching contacts (a durable failed log row, no send). An unresolved expression still sends, but leaves the raw token visible to the recipient.
Running an SMS Inspect
Trigger a run against an SMS template by ID or slug:
See the Inspect API reference for the full request and response shape.