Your first SMS
This page exists to be read once. If you already shipped the Quickstart, skip ahead to Send an instant SMS for the multi-recipient patterns.
The minimum viable request
Section titled “The minimum viable request”POST /api/v1/lisoloo/sms-api/send HTTP/1.1Host: {BASE_URL}app-key: sk_test_kqz3pX9aB7nT2vR4wY8eD1fH6jM5oU0iContent-Type: application/json
{ "to": ["+243998857000"], "message": "Hello from Lisoloo!"}{BASE_URL} is the sandbox host from your
dev portal → Lisoloo → Developer → API keys.
Two required fields:
tois always an array — even for a single recipient. Each entry must be E.164 (+CCNNNNNN…). The gateway will reject the request with1104 INVALID_PHONE_NUMBERif any entry fails the regex^\+?\d{1,15}$.messageis the SMS body. Plain UTF-8. Length determines how many SMS segments will be billed — see Character limits.
Everything else (sender_id, sending_type, callback_url,
scheduled_dates, recurring_schedule) is optional. With none of them
set the gateway treats the request as sending_type: "immediate" and
queues for immediate dispatch.
The minimum viable response
Section titled “The minimum viable response”{ "status_code": 201, "data": { "message_id": "507f1f77bcf86cd799439011", "total_recipients": 1, "total_messages": 1, "total_cost": 0.02, "currency": "USD", "status": "pending", "sending_type": "immediate" }}What each field means:
| Field | Type | Notes |
|---|---|---|
status_code | int | Mirrors the HTTP status — 201 on a successful queue. |
data.message_id | string | The canonical ID. Use it on GET /status/{message_id} and to correlate webhook events. |
data.total_recipients | int | Count of unique numbers in to. |
data.total_messages | int | total_recipients × segments_per_message. See Character limits. |
data.total_cost | float | Pre-VAT cost in currency. Already deducted from your balance. |
data.currency | string | Three-letter ISO 4217 code. |
data.status | string | Initial status. pending for immediate, scheduled for scheduled/recurring. See Message lifecycle. |
data.sending_type | string | Echoes back the sending_type from the request (immediate, scheduled, or recurring). |
What just happened on the server side
Section titled “What just happened on the server side”- The gateway received the request and validated the
app-keyheader. - It checked your balance —
available_sms≥total_messages. - It enqueued one delivery job per recipient against the carrier connector.
- It returned
201immediately. The actual carrier handoff happens asynchronously; the status transitions throughpending → processing → sent → deliveredover the next few seconds. - If a
callback_urlor merchant-levelwebhook_urlwas configured, the gateway POSTs a delivery receipt at each transition.
What can go wrong
Section titled “What can go wrong”| Symptom | Likely cause | Fix |
|---|---|---|
401 Missing or invalid app-key | Header missing, mistyped, or wrong environment. | Re-check the header name (app-key, hyphenated, lowercase) and the key prefix matches the base URL. |
400 with 1104 INVALID_PHONE_NUMBER | One of the to entries isn’t E.164. | Validate each entry against ^\+?\d{1,15}$ before sending. |
400 with 1109 MISSING_REQUIRED_FIELD | to or message is missing or empty. | Both fields are mandatory; an empty array doesn’t count. |
402 with 1402 INSUFFICIENT_BALANCE | Your account doesn’t have enough credit. | Top up via the Bloonio dashboard. |
429 with 1301 RATE_LIMIT_EXCEEDED | More than the per-minute cap. | Honour Retry-After. See Rate limits. |
The full code list is at the error catalogue.
See also
Section titled “See also”- Send an instant SMS — multi-recipient patterns
- Schedule an SMS — adding
scheduled_dates - Check message status — polling
- Webhooks overview — async receipts