Messaging services console
A messaging service is the unifying address for SMS traffic: one id (msvc_…) in place of a bare sender ID, bundling the sender pool, the opt-out list, the inbound webhook, and the throughput cap. Pass that id as messaging_service_id on a send and every message inherits the service’s defaults — you stop repeating from numbers, callback URLs, and STOP/HELP copy per request, and the send pipeline resolves them identically on every call. A bare sender ID (from) controls only which number the message goes out from; the service id controls that plus everything around it.
Every field a messaging service carries — the sender pool, the per-country pool map, the opt-out list, the inbound webhook, the behaviour toggles, and the send-rate cap — is editable in the dashboard. This guide first decides whether you need a service at all, then walks the console workflow step by step, then shows a send wired through the service (section 6) and scripts the same lifecycle over the API (section 8); Messaging services — the entity model defines what each field means, and the SMS sending services guide covers the API path end to end.
Create or stay per-channel?
Keep your existing per-channel senders — an alphanumeric sender ID, a long code, or a sender pool passed straight on send requests — when one kind of traffic answers all of these with “no”:- Only one sender serves the channel — there is nothing to rotate.
- The platform-default STOP/HELP wording is acceptable; compliance copy is not brand-specific.
- Replies either can’t arrive (one-way alphanumeric) or already have a destination. A numeric long code with replies landing in the dashboard inbox qualifies as “has a destination” — don’t create a service just to wire a webhook you already have.
- No per-profile throughput ceiling is needed beyond the tenant and carrier limits.
from until the first request arrives carrying messaging_service_id. The full construct decision (service vs pool vs inline sender) is scored per traffic pattern in Choosing a sender construct.
1. Route
Open Messages → SMS → Services (/messages/sms/services). The list shows every service on the account with its label and id. New service creates, the row menu edits or deletes, and clicking a service’s label opens its console page with four tabs: Config, Phone numbers, Inbound webhook, and Throughput.
2. Create a service and assign a sender pool
Click New service. The dialog asks for a label (required) and an optional external id — the vendor id you map during a migration, for example a TwilioMGxxx id. As you type an external id, the console looks it up and, if it already belongs to a service on your account, blocks the create and links you to the existing service instead of getting a 409 from the API.
Create lands you on the service’s Config tab. Assign the sender pool from the Sender pool picklist — this is the pool that supplies the from number when a send names the service but no sender of its own. Pick None to leave the service without a pool, which is valid: sends then fall through to the sticky conversation sender or the org default. Pool rotation behaviour (sticky, round-robin, random, geomatch) is set on the pool itself — see Sender pools.
The same tab sets the Opt-out list picklist (custom STOP/HELP copy and keyword aliases; the platform defaults always merge in) and two numeric ceilings:
- Validity period — 0–14400 seconds (up to 4 hours). Leave empty to use the carrier default.
- Send-rate cap — 1–1000 messages per second. Leave empty for no per-service cap. The Throughput tab monitors this cap live (section 5).
country_sender_pools on PATCH /api/v1/messaging/services/:id). It does not appear in the console; the model page covers the map in full.
3. What the console toggles do
The Send behaviour card on the Config tab carries four switches. Each applies to every message sent through this service — these are not fill-in-on-blank defaults like the pool and callback fields.
Save Save changes at the bottom of the form — nothing persists until you do. The detail page re-reads the service after each save, so a colleague editing the same service in another tab sees your updated values. For deeper per-flag rationale, see the entity model on Messaging services and the sender-precedence chain on Sender resolution.
4. Wire inbound replies and attach numbers
The Inbound webhook tab decides where replies to this service’s numbers are delivered:- Set the Inbound URL (must be
https://— the field rejectshttp://inline rather than on a 422) and pick the HTTP method —POSTrecommended,GETonly for legacy listeners that cannot read a body. - Optionally set a Fallback URL, called once when the primary returns non-2xx after retries, and a Status callback URL for delivery and failure updates on outbound messages.
- The second card links to Inbound webhook settings (
/developer/webhooks/inbound) where the account-level signing key lives — service-level payloads sign with that key.
422 NOT_SMS_CAPABLE — the right pane pre-filters, and the API re-checks.
5. Read the live throughput tab
The Throughput tab shows the already-enforced send-rate engine for this service:- Send rate — accepted messages per second right now, against the configured cap.
- Utilization — the percentage of the cap you’re using; turns amber past 70% and red past 90%.
- Queue depth — sends held back this second because the cap was reached.
- Send rate vs cap chart — the last ~60 one-second samples with a dashed cap line.
- Throttle events — counted per window, with the last throttle timestamp; an Active badge means senders are hitting the cap right now.
6. Send through the service
A send names the service withmessaging_service_id; every send request — the unified POST /api/v1/messages and the channel-specific endpoints such as POST /api/v1/messages/sms — accepts that id in place of a bare from. The send pipeline resolves the service once and stamps the resolved sender onto the message, so editing the service later rewrites nothing already sent.
An example against the same service the console walkthrough built above (the dashboard’s SMS composer shows the same wiring as a service picklist on the send form):
202
- Request fields beat service defaults. Pass an explicit
fromand that specific send uses it; every other send through the same id keeps the pool sender. An explicit per-send webhook URL overrides the service’sstatus_callbackthe same way — match for Twilio’s MessagingServiceSid contract. - Resolution happens at send time. The sender pool, opt-out list, callbacks, and validity period are read once as the send executes; a config saved a second earlier applies to the next send.
7. Test a service by reference id
If a send integration still references a vendor id (a TwilioMGxxx is the common case), resolve it before routing traffic: paste the id into the create dialog’s External id field. The debounced lookup returns the existing service, and the dialog links you into its console page — use the same check when a migration import claims an id was already ingested. The equivalent API call is GET /api/v1/messaging/services/lookup?external_id=…, and the model page covers the import upsert that external_id keys on.
8. Script the service lifecycle from the API
Everything the console tabs do maps ontoPOST/PATCH /api/v1/messaging/services — the console is a view over the API, so any field you can click you can also script. The create request below carries everything the PATCH can change at once, but each following sample uses PATCH so the field-by-field shape shows individually.
Create the service, then PATCH its config
A create with the full config plus a follow-up PATCH — eachmeta block carries request_id and timestamp, matching the standard response envelope:
null. To clear the behaviour toggles, PATCH them to false.
Set the per-country sender-pool map
The one field the console cannot reach —country_sender_pools routes recipients in a given country through that country’s pool instead of the default sender_pool_id. Keys are ISO-3166-1 alpha-2 uppercase; values are pool ids. Replace the whole map per PATCH — there is no per-country merge, and an entry that is missing on the PATCH is removed:
"country_sender_pools": {} to remove every per-country override and fall back to the default pool for all recipients. A recipient in a country with no entry uses sender_pool_id; a country listed there whose pool id no longer exists falls back the same way.
Attach a number and read live throughput
The console’s Phone numbers and Throughput tabs call these two endpoints:utilization_pct is null when the service carries no cap; queue_depth is the over-cap sends held for retry this second, not a failure count. GET /messaging/services/throughput-stats (no :id) returns the same shape aggregated across every service on the account.
The SMS-capability gate — 422 NOT_SMS_CAPABLE
Attach refuses a DID whose carrier-reported capabilities contain nosms entry (voice, fax, or MMS only):
422 Error
9. Error states
The console surfaces its own failure modes rather than leaving blank panes:Where the pieces fit
Three decisions sit between a service id and a delivered message:
The last row only exists if you attach your own upstream: the BYO SMPP carrier lifecycle covers attach, health, and how carrier health feeds the routing policy. Outbound SMS never leaves that boundary — the wholesale trunk plus your own connected carriers. A tenant with no attached carrier can treat the third row as decided.
See also
- Messaging services — the entity model — what each field means, when its defaults fill in, and the per-country pool map
- SMS sending services — the API path for the same lifecycle
- Sender pools — the rotation strategies the service’s pool can use
- Custom Opt-Out Keyword Lists — the STOP/HELP copy a service points at
- Sender resolution — the full precedence chain the service plugs into
- BYO SMPP carrier lifecycle — attach your own upstream carrier and feed it into routing
- Least-cost routing (LCR) policy — rank the wholesale trunk against your carriers per destination
- Choosing a sender construct — service vs pool vs inline sender, scored per traffic pattern