API Docs
Send SMS and check your wallet balance from your own systems. Generate a personal API key from your dashboard under SMS Api, then integrate directly against the endpoints below — no SDK to install.
POST Endpoint
/api/v1/sms/send
Headers
Authorization: Bearer sms_live_... Content-Type: application/json
Request body
{
"senderId": "YourBrand",
"to": ["+233244000000", "+233201234567"],
"message": "Hello from the API",
"unicode": false
}Response — one recipient (200, sent immediately)
{
"status": "sent",
"campaignId": "9c1e...-...-...",
"sent": 1,
"failed": 0,
"unitsCharged": 1,
"walletBalance": 148,
"recipients": [{ "to": "+233244000000", "status": "sent" }]
}Response — two or more recipients (202, queued)
{
"status": "queued",
"campaignId": "9c1e...-...-...",
"recipientCount": 2
}campaignId to get sent/failed/unitsCharged. Branch on the status field ("sent" or "queued") rather than the HTTP code alone.Every send debits the caller's own wallet and requires an approved Sender ID on their account, exactly like sending from the dashboard. Errors return a non-2xx status with { "error": "..." }.
GET Endpoint
/api/v1/sms/campaigns/{campaignId}Headers
Authorization: Bearer sms_live_...
Response (200)
{
"campaignId": "9c1e...-...-...",
"status": "completed",
"recipientCount": 2,
"sent": 2,
"failed": 0,
"unitsCharged": 2,
"createdAt": "2026-09-24T03:51:12.271Z",
"completedAt": "2026-09-24T03:52:32.035Z",
"recipients": [
{ "to": "+233244000000", "status": "sent", "time": "2026-09-24T03:52:20.118Z" },
{ "to": "+233201234567", "status": "sent", "time": "2026-09-24T03:52:31.902Z" }
]
}status moves queued → processing → completed (or failed). Poll every few seconds until it's no longer queued or processing.
All times are ISO 8601 in UTC (GMT). time is when our provider accepted that message for delivery (for a failed message, when the attempt was made). It is not a carrier delivery confirmation. completedAt is null until the campaign has finished.
GET Endpoint
/api/v1/sms/balance
Headers
Authorization: Bearer sms_live_...
Response (200)
{
"balance": 148,
"currency": "units"
}For systems that can't send custom headers, the API key can go in the query string instead — mirrors the old system's own API shape, for drop-in migration. Prefer the header-based endpoints above where possible: a key in a URL can end up in server logs or browser history.
Endpoints
GET /api/v1/sms/api?action=send-sms&api_key=...&to=...&from=...&sms=... GET /api/v1/sms/api?action=check-status&api_key=...&campaign_id=... GET /api/v1/sms/api?action=check-balance&api_key=... GET /api/v1/contacts/api?action=subscribe-us&api_key=...&phone_number=...
action=send-sms follows the same rule as the header-based endpoint above. With one recipient it sends immediately and returns { "status": "sent", "campaign_id": "...", "sent": 1, "failed": 0, "units_charged": 1 }. With two or more it returns { "status": "queued", "campaign_id": "...", "recipients": N }, and you use action=check-status with that campaign_id to poll the outcome.