Skip to main content

Sender resolution

Every outbound message needs a sender. You can name one on the request, point at a sender pool, or point at a messaging service — and you can sometimes supply more than one of these on the same POST /messages body. This page is the authoritative statement of which one wins, what the pool does with its members, and what happens when no source resolves.

1. sender_pool_id beats a bare from

Passing from on the request body names the sender directly — as long as no sender_pool_id travels with it. When you send both from and sender_pool_id on the same POST, the pool resolves after the explicit-from read and wins (Twilio Messaging Service parity: a pool/service reference is an explicit identity choice for the send). The message goes out from the pool’s pick and the from you passed is silently ignored.
This send uses a member of pool_abc123, not +14155559999. To pin the sender, pass from alone and drop the pool id. Three adjacent rules:
  • Empty from counts as omitted. A from: "" body behaves exactly like no from at all and falls through to the rest of the chain (this is what the dashboard’s “Use account default” option submits).
  • A validated from still needs to be deliverable. It must be a number your tenant owns, a valid short code, or a registered alphanumeric sender ID for the destination’s rule set; a from that fails that validation returns a 422 with a field-level detail naming from.
  • from_extension is a sibling of from, not of the pool. It resolves a UCaaS extension to its DID identity; it cannot be combined with a pool id on the same request — from_extension and sender_pool_id are mutually exclusive and sending both returns 422 VALIDATION_ERROR.

2. sender_pool_id resolution order per strategy

When you pass sender_pool_id (without an explicit from), the pool picks exactly one of its members at send time: Pool resolution happens before org defaults and platform fallbacks, so a pool id always wins over the fallback chain below. One override to know: if the parent messaging service has area_code_geomatch enabled, the pool resolves via geomatch even when its own stored strategy is sticky, round_robin, or random.

Common misconfigurations

  • Pool with no senders — an empty pool returns 422 SENDER_POOL_EMPTY at send time. Add at least one DID or sender ID to the pool before routing traffic through it.
  • Passing both from and sender_pool_id — the pool wins and the explicit from is silently ignored. If you expected your explicit from to beat the pool you also passed, the pool’s pick is used instead. Drop the pool when you want a fixed sender, or drop from when you want pool rotation.
  • Mixed sender types in one pool — a pool may hold E.164 long codes, short codes, and alphanumeric sender IDs together. Each pick is routed by the member’s type (numeric members go down the numeric path, alphanumeric members down the alphanumeric path), so a mixed pool does not mis-route — but check the destination’s rules before mixing: US and Canada corridors reject alphanumeric MT, so if an alphanumeric member can be picked for a +1 recipient, the carrier may drop the message. Keep per-corridor pools in regions where alphanumeric senders apply.

3. Messaging-service default sender fallback

A messaging service binds a default sending identity plus per-message settings (status_callback, validity_period). When you pass messaging_service_id, the service’s own sender source fills in only if you did not pass a sender yourself:
  • You passed from or from_extension → the service’s sender config is ignored for the from decision.
  • You passed sender_pool_id → your pool wins; the service’s pool is ignored.
  • You passed neither → the service’s pool becomes the sender source.
If the service carries a country_sender_pools map, the recipient-country’s entry (looked up from to) is preferred over the service’s default pool — again only when you did not set sender_pool_id on the request. Message-level fields always beat service-level config: the service is a fallback, not an override. For the full service schema, see the Messaging credentials & services reference.

4. Channel-specific rules

  • WhatsApp — Meta owns the sender: a business-initiated message must use an approved template, and only a message inside the 24-hour session window opened by the user’s last inbound may be free-form. There is no sender pool or arbitrary from on this channel; the sender is your WABA phone number, and the template/window rules are what gate the send.
  • SMS alphanumeric sender ID — in countries where local regulation or carrier practice requires a pre-registered alphanumeric sender ID (much of Europe, the Middle East, and Asia), keep that sender in a pool or pass it as from; in US/Canada corridors alphanumeric MT is not accepted, so a +1 destination falls back to a platform phone number while other destinations use the platform alphanumeric sender (see below). Keep the two pools separate per corridor.
  • Two-way SMS replies — when you reply on an existing conversation without naming a sender, Orbit pins the sender to the DID the contact originally texted, so the thread stays on one number. This only fires for SMS and only when no from, from_extension, or pool already named a sender.

5. The fallback chain, when nothing named a sender

With no from, no extension, no pool, no service, and no sticky inbound sender, the sender resolves in this order — the first source that produces a sender wins:
  1. Your organization’s default sender, if one is set for the channel.
  2. Your first active phone number.
  3. The platform default: a platform phone number for US/Canada (+1) destinations, or the Devotel alphanumeric sender ID for other destinations.
The US/Canada substitution exists because +1 carriers reject the shared alphanumeric sender, so the implicit default for a +1 recipient is always a phone number. If you confirm a shared alphanumeric sender for a US send and see a phone number on the delivered message, this is the reason — pass from or a pool to pin the identity instead.

Troubleshooting quick reference

See also