Operations deep-dive
Cannabis customer SMS — what carriers actually filter, what gets you blocked
Cannabis customer SMS is harder than regular retail SMS for three reasons stacked: (1) carrier filters (T-Mobile, Verizon, AT&T) block cannabis-keyword content aggressively under their A2P 10DLC enforcement; (2) WAC 314-55-155 caps the message content even before carriers see it; (3) operators routinely tank their sender reputation by leading with ‘$10 off!’ spam-bait copy. Most operators don’t notice until 40% of their messages stop landing. Here’s the discipline that keeps the channel open + the patterns that don’t trip filters.
By CannAgent6 min read
Why cannabis SMS is structurally hard
- **A2P 10DLC carrier filters.** Every business SMS in the US since 2023 must register a 10-digit-long-code (10DLC) campaign with the carriers. Cannabis is on the ‘restricted’ list; carriers approve campaigns at lower rates + sample messages aggressively. Send the wrong content + your registered campaign gets paused, not just the message blocked.
- **Cannabis-keyword filtering.** ‘THC,’ ‘flower,’ ‘edible,’ ‘ounce,’ ‘eighth’ — all soft-flagged. The combination of cannabis-keyword + discount-language + urgency-language is what carrier ML models score as spam. ANY ONE of those is fine; ALL THREE in one message and the deliverability rate drops to ~30%.
- **WAC 314-55-155(2)(c) advertising scope.** Customer-facing SMS that promotes cannabis products IS advertising. The age-disclaimer + audience-restriction rules apply. We can’t SMS to a number we haven’t verified is a 21+ customer (loyalty enrollment establishes that; cold prospect lists don’t).
- **Sender reputation compounding.** Once a number / shortcode hits a complaint threshold, ALL future messages from that sender get filtered harder. Recovery is months, not weeks. The discipline is preventing reputation damage, not recovering from it.
The 4 SMS types — and what works for each
| Type | Carrier filter risk | Recommended pattern |
|---|---|---|
| Transactional (order ready, account update) | Low | Lead with action: ‘Order ready for pickup at the front desk.’ No order-ID in body (PII discipline per memory). No discount tail. |
| Loyalty milestone (anniversary, birthday) | Low-mid | Lead relational: ‘Hey Alex, been a year since your first visit. Stop by when you’re around.’ No price-bait. No urgency. |
| Promotional (new product, weekly deal) | High | Lead with the product, not the discount. ‘New flower from <vendor> just landed; favorites of yours.’ No ‘LIMITED TIME’ / no exclamation marks / no $-bait. |
| Re-engagement (haven’t shopped in 60+ days) | Mid | Soft check-in: ‘Been a few weeks; everything good?’ Don’t lead with points-balance reminder (per memory: that pushes redemption + converts soft liability to hard discount). |
Pattern-level rules we run
- **One short, one URL, one CTA.** Carrier ML models score multi-link / multi-CTA SMS as spam. Single link → /menu OR /pickup, NEVER both.
- **Name the customer.** Per the named-customer-beat operator pattern: ‘Hey Alex’ > ‘Hey there.’ First names only; we never use last names in SMS body. Ungendered.
- **No exclamation marks.** Carrier filters score them as spam-bait. The voice-rule from /docs/brand-voice.md applies to SMS verbatim.
- **No price in the body.** ‘Save $10’ / ‘30% off’ / ‘BOGO’ all trip filters. Mention the offer if relevant; don’t structure the message around the price.
- **Phone normalization at the lib boundary, not the call site.** Per the operator pattern: lib/sms.ts sendSms() calls normalizeToE164() internally. Don’t pre-normalize at call sites; centralized + idempotent.
- **Quiet hours.** No SMS before 9 AM or after 8 PM in the customer’s tz. Carrier complaint rates spike outside that window.
- **Hard-stop on STOP.** Customer replies STOP → no more SMS to that number, period. Carrier rule + ethical floor + the unsubscribe page lives at /unsubscribe.
- **Cap frequency.** No more than 1 SMS per customer per 7-day window. Repeat sends compound carrier-filter risk + customer-irritation risk.
What we don’t do
- **No SMS to non-loyalty walk-ins.** Single-visit cash customers haven’t opted in beyond the WAC 314-55 retail interaction. Their phone is on file (if at all) for ID-scan purposes only.
- **No third-party SMS list rentals.** Doesn’t prove 21+ verification; carrier-side complaint rate destroys sender reputation; WSLCB cite-able if they audit.
- **No URLs to non-CannAgent / non-shop domains.** Off-domain links score as suspicious in carrier ML; brand confusion compounds.
- **No emojis in cannabis SMS body.** Carrier ML treats heavy-emoji content as spam-marker. Plain text only.
- **No order-ID in the customer SMS body.** Per the POS customer-display payload schema memory + the pickup-flow guide: order-IDs are operator-side; customer SMS just confirms readiness.
Takeaways
- Cannabis SMS hits 3 stacked headwinds: A2P 10DLC carrier filters / WAC 314-55-155 advertising scope / cannabis-keyword spam-bait detection. ALL THREE in one message → ~30% deliverability
- Lead with relationship + product, not discount. Discount-led SMS hits carrier filters AND trains customers to wait + accelerates margin-leak (per loyalty-design)
- Pattern: one URL / one CTA / first-name only / no exclamation marks / no price-in-body / quiet hours 9-8 / hard-stop on STOP / cap 1 SMS per customer per 7 days
- Loyalty-only audience is the WSLCB floor — opted-in 21+-verified. Cold lists + non-loyalty walk-ins are not in scope
- Sender reputation compounds. Monitor DELIVERY rate (not just send rate); recovery from a hit is months not weeks. Prevention is the discipline
Ready to talk through your migration?
30-minute demo. We end by quoting the cutover from your current setup — fixed scope, no hourly games.