TL;DR
Purchase is now solid: search → address → regulatory bundle → buy → voice webhook auto-configured. International compliance is covered.
The #1 gap is US A2P 10DLC. Regulatory bundles do not register US messaging — unregistered US local numbers get their SMS blocked by Twilio & carriers. For a US org, messaging effectively doesn't work yet.
Toll-free verification is the simpler sibling path — one form, no campaign fees — and is also missing.
Small compliance + UX gaps compound: no STOP/HELP auto-reply, failed messages show a generic "Failed", numbers can't be released.
Locked order (Q1): toll-free verification → STOP/HELP responder → 10DLC → error surfacing, then number lifecycle, porting, caller-ID branding. Setup is assistant-driven (Q2).
01What we have today
The journey a user can already complete, and where each piece lives.
| Capability | Status | Where |
|---|---|---|
| Per-org subaccount or BYO credentials | solid | organization_twilio_settings, create_twilio_subaccount_util, /api/twilio/connect |
| Number search & purchase (country, type, area code, pattern) | solid | buy_phone_component, buy_phone_number_api |
| Regulatory addresses (CRUD) | solid | phone_addresses_section_component, addresses utils |
| Regulatory bundles — wizard, docs, submit, status webhook, attach at purchase | solid · new | regulatory_bundle_service, phone_bundles_section_component, /twilio/bundle-status |
| Voice webhook auto-config at purchase | solid | set_phone_number_twiml_util, voice_webhook_active |
| Per-number config: voice/text agent, routing mode, auto-reply, member assignment | solid | phone_details_component, member_phone_picker_component |
| SMS + MMS send, delivery status, inbound via Event Streams | solid | send_message_api, twilio_events_api |
| STOP/UNSUBSCRIBE detection → opt-out record → pre-dispatch block | partial | detect_opt_out_keyword, is_channel_opted_out_service — no confirmation auto-reply |
| US A2P 10DLC (brand, campaign, messaging service) | missing | — |
| Toll-free verification | missing | — |
| Number release / porting / CNAM | missing | — |
02What the bundle work covers — and what it doesn't
Twilio has two unrelated compliance systems that are easy to conflate. We built one of them.
✓ Built — Regulatory bundles (Numbers v2)
Identity proof required to purchase numbers in regulated countries (DE, FR, AU, GB mobile, …). Gate is at buy time: no approved bundle → Twilio refuses the purchase (errors 21649/21650). Once bought, the number works.
✗ Not built — A2P 10DLC (TrustHub)
Carrier registration required to send SMS from US local numbers. Gate is at message time: US carriers + Twilio block or heavily filter traffic from unregistered numbers. Buying the number succeeds; messaging silently fails.
For a US org, the happy path is currently broken at the last step
A US business buys a local number in Loquent today, assigns a text agent, sends a campaign — and messages fail with error 30034 (unregistered 10DLC) or get carrier-filtered (30007). Since mid-2023 Twilio blocks unregistered US-bound long-code traffic outright. Everything upstream of this works; the send itself doesn't. This is the single highest-value gap.
The 10DLC chain has four pieces, all per-org, all absent from the codebase: a TrustHub Secondary Customer Profile (business identity), an A2P Brand (registered against the profile; sole-proprietor or standard), a Messaging Service (sender pool), and an A2P Campaign (use-case declaration; numbers attach to it via the messaging service).
The bundle wizard is 70% of the 10DLC wizard
Multi-step wizard, org-scoped Twilio resource rows, status-callback webhook, notifications on approve/reject, "required before X" purchase interception — all of that machinery was just built for bundles and maps one-to-one onto TrustHub profiles, brands, and campaigns. The marginal cost of 10DLC dropped dramatically with PR #1469.
03Limitations & gaps in what exists
Smaller issues in the shipped surface, worth fixing opportunistically or alongside roadmap items.
| Gap | Impact | Notes |
|---|---|---|
| No STOP confirmation / HELP reply | CTIA compliance + customer confusion | We detect STOP and record the opt-out, but send nothing back. Carriers expect a confirmation ("You've been unsubscribed…") and a HELP response. HELP isn't even detected today. |
| Generic failure surfacing | User can't self-diagnose | Delivery failures map to a bare Failed status. Twilio error codes (30034 unregistered, 30007 filtered, 21610 opted-out, 30003 unreachable) are dropped, so a compliance problem looks identical to a wrong number. |
| Numbers are immutable after purchase | Orgs accumulate paid numbers they can't shed | No release/delete UI or endpoint. Cost leaks silently every month. |
| Direct-from-number sending (no Messaging Service) | Blocks 10DLC; loses sticky sender / scaler | send_sms_util posts with a raw From number. 10DLC campaigns require numbers to live in a Messaging Service sender pool. |
| Bundle wizard: single end-user, no reuse | Repeat data entry for multi-country orgs | Each bundle re-collects identity. Twilio supports bundle copies across number types; we don't expose them. Fine for v1. |
| WhatsApp is an enum variant without onboarding | Channel exists in types only | MessageChannel::Whatsapp routes through the SMS path; there's no WABA sender registration, so no org can actually use it. |
| No per-number voicemail / business hours | Multi-location orgs share one greeting | Voicemail message, after-hours message, and timezone are org-level only (organization_profile). |
| No usage/cost visibility | Surprise Twilio spend | Number monthly fees, message segments, and carrier fees aren't shown anywhere in-app. |
04Roadmap, ordered by org value
Value scoring assumes the target user: a non-technical US small business that wants to buy a number and start texting customers the same day.
-
P1US A2P 10DLC registration · #1069 effort L–XL unblocks US SMS
TrustHub Secondary Customer Profile → A2P Brand → Messaging Service → A2P Campaign, as a guided wizard mirroring the bundle wizard. Status webhooks + notifications on brand/campaign approval. Numbers auto-attach to the org's Messaging Service. Without this, US local-number messaging fails; with it, Loquent's core promise works. Sole-proprietor brand type keeps the form small for tiny businesses.
-
P2Toll-free verification · #1517 effort M fast compliance path
Twilio's TollfreeVerification API: one form (business info, use case, sample messages, opt-in proof), one approval, then full-volume US + CA messaging — no brand, no campaign, no per-campaign fees. For many SMBs this is the path we should actively steer to at purchase time: "Want to text customers? A toll-free number is verified in days with one form." Shares the wizard/status/notification machinery with P1.
-
P3STOP/HELP compliance responder · #1518 effort S carrier requirement
Auto-reply to STOP with an unsubscribe confirmation, detect + answer HELP with org name and contact info, handle START/UNSTOP to re-opt-in. Configurable copy in settings with sensible defaults. We already detect the keywords — this closes the loop carriers require and is table stakes for P1/P2 approval reviews.
-
P4Delivery error surfacing · #1519 effort S–M
Persist the Twilio error code on failed messages, map the common ones to friendly explanations ("This contact's carrier filtered the message — your number may need registration", "This contact opted out"), and show them inline in the conversation plus a deliverability snapshot in Insights. Turns silent failures into actionable guidance, and tells users why they need P1/P2.
-
P5Number lifecycle: release · #1137 effort S
Release a number back to Twilio with a destructive-confirm dialog (warn about losing the number permanently and active agent bindings). Stops silent monthly cost leak; completes CRUD on the phone tab.
-
P6Number porting (port-in) · #1520 effort L
Bring the org's existing business number into Loquent via Twilio's Porting API: eligibility check, LOA + bill upload (document upload machinery exists from bundles), port-date tracking with webhooks. High emotional value — SMBs rarely want a new number; they want their number to become AI-powered. Long async flow (1–4 weeks), so notifications matter.
-
P7Per-number voicemail & business hours · #1521 effort M
Move voicemail greeting, after-hours message, and hours/timezone from org-level to optional per-number overrides. Required by multi-location orgs; pairs naturally with
humans_firstrouting which already references business hours. -
P8Caller-ID branding (CNAM + voice trust) · #1522 effort M
Outbound CNAM so calls show the business name instead of a raw number, plus SHAKEN/STIR attestation via the org's subaccount (Voice Integrity / Trusted Calling later). Directly improves answer rates for the AI agent's outbound calls — but only matters once orgs do meaningful outbound voice volume.
-
P9Usage & cost visibility · #1523 effort M
Surface per-org Twilio spend (Usage Records API): number rentals, message segments, voice minutes, carrier fees. Foundation for billing pass-through later; until then, a transparency feature.
-
P10WhatsApp sender onboarding · #1524 effort XL
WABA registration via Twilio's Senders API, template management, quality ratings. The channel enum and inbound plumbing exist; everything else doesn't. Big lift, separate initiative — listed for completeness.
Why this order
P1–P4 complete the "messaging actually works and is compliant" story — they're sequenced by how many orgs they block (all US orgs → toll-free subset → every texting org → every org that hits an error). P5–P9 are quality-of-ownership features. Per decision Q1, P2 leapfrogs P1 in delivery order: it's smaller and unblocks the same user need via a different number type.
05Wave 1 in depth — the compliance setup family
P1 + P2 + P3 as one coherent "Messaging setup" experience rather than three disconnected features.
Decision Q2 — assistant-driven, no manual config UI for now
The flows below were originally framed as settings wizards. Per the 2026-06-12 decision, the assistant owns the experience from day one: it collects and prefills registration data (org profile, intake data), renders the filled form as a rich in-thread component the user reviews and edits, and submits only on explicit confirmation. Settings keep read-only status surfaces (badges, rejection reasons) whose CTAs open the assistant. This rescopes #1069's M4 (#1076) from a settings wizard to assistant tools + in-thread form rendering.
The user-facing frame: a per-number "Messaging status"
Users shouldn't learn the words "10DLC" or "TrustHub". Each phone card gets a messaging-readiness state, exactly like the existing "Not active" voice-webhook badge:
New Twilio surface needed
| Resource | API | Stored where (new tables) |
|---|---|---|
| Secondary Customer Profile | TrustHub v1 CustomerProfiles + entity assignments | org_trusthub_profile (sid, status, failure_reason) |
| A2P Brand | Messaging v1 BrandRegistrations | org_a2p_brand (sid, type: sole-prop/standard, status, score) |
| Messaging Service | Messaging v1 Services + PhoneNumbers sender pool | messaging_service_sid on organization_twilio_settings |
| A2P Campaign | Messaging v1 UsAppToPerson (on the service) | org_a2p_campaign (sid, use case, status, reason) |
| Toll-free verification | TollfreeVerification API | org_tollfree_verification (sid per number, status, reason) |
Send-path change
Once a Messaging Service exists, send_sms_util sends with MessagingServiceSid + the specific From number (keeps per-number agent identity while inheriting campaign registration, sticky sender, and Advanced Opt-Out if we enable it). Numbers join the sender pool automatically at purchase / on campaign approval.
Sequencing constraint inside P1
Brand approval requires an approved customer profile; campaign approval requires an approved brand. Sole-prop brands also require OTP verification of a contact phone. The flow must tolerate multi-day waits at two points — the bundle machinery's webhook + notification pattern handles this, and with the assistant-driven model (Q2) the assistant must be able to resume the registration in a later conversation, with the phone-card badge as the re-entry point.
06Decisions resolved 2026-06-12
The four open questions, with the calls that were made. These are reflected in the filed issues (§7).
Ship order within Wave 1 — 10DLC first or toll-free first?
- Toll-free verification first (P2 → P3 → P1). Smaller build, days-not-weeks approval for users, and we can steer new orgs to toll-free numbers at purchase so messaging "just works" while the bigger 10DLC flow is built.
- 10DLC first (P1 → P2 → P3). Local numbers are what users instinctively pick (local presence, existing area code expectations), and area-code search is already a built feature we'd be soft-deprecating.
- Build both behind one "Set up messaging" entry and ship together. Cleanest UX, slowest time-to-value.
Who fills the compliance forms — the user, or the AI assistant?
- Standard wizard forms first; assistant pre-fill as a fast-follow (org profile + intake data already hold business name, address, website — most fields are derivable).
- Assistant-driven from day one ("I'll register your business for texting — confirm these details"). On-brand for Loquent.
BYO-Twilio orgs — do the registration flows apply?
- Yes, run the same registrations against their account (APIs are identical), but detect + adopt existing messaging services/brands instead of creating duplicates.
- No — show "manage compliance in your Twilio console" for BYO, like we already do for voice webhooks. Less work, but BYO orgs get a second-class product.
Who pays the registration fees?
- Absorb into subscription tiers (one-time brand fee + monthly campaign fee per org; sole-prop is cheap, standard brands aren't).
- Pass through at cost with a disclosure step.
- Decide per tier: absorb sole-prop/toll-free (low, bounded cost) in paid tiers; pass through standard-brand fees.
07Issues & execution
Everything is filed under spike #1525 in the Twilio Configuration milestone. The pre-existing 10DLC epic #1069 (with M1–M8: #1073–#1081) was adopted as P1 and moved into the milestone; #1137 (release) stays under the Monetization epic #1134 and is referenced, not re-parented.
| # | Issue | Priority | Wave |
|---|---|---|---|
| Spike | #1525 — buy a number and set up everything needed to message | P1 | — |
| P1 | #1069 — A2P 10DLC registration (epic, M1–M8: #1073–#1081) | P1 | 1 · Track A |
| P2 | #1517 — assistant-driven toll-free verification | P1 | 1 · Track B · first |
| P3 | #1518 — STOP/HELP/START auto-responder | P1 | 1 · Track C |
| P4 | #1519 — delivery error surfacing | P1 | 1 · Track D |
| P5 | #1137 — number release (under #1134, Monetization) | — | 2 |
| P6 | #1520 — port-in flow | P2 | 2 |
| P7 | #1521 — per-number voicemail & business hours | P2 | 2 |
| P8 | #1522 — CNAM + SHAKEN/STIR | P2 | 3 |
| P9 | #1523 — usage & cost visibility | P2 | 3 |
| P10 | #1524 — WhatsApp sender onboarding | P2 | 3 |
Parallelization
Almost everything parallelizes — the only hard internal sequence is inside #1069 (M1 → M8). Wave boundaries are value groupings, not dependency barriers; a Wave 2/3 item can start any time someone is free.