When you create a number group with sticky_sender: true, the platform picks From numbers out of that pool and holds one per recipient. A compat send names the group as MessagingServiceSid and carries no From.
sticky-sendernumbers
The claim
Three documented pieces. POST /api/relay/rest/number_groups creates the group. The vendored REST spec requires name and documents sticky_sender as a boolean, default false. Its description is “Whether the number group uses the same ‘From’ number for outbound requests to a number, or chooses a random one”. POST /api/relay/rest/number_groups/{id}/number_group_memberships adds a member by phone_number_id, its one required field. Then the compat create message takes the group in place of a number. https://signalwire.com/docs/compatibility-api/rest/messages/create-message describes MessagingServiceSid as “The ID of a number group to use when sending the message”. It adds “Either From or MessagingServiceSid must be provided”.
Why it holds
The SDK wraps the first two as client.number_groups.create and add_membership, and the send as client.compat.messages.create.
How it works
def create_pool(name, numbers):
ids = [number_id(e164) for e164 in numbers] # resolve first, so a bad number fails early
group = client.number_groups.create(name=name, sticky_sender=True)
for phone_number_id in ids:
client.number_groups.add_membership(group["id"], phone_number_id=phone_number_id)
return group["id"]
def send(group_id, to, body):
return client.compat.messages.create(To=to, Body=body, MessagingServiceSid=group_id)
What the platform receives:
POST /api/relay/rest/number_groups
{"name": "repair-updates", "sticky_sender": true}
POST /api/relay/rest/number_groups/<group_id>/number_group_memberships
{"phone_number_id": "<id of +1555XXXXXXX>"}
POST /api/laml/2010-04-01/Accounts/<project>/Messages
{"To": "+1555YYYYYYY", "Body": "Your bike is ready.", "MessagingServiceSid": "<group_id>"}
Membership takes a phone number’s resource id, not its digits, so number_id looks it up with the spec’s filter_number query and compares the number exactly. The send has no From. Which member number the recipient sees is the platform’s choice, and sticky_sender is what makes that choice hold for them.
Limitations
You prove the requests. The pin itself, one From per recipient, is the platform’s behaviour and shows only in a live messaging log.
The vendored compat spec describes MessagingServiceSid only as a UUID. The sentence naming it as a number group id is from the live reference page linked above, fetched on 2026-09-02.
What to change first
Pass sticky_sender=False in create_pool and run the verifier. The exact-body assertion fails. The spec’s default says why nothing else would: false is what you get when you do not ask, and then the platform “chooses a random one”.