SMS Channel
Norq compiles SMS templates from Markdown to plain text with automatic segment counting and encoding detection -- GSM-7 (standard ASCII, 160 chars/segment) vs UCS-2 (Unicode, 70 chars/segment).
Pipeline: Markdown -> AST (parse tree) -> plain text + segment count + encoding detection
Template file
sms.md -- always Markdown mode. Native JSON is not supported for SMS.
Frontmatter
---
enabled: true
---| Field | Required | Description |
|---|---|---|
enabled |
No | true (default) or false. Disables the channel. |
SMS has no subject or other channel-specific frontmatter fields.
Directive compilation
| Directive | SMS output |
|---|---|
:::header |
Ignored |
:::footer |
Appended after a blank line |
:::action |
"Label: url" plain text |
:::callout |
Rendered as plain text (no styling) |
:::hero |
Ignored |
:::fields |
Plain text lines ("Key: Value") |
:::media |
URL as plain text; ignored for non-URL content |
:::columns |
Stacked sequentially as text |
:::list |
Numbered lines |
:::highlight |
Star prefix (⭐ text) |
:::centered |
Plain text (no alignment) |
:::raw |
Ignored |
Example
Hey {{user.first_name}}, order #{{order.id}} shipped! Track: {{tracking_url}}
::: footer
Reply STOP to unsubscribe.
:::Highlight
Rendered with a star prefix to draw attention in a plain-text medium.
::: highlight
Flash sale: 30% off today only!
:::Compiles to: ⭐ Flash sale: 30% off today only!
Compiled output
Norq produces three fields:
body: The plain text messagesegments: Number of SMS segments (1 segment = 160 GSM-7 chars or 70 UCS-2 chars)encoding:"gsm7"or"ucs2"
Example output:
{
"body": "Hey Gaurav, order #ORD-123 shipped! Track: https://track.example.com/123\n\nReply STOP to unsubscribe.",
"segments": 1,
"encoding": "gsm7"
}Segment counting
SMS messages are split into segments based on encoding:
| Encoding | Single segment | Multi-segment (per segment) |
|---|---|---|
| GSM-7 | 160 characters | 153 characters |
| UCS-2 | 70 characters | 67 characters |
GSM-7 covers ASCII letters, digits, and common punctuation. If the message contains any character outside GSM-7 (e.g., emoji, accented characters, CJK), the entire message switches to UCS-2.
Testing segment count
Use tests.yaml to enforce segment limits:
tests:
- name: "SMS fits 1 segment"
channel: sms
sample: "New user"
assert:
sms_segments: { lte: 1 }
- name: "SMS under 2 segments"
channel: sms
sample: "Shipped with discount"
assert:
sms_segments: { lte: 2 }Best practices
- Keep messages under 160 characters for a single segment (cheapest delivery)
- Avoid emoji if you want GSM-7 encoding (fewer characters per segment with UCS-2)
- Use
{{url | truncate 30}}if URLs are long and you need to save characters - Put opt-out text in
:::footerfor consistent formatting - Use
norq preview <name> --channel sms --terminalto see the message and segment count