Skip to Content

SMS

Base path: /sms

Send SMS

POST /sms/send

{ "recipients": ["+254712345678", "0798765432"], "message": "Your order is ready for pickup.", "sender_id": null }
  • recipients — up to 10,000 phone numbers per request. Any format (0712..., 254712..., +254712...) is accepted and normalised to E.164.
  • message — up to 918 characters (6 SMS pages).
  • sender_id — optional; defaults to your account’s default Sender ID, or the shared WakaliLabs ID if you haven’t registered your own. When sending via the shared ID, your company name is automatically prefixed to the message body.

202 response:

{ "job_id": "b6b2b6f0-...", "recipients_count": 2, "cost_kes": 1.0, "status": "sent" }

Billing is calculated on the final outgoing message — after the company-name prefix is applied — since a long prefix can push a message over a multi-part boundary. Your wallet is debited upfront and automatically refunded if the send fails.

402 Insufficient balance if your wallet can’t cover the cost. Top up via POST /wallet/topup.

List messages

GET /sms/messages?page=1&page_size=20

Paginated history of your own messages, newest first. Each item includes status (queued | sent | delivered | failed), provider_status (raw carrier status, for display), and source (api | browser).

Get a single message

GET /sms/messages/{message_id} — look up by the message’s own id (not the provider ID). Returns whatever is currently stored — does not make a live carrier call.

Live delivery status

GET /sms/delivery/{message_id} — looked up by provider message id, not the internal id. Triggers a live status check against the carrier and syncs the result to our records before returning. Use this when you need the freshest possible status; use the plain messages endpoints above for routine polling to avoid unnecessary upstream calls.

Stats

GET /sms/stats — counts of your messages grouped by status:

{ "total": 240, "delivered": 210, "sent": 12, "failed": 15, "queued": 3 }
Last updated on