SMS Send Flow
Overview
The SMS send flow describes how MALIPOPAY delivers transactional and marketing SMS to Tanzanian subscribers. Messages are routed through one of several provider adapters (Airtel, Africa's Talking, SMPP direct) based on the recipient's network and the merchant's configuration. All SMS are queued via BullMQ and delivered asynchronously with automatic failover.
Sequence diagram
sequenceDiagram
autonumber
participant M as Merchant App
participant API as SMS Service
participant Q as BullMQ Queue
participant W as SMS Worker
participant GW as SMS Gateway<br/>(Airtel / SMPP / AT)
participant C as Recipient
participant DLR as Delivery Report
M->>API: POST /sms/<br/>apiToken, sender, phone, message
API->>API: validate apiToken,<br/>balance, sender ID whitelist
API-->>M: 201 Accepted<br/>{ messageId, status: QUEUED }
API->>Q: enqueue with priority
Q->>W: job picked up
W->>GW: dispatch via operator
GW->>C: SMS delivered
alt Delivered
GW->>DLR: DLR success callback
DLR->>API: POST /sms/dlr { messageId, status: DELIVERED }
API->>M: webhook { messageId, status: DELIVERED }
else Failed (no credit, invalid number, expired)
GW->>DLR: DLR failure callback
DLR->>API: POST /sms/dlr { messageId, status: FAILED, reason }
API->>API: requeue (up to 3 attempts)
API->>M: webhook { messageId, status: FAILED, reason }
end
Key endpoints
| Step | Endpoint | Notes |
|---|---|---|
| 1 | POST /sms/ | Send a single SMS. Returns immediately with QUEUED. |
| 1b | POST /sms/bulk | Send up to 10,000 recipients in one call. Same semantics, async delivery. |
| 1c | POST /sms/flash | Send a class-0 (flash) SMS. Appears on screen without being saved. Ideal for OTPs. |
| 1d | POST /sms/schedule | Schedule SMS for future delivery. Takes sendAt ISO timestamp. |
POST /sms/sms-stats | Pull delivery stats for a date range and sender ID. | |
GET /sms/{id} | Fetch status of a specific message by messageId. |
Channels and priority
Messages are prioritized in the queue by type:
| Type | Priority | Typical latency |
|---|---|---|
otp | 1 (highest) | < 3 s |
transactional | 2 | < 10 s |
notification | 3 | < 30 s |
marketing / bulk | 4 (lowest) | throttled per provider |
OTPs bypass the marketing throttle to guarantee fast delivery. Bulk campaigns are throttled to 100 SMS/sec to respect operator rate limits.
Provider failover
If the primary provider fails (HTTP 5xx or no response in 15s), the SMS worker automatically retries via the secondary provider. The failover order is configurable per sender ID. Typical chain:
Airtel direct → Africa's Talking → SMPP fallback
After 3 failover attempts across providers, the message is marked FAILED and the merchant is charged zero.
Sender IDs
Every SMS must use a pre-approved sender ID. To register a new sender ID:
POST /company/sender-request
{ "senderId": "ACMECORP", "category": "TRANSACTIONAL" }
This enters the TCRA approval queue. Approval typically takes 1-3 business days. While pending, SMS attempts with that sender ID return HTTP 403 with code: 1101 (sender not approved).
Pricing
Per-SMS pricing is volume-tiered. Your current rate is visible in the dashboard and via:
GET /billing/rate
See the SMS pricing guide for the full tier table.