Design an SMS System
SMS remains a critical channel for time-sensitive communications: OTP codes for authentication, banking alerts, appointment reminders, and marketing campaigns. Platforms like Twilio, Nexmo/Vonage, AWS SNS, and enterprise SMS platforms handle billions of messages monthly. Designing an SMS system requires not just scalable software architecture but also deep integration with telecom protocols, multi-provider routing, strict compliance, and reliable delivery tracking. This article walks through a complete design for a robust, production-grade SMS service.
Step 1: Requirements Clarification
Functional Requirements
- Send SMS to one or more recipients programmatically.
- Support transactional and marketing messages with different priority classes.
- Queue messages for delivery to handle bursts without blocking the client.
- Track delivery status at each stage (queued, sent, delivered, failed).
- Retry failed messages according to configurable policies.
- Support templates for message content, including variable substitution.
- Support scheduling of messages for a future date/time.
- Support sender IDs / short codes / long codes to control sender identity.
- Handle inbound SMS (optional) for two-way communication like replies or STOP requests.
- Support opt-out / unsubscribe management (STOP, UNSTOP, HELP).
- Multi-region sending support with localized number pools.
Non-Functional Requirements
- High availability – the SMS sending API must be operational 99.95%+; messages must not be lost before delivery attempt.
- Reliable delivery – the system must retry transient failures and provide a delivery report.
- Low latency for OTP / alerts – end-to-end delivery (from API call to phone) should be under a few seconds.
- Scalability – must handle massive bursts (e.g., millions of OTPs during a product launch).
- Fault tolerance – component failures should not cause message loss.
- Message durability – accepted messages must be persisted before acknowledgment.
- Compliance and anti-spam controls – enforce regional regulations, handle opt-outs.
- Idempotency – duplicate API calls must not result in duplicate messages.
- Carrier routing resilience – fallback to alternate providers if a carrier rejects a message.
Step 2: Capacity Estimation
Assume a platform serving thousands of businesses:
- Total outbound messages per day: 500 million
- Average per second: ~5,800 messages/sec
- Peak burst (e.g., Black Friday OTP): 10x average → ~58,000 messages/sec
- Message size: typically 160 characters; storage record ~500 bytes
- Delivery report writes: approximately equal to outbound messages (one status update per message, sometimes multiple callbacks)
- Storage daily: 500M * 500 bytes ≈ 250 GB/day; with retention of 180 days, ~45 TB
- Inbound SMS (optional): much lower volume, perhaps 10% of outbound.
SMS systems are bursty and asynchronous. The design must accept spikes instantly and process them smoothly via queues, without backpressure propagating to clients.
Step 3: API Design
The system provides a REST API for message submission and webhooks for status callbacks.
Primary REST Endpoints
-
Send SMS
POST /api/v1/messages
Body:{ to, from (sender ID), body, template_id, params, scheduled_at, idempotency_key, priority }
Returns:{ message_id, status: "queued" } -
Get message status
GET /api/v1/messages/{message_id}
Returns:{ message_id, status, delivered_at, error_reason, segments } -
Cancel scheduled message (if not yet sent)
DELETE /api/v1/messages/{message_id} -
Get message history (paginated, filtered by date, status)
GET /api/v1/messages?from={date}&to={date}&status={status} -
Register sender ID (admin)
POST /api/v1/senders
Body:{ sender_id, type (shortcode/longcode/alpha), country } -
Opt-out management
POST /api/v1/optouts(to add or remove based on inbound STOP) -
Inbound message webhook (configured by client)
POST {client_webhook_url}with inbound message payload.
Idempotency is achieved by accepting an idempotency_key; duplicate keys within a 24-hour window return the existing message ID and status.
Step 4: High-Level Architecture
The system decouples request acceptance from delivery, using a distributed message queue.
- API Gateway & Auth: authenticate clients, enforce rate limits.
- SMS Service: validates request, renders template, persists message with
queuedstatus, publishes to queue. - Template Service: merges templates with parameters; supports multi-language.
- Message Queue: durable, partitioned event stream (e.g., Kafka) that buffers messages and allows independent scaling of submission and delivery.
- Routing Engine: determines which telecom provider or direct carrier connection to use based on recipient number, cost, reliability, and client preference.
- Delivery Workers: stateless pools of workers per provider or route; they transmit messages over provider APIs (HTTP, SMPP) and handle provider-specific logic.
- Telecom Gateway / Aggregator: third-party service that forwards messages to mobile carriers.
- Carrier Network: final mobile network that delivers SMS to the handset.
- Delivery Report Processor: consumes asynchronous delivery receipts (DLRs) from providers via webhooks or polling, updates message status, and triggers client callbacks.
- Callback/Webhook Service: notifies clients of final delivery status.
- Message Store: durable, sharded database for message and status records.
- Analytics: aggregates delivery rates, latency, and provider performance.
Step 5: SMS Delivery Flow
The delivery path is entirely asynchronous after acceptance.
- Client submits message; service persists it and publishes to queue.
- Router selects the optimal provider based on destination and rules.
- Worker dispatches to the gateway; status updates to
sent. - Asynchronously, the gateway sends a delivery receipt (DLR) indicating success or failure.
- DLR Processor updates the message store and optionally calls a client webhook.
If the initial gateway submission fails due to a temporary error, the message is re-queued with a delay for retry. If it fails permanently (e.g., invalid number), the status is marked failed immediately.
Step 6: Telecom and Gateway Integration
SMS delivery involves multiple layers:
- SMS Gateway / Aggregator: These platforms (e.g., Twilio, Vonage, Sinch, Infobip) abstract carrier connections. They offer HTTP APIs and sometimes SMPP.
- Carriers: The actual mobile network operators (e.g., AT&T, Vodafone) that deliver to handsets. Direct carrier connections are possible for very high volumes but require individual agreements.
- Protocols:
- HTTP-based APIs: REST/JSON, simple to integrate, used by most applications.
- SMPP (Short Message Peer-to-Peer): A low-level, persistent TCP protocol used by high-volume gateways. Provides more control over throughput and delivery receipts.
- Delivery Receipts (DLR): Reports sent by the carrier back to the gateway (and then to the platform) indicating final status: delivered, expired, rejected, etc.
- Two-way messaging: Inbound SMS are handled via webhooks set by the platform when a message arrives at a specific number (shortcode/longcode).
The platform implements a provider abstraction layer so that adding a new gateway only requires a new worker adapter.
Step 7: Sender Identity and Number Management
The sender ID (what the recipient sees) dramatically affects deliverability and trust.
- Short codes (e.g., 12345): Dedicated 5-6 digit numbers, ideal for high-throughput OTP and marketing in a single country. Require carrier approval.
- Long codes (standard 10-digit numbers): Can be used for person-to-person messaging, support voice, but have lower throughput limits.
- Toll-free numbers: In North America, used for business messaging with enhanced trust.
- Alphanumeric sender IDs (e.g., "MyCompany"): Supported in many countries outside North America. Often require business verification.
- Regional restrictions: Some countries mandate pre-registration of sender IDs and message templates (e.g., India's DLT). The platform must manage compliance per country.
- Brand registration: For 10DLC (10-digit long code) in the US, brands must register with carriers to avoid filtering.
The system allows clients to manage sender IDs, which are verified and associated with specific countries and message types.
Step 8: Message Routing
Routing messages intelligently is critical for cost control, performance, and reliability.
- Route selection by region: Choose a provider or direct connection that has strong termination agreements in the recipient's country.
- Provider fallback: If the primary provider returns an error or times out, the router can try an alternate route automatically.
- Cost-based routing: Marketing messages can be sent over cheaper routes, while OTP uses the most reliable (and possibly more expensive) path.
- Reliability-based routing: Dynamically adjust routing weights based on live delivery success rates from each provider.
- Geography-aware routing: For global platforms, route messages through regional gateways to reduce latency.
- Load balancing across gateways: Distribute traffic among multiple connections or providers to avoid throttling.
The Routing Engine uses configurable rules (JSON policies) and real-time metrics to make sub-second decisions.
Step 9: Delivery Reports and Status Tracking
SMS delivery is inherently asynchronous. The state machine for a message includes:
accepted– Message persisted, not yet dispatched.queued– In the message queue.sent– Submitted to the gateway.delivered– Gateway confirms handset received the message.failed– Permanent delivery failure (invalid number, carrier block).undelivered– Message expired after retries.expired– Scheduled message not sent before expiry.
Delivery reports (DLRs) are received from gateways via webhooks or polling. The DLR Processor matches the report to the original message using provider_message_id, updates the status, and triggers client callbacks. In case of missing DLRs, reconciliation jobs can query the gateway for pending statuses.
Step 10: Retry and Failure Handling
Transient failures are common: gateway timeouts, carrier congestion, temporary network issues.
- Retry policies: Configurable per client or message type. Example: up to 3 retries for OTP, with 1-minute gaps; 1 retry for marketing after 5 minutes.
- Exponential backoff: Workers re-queue the message with an increasing delay (e.g., 60s, 300s, 900s) after a transient failure.
- Provider failover: If the original route fails permanently, the message can be re-routed to a backup provider.
- Dead letter queue: After all retries are exhausted, the message goes to a dead letter topic for manual inspection and possible replay.
- Temporary vs permanent failures: Invalid destination number (
+000000) is permanent and markedfailedimmediately. CarrierREJECTwith a specific error code may also be permanent.UNDELIVERABLEafter multiple retries is markedfailed.
Idempotent resend logic: if the client retries the same idempotency_key, the system returns the current status without sending a duplicate.
Step 11: User Preferences and Opt-Out
SMS is a regulated channel. Handling opt-outs is mandatory.
- STOP / UNSUBSCRIBE: Recipients can reply "STOP" to an SMS to opt out. The platform receives this inbound message via webhook, marks that recipient number as opted out for that sender, and stops future messages.
- Opt-in requirements: Many countries require explicit consent before sending marketing messages. The platform can store opt-in status per number.
- Preference management: A customer portal allows end-users to select which types of messages they receive (transactional vs promotional).
- Quiet hours: The platform can be configured to suppress messages during specific hours per region.
- Regional compliance: Some countries mandate adding "Msg&Data rates may apply" disclaimers. The platform can enforce template approval.
- Abuse prevention: Rate limiting per sender, per destination, and per IP prevents spam.
Step 12: Storage Design
The message store must handle high write throughput and efficient lookups by message ID and client account.
Schema (conceptual):
message_id(UUID)account_idto(hashed or encrypted)from(sender ID)body(plain text)statusprovider_message_iderror_codescheduled_atcreated_at,updated_at
Partitioning: Shard by account_id to group a client's messages together, or by message_id hash for uniform distribution. Time-based sub-partitioning for archiving.
SQL vs NoSQL:
- NoSQL (DynamoDB, Cassandra) offers high write scalability, natural sharding, and flexible schema. Good for primary message store.
- SQL (PostgreSQL) is better for client account metadata, sender IDs, and billing. The system uses both—NoSQL for hot message data, SQL for configuration.
For delivery status queries by clients, a read-optimized view can be maintained via an index or search engine.
Step 13: Scalability Strategies
- Horizontal scaling: All services (API, workers, router) are stateless and horizontally scalable.
- Queue-based processing: Kafka partitions messages by recipient or account, enabling parallel consumption.
- Provider sharding: Assign workers to specific providers to isolate load and failures.
- Multi-region deployment: Deploy API endpoints and workers in multiple regions (North America, Europe, Asia) to reduce latency and adhere to data residency.
- Rate limiting: Clients are limited per second and per day. Providers are rate-limited by worker-side token buckets to avoid rejections.
- Backpressure: If queue depth exceeds a safe threshold, the API Gateway can throttle new requests with HTTP 429.
- Dedicated tenant isolation: Very large enterprise customers can be given dedicated queues and worker pools to prevent "noisy neighbor" issues.
Step 14: Reliability and Observability
- Monitoring delivery success rate per client, per provider, per country.
- Alerting on provider degradation (e.g., delivery rate drops below 90%). Automatic route weight adjustments.
- Dead letter monitoring – alerts if more than N messages accumulate in the DLQ.
- Latency metrics – track time from API submission to final delivery report, segmented by stage.
- Provider health dashboards – real-time view of error rates, latency, throughput per gateway.
- Tracing and correlation IDs – every message is tagged with a trace ID that flows through all services, helping debug delivery issues.
- SLOs: e.g., "99.9% of accepted OTP messages delivered within 5 seconds."
Step 15: Security and Compliance
- Authentication: API keys, OAuth 2.0 for client access. mTLS for internal services.
- Authorization: Clients can only view their own messages. Admin controls for provider configuration.
- Encryption in transit: All APIs and worker-to-provider connections use TLS 1.2+.
- Encryption at rest: Message bodies and recipient numbers are encrypted (AES-256). PII protection: recipient numbers can be tokenized for internal processing.
- Anti-spam controls: Real-time rule engine blocks known spam patterns, suspiciously high volumes.
- Compliance considerations: SOC 2, ISO 27001, GDPR (data processing agreements, right to erasure). Regional templates approval (India DLT). Carrier requirements.
- Audit logging: All administrative changes, message sends, and access are logged to an immutable store.
Real-World Example: OTP Delivery for a Banking Application
Consider a mobile banking app that sends one-time passwords for login. The requirement is extremely low latency and high reliability.
- The banking app sends the OTP with high priority.
- The SMS service immediately queues it with priority flags; the message broker ensures it's picked up first.
- The routing engine selects the fastest provider for the recipient's country.
- If the primary gateway does not confirm acceptance within a tight window (e.g., 2 seconds) or returns a transient error, the router instantly falls back to a secondary provider, ensuring the OTP reaches the user.
- Delivery status is updated and the app can poll or receive a webhook.
The system also ensures that after the OTP expires (e.g., 5 minutes), any delayed delivery attempts are suppressed.
Trade-offs
- Latency vs reliability: Adding retries and fallbacks increases latency but improves delivery probability. OTP messages favor speed and use aggressive timeouts and instant fallback; marketing messages can retry over minutes.
- Cost vs delivery success rate: Premium routes (direct carrier connections) are more expensive but deliver faster. Cheap aggregator routes may have lower success. A cost-aware router can allocate higher-cost routes for transactional and cheaper for bulk.
- Simplicity vs multi-provider routing: Using a single SMS gateway simplifies architecture but creates a single point of failure. Multi-provider routing increases complexity but dramatically improves resilience.
- Synchronous vs asynchronous submission: Synchronous submission to the gateway in the API path would be faster for the first hop but couples the API to gateway availability and makes client requests slow. Decoupling via queue is the standard.
- Global coverage vs operational complexity: Supporting hundreds of countries requires constant provider management, local regulations, and compliance. This is often the hardest part of building an SMS platform.
Common Mistakes
- Sending SMS synchronously in the request path – blocks API threads and fails if gateway is slow; always decouple with a queue.
- No retry / fallback logic – a single provider outage results in message loss.
- Ignoring delivery reports – assuming a message is delivered because the gateway accepted it is false; carriers can silently drop messages.
- No idempotency – network retries from clients cause duplicates, annoying users and wasting credits.
- No opt-out handling – failing to respect STOP requests leads to carrier blocks and legal penalties.
- Storing all status updates in one hot partition – using
message_idas shard key may be fine, but per-account status queries need secondary indexes or a separate query model. - Tight coupling to a single telecom provider – impossible to optimize cost or survive outages.
Interview Perspective
In a system design interview, expect questions that probe your understanding of asynchronous delivery and telecom integration:
- How do you design an SMS system?
- How do you ensure reliable delivery?
- How do you handle provider failures?
- How do you support OTP traffic spikes?
- How do you track delivery status?
- How do you route messages across providers?
- How do you handle opt-out and compliance?
Demonstrate that you can separate the client API from the actual delivery, design a robust retry/failover system, and respect telecom constraints like sender IDs and regulations.
Summary
A production SMS system is a complex integration of asynchronous processing, telecom gateways, multi-provider routing, and strict compliance. It must accept messages instantly, buffer them through durable queues, route intelligently based on cost and reliability, and faithfully track delivery status through a web of carrier networks. By designing with horizontal scalability, provider abstraction, retry logic, and strong observability, you can build an SMS platform that delivers time-critical communications to billions of devices worldwide.