SMS Bulk Messaging Integration in Mobile Application
SMS broadcast in mobile app — server task. Client only initiates: "send notification to users X, Y, Z". Everything else — HTTP request to backend or direct SMS gateway. So mobile part small, but server part needs careful design.
Gateway Selection
Most common options:
| Gateway | Pros | Cons |
|---|---|---|
| Twilio | REST API, webhooks, global reach | More expensive for CIS |
| SMSC.ru / SMS-gateway.ru | Cheap for Russia/CIS | No webhook notifications on some plans |
| Infobip | Multichannel (SMS + Viber + WhatsApp) | Complex onboarding |
| Vonage (Nexmo) | Mobile SDK, number verification | Limited CIS coverage |
For typical B2C app with CIS audience — SMSC or local gateway. International — Twilio or Infobip.
Server: What to Implement
SMS send via Twilio — one HTTP request:
POST https://api.twilio.com/2010-04-01/Accounts/{AccountSid}/Messages
Authorization: Basic {base64(AccountSid:AuthToken)}
Content-Type: application/x-www-form-urlencoded
To=%2B380991234567&From=%2B14155551234&Body=Your+order+ready
But bulk broadcast — queue, not sync calls in loop. Simultaneous send to 10,000 via sync HTTP kills backend and budget (gateway rate limiting). Right scheme: task queue (RabbitMQ, Redis + BullMQ, SQS), workers with throttling per gateway limit.
Twilio rate limit — up to 100 SMS/sec on Short Code, 1 SMS/sec on regular (Long Code). Regular number — workers must respect this limit.
Mobile Client: What to Build
Mobile side needs:
- Message compose form — character counter (160 for Latin, 70 for Cyrillic, concatenation when exceeded).
- Recipient segment selection — via backend API.
- Launch broadcast — POST request with parameters.
- Status tracking — polling or WebSocket/SSE for real-time progress.
// iOS — send broadcast request
struct BulkSmsRequest: Codable {
let segmentId: String
let message: String
let scheduledAt: Date?
}
func sendBulkSms(_ request: BulkSmsRequest) async throws -> BulkSmsJob {
let response = try await apiClient.post("/admin/sms/bulk", body: request)
return try response.decode(BulkSmsJob.self)
}
Character counter important UX detail. SMS splits on length exceed, each part billed separately:
fun countSmsPartsAndChars(text: String): SmsInfo {
val isGsm7 = text.all { it.isGsm7Char() }
val maxPerPart = if (isGsm7) 160 else 70
val maxConcatenated = if (isGsm7) 153 else 67
return if (text.length <= maxPerPart) {
SmsInfo(parts = 1, charsUsed = text.length, charsPerPart = maxPerPart)
} else {
val parts = ceil(text.length.toDouble() / maxConcatenated).toInt()
SmsInfo(parts = parts, charsUsed = text.length, charsPerPart = maxConcatenated)
}
}
Delivery Statuses
Twilio webhooks server on each status change: queued → sending → sent → delivered or undelivered / failed. Backend aggregates, mobile queries summary:
GET /admin/sms/jobs/{jobId}/stats
→ { "total": 5000, "sent": 4823, "delivered": 4601, "failed": 177 }
Timeline
SMS gateway integration (Twilio or SMSC), broadcast queue implementation, mobile UI with character counter, segment selection, progress tracking — 5–8 workdays.







