Twilio Messaging Service: Scalable SMS with Pools and Failover
In this tutorial, you will learn about Twilio Messaging Service: Scalable SMS with Pools and Failover. We cover key concepts, practical examples, and best practices to help you master this topic.
Twilio Messaging Service is a container that manages multiple phone numbers with intelligent sender selection, geo-routing, automatic failover, A2P 10DLC Compliance, and sticky sender assignments for high-volume SMS.
What You'll Learn
How to create and configure a Messaging Service, add numbers to sender pools, configure sender selection strategies (sticky, random, geo-routing), enable A2P 10DLC compliance, handle failover, and monitor message throughput.
Why It Matters
Sending from a single number limits throughput to 1 message per second and creates a single point of failure. Messaging Service distributes load across number pools, improves deliverability, and ensures regulatory compliance. DodaTech uses Messaging Service for all customer notifications.
Real-World Use
DodaTech sends 10,000 promotional messages. The Messaging Service auto-selects the best sender based on recipient geography and carrier, distributes across 5 numbers to maintain throughput, and automatically fails over if one number is rate-limited.
flowchart LR
A["Send Message\nvia Service"] --> B["Sender\nSelection"]
B --> C{"Strategy"}
C -->|Sticky| D["Same Sender\nper Recipient"]
C -->|Random| E["Random Sender\nfrom Pool"]
C -->|Geo| F["Local Sender\nby Country"]
D --> G["Send Message"]
E --> G
F --> G
G --> H{"Success?"}
H -->|Yes| I["Message\nDelivered"]
H -->|No| J["Failover to\nNext Sender"]
J --> G
style A fill:#f22f46,color:#fff
style G fill:#dbeafe,stroke:#2563eb
style I fill:#bbf7d0,stroke:#16a34a
Creating a Messaging Service
import os
from twilio.rest import Client
client = Client(
os.environ["TWILIO_ACCOUNT_SID"],
os.environ["TWILIO_AUTH_TOKEN"]
)
# Create a messaging service
def create_messaging_service(name, sticky_sender=True):
service = client.messaging.services.create(
friendly_name=name,
status_callback="https://example.com/sms/status",
sticky_sender=sticky_sender,
area_code_geomatch=True, # Auto-match area codes
fallback_to_long_code=True,
number_pool_enabled=True
)
print(f"Service SID: {service.sid}")
print(f"Name: {service.friendly_name}")
print(f"Sticky Sender: {service.sticky_sender}")
return service
service = create_messaging_service("DodaTech Notifications")
# Expected output:
# Service SID: MGxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# Name: DodaTech Notifications
# Sticky Sender: True
Adding Senders to the Pool
# Add existing phone numbers to the service
def add_sender_to_pool(service_sid, number_sid):
sender = client.messaging.services(service_sid).phone_numbers.create(
phone_number_sid=number_sid
)
print(f"Added sender: {sender.sid}")
print(f"Number SID: {sender.phone_number_sid}")
return sender
# Add multiple numbers
number_sids = [
"PN1111111111111111111111111111111111",
"PN2222222222222222222222222222222222",
"PN3333333333333333333333333333333333"
]
for nsid in number_sids:
add_sender_to_pool(service.sid, nsid)
# Expected per addition:
# Added sender: PSxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# Number SID: PNxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# List senders in pool
def list_pool_senders(service_sid):
senders = client.messaging.services(service_sid).phone_numbers.list()
print(f"Pool size: {len(senders)}")
for s in senders:
print(f" {s.phone_number} (SID: {s.sid[:30]}...)")
return senders
# list_pool_senders(service.sid)
# Expected output:
# Pool size: 3
# +14155551234 (SID: PSxxxxxxxxxxxxxxxxxxxx...)
# +14155555678 (SID: PSxxxxxxxxxxxxxxxxxxxx...)
# +14155559012 (SID: PSxxxxxxxxxxxxxxxxxxxx...)
Sending via the Messaging Service
def send_via_messaging_service(service_sid, to_number, body):
message = client.messages.create(
messaging_service_sid=service_sid,
to=to_number,
body=body
)
print(f"Message SID: {message.sid}")
print(f"From: {message.from_}")
print(f"Status: {message.status}")
print(f"Service: {message.messaging_service_sid}")
return message
message = send_via_messaging_service(
service.sid,
"+14155551234",
"Your DodaTech order is confirmed!"
)
# Expected output:
# Message SID: SMxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# From: +14155559876
# Status: queued
# Service: MGxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
A2P 10DLC Compliance
# A2P 10DLC registration (US requirement)
# 1. Register your brand
def register_brand(brand_name, website, vertical="software"):
brand = client.messaging.brand_registrations.create(
brand_name=brand_name,
website=website,
vertical=vertical,
# Additional fields as required
)
print(f"Brand SID: {brand.sid}")
print(f"Status: {brand.status}")
print(f"Verification: {brand.verification_status}")
return brand
# 2. Register campaign for a use case
def register_campaign(brand_sid, use_case="2FA", description="Two-factor authentication codes"):
campaign = client.messaging.us_app_to_person.create(
brand_registration_sid=brand_sid,
description=description,
message_flow=use_case,
# Additional fields
)
print(f"Campaign SID: {campaign.sid}")
print(f"Status: {campaign.status}")
return campaign
# Expected output:
# Brand SID: BRxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# Status: PENDING
# Verification: SELF_DECLARED
Status Callback Handling
# The status_callback URL receives POST requests for each message status change
@app.route("/sms/status", methods=["POST"])
def message_status():
message_sid = request.form["MessageSid"]
message_status = request.form["MessageStatus"]
error_code = request.form.get("ErrorCode", "0")
from_number = request.form["From"]
to_number = request.form["To"]
print(f"Status update: {message_sid}")
print(f" Status: {message_status}")
print(f" Error: {error_code}")
print(f" From: {from_number} -> To: {to_number}")
if message_status == "delivered":
print("Message delivered successfully")
elif message_status in ["undelivered", "failed"]:
print(f"Delivery failed, error code: {error_code}")
elif message_status == "sent":
print("Message sent to carrier")
return "", 200
Common Mistakes
1. Using a Single Number Instead of a Service
Single numbers are limited to ~1 msg/sec throughput and have no failover. Always use Messaging Service for production, even for low volume, for future scalability.
2. Not Configuring Fallback to Long Code
If your toll-free number is rate-limited, messages fail. Enable fallback_to_long_code: true so the service auto-fails over to a local number when the primary sender is blocked.
3. Skipping A2P 10DLC Registration
Without registration, messages from US numbers may be blocked or filtered by carriers. Register your brand and campaign before sending high-volume traffic to US numbers.
4. Setting Incorrect Sticky Sender Behavior
Sticky sender ensures the same recipient always sees the same sender number, which is important for reply continuity. Disable it only if you don't need reply routing.
5. Not Monitoring Pool Health
If a number in the pool gets carrier-blocked, it stays in the pool and causes failures. Regularly check delivery rates per sender and auto-remove numbers with high failure rates.
Practice Questions
- What is the difference between sending from a single number vs a Messaging Service?
- How does sticky sender work and why is it important?
- What is A2P 10DLC and why is it required?
- How does geo-routing improve deliverability?
Answers:
- A single number has ~1 msg/sec throughput with no failover. A Messaging Service pools multiple numbers, provides intelligent routing, failover, geo-matching, and higher throughput.
- Sticky sender ensures the same recipient always receives messages from the same number. This allows them to reply to that number and maintains conversation context across messages.
- A2P 10DLC is a US carrier requirement for application-to-person messaging. Brands register their use case and receive a campaign ID. Unregistered traffic may be blocked or filtered.
- Geo-routing selects a sender number local to the recipient's country or area code. Local numbers have higher deliverability, lower latency, and appear as domestic rather than international messages.
Challenge: Build a high-volume SMS system: create a Messaging Service with 5 numbers in the pool, configure sticky sender with geo-routing, register for A2P 10DLC compliance, implement status callbacks with delivery rate monitoring, set up failover testing (remove one number from pool and verify auto-failover), and generate a throughput report.
FAQ
Mini Project
Build a scalable messaging system: create a Messaging Service with 3 numbers, configure geo-routing and sticky sender, register for A2P 10DLC, implement status callback logging, test failover by temporarily removing a number, send 100 test messages and measure throughput, and generate a delivery rate report.
What's Next
Conversation API — build multi-channel conversations with Twilio Conversations.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro