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.

Send SMS

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
}

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": "..." }.

Check send status

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.

Check balance

GET Endpoint

/api/v1/sms/balance

Headers

Authorization: Bearer sms_live_...

Response (200)

{
  "balance": 148,
  "currency": "units"
}
Legacy URL-based endpoints

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.