cURL
curl --request POST \
--url https://api.belio.co.ke/message/{serviceId} \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"type": "SendToEach",
"messages": [
{
"text": "<string>",
"phone": "<string>"
}
]
}
'import requests
url = "https://api.belio.co.ke/message/{serviceId}"
payload = {
"type": "SendToEach",
"messages": [
{
"text": "<string>",
"phone": "<string>"
}
]
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({type: 'SendToEach', messages: [{text: '<string>', phone: '<string>'}]})
};
fetch('https://api.belio.co.ke/message/{serviceId}', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.belio.co.ke/message/{serviceId}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'type' => 'SendToEach',
'messages' => [
[
'text' => '<string>',
'phone' => '<string>'
]
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.belio.co.ke/message/{serviceId}"
payload := strings.NewReader("{\n \"type\": \"SendToEach\",\n \"messages\": [\n {\n \"text\": \"<string>\",\n \"phone\": \"<string>\"\n }\n ]\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.belio.co.ke/message/{serviceId}")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"type\": \"SendToEach\",\n \"messages\": [\n {\n \"text\": \"<string>\",\n \"phone\": \"<string>\"\n }\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.belio.co.ke/message/{serviceId}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"type\": \"SendToEach\",\n \"messages\": [\n {\n \"text\": \"<string>\",\n \"phone\": \"<string>\"\n }\n ]\n}"
response = http.request(request)
puts response.read_body{
"desc": {
"requestId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"units": {
"Safaricom": 123,
"Airtel": 123
},
"addresses": {
"Safaricom": 123,
"Airtel": 123
}
}
}{
"desc": "<string>",
"result": "<unknown>"
}{
"desc": "<string>",
"result": "<unknown>"
}{
"desc": "<string>",
"result": "<unknown>"
}{
"desc": "<string>",
"result": "<unknown>"
}{
"error": "<string>",
"error_description": "<string>"
}{
"desc": "<string>",
"result": "<unknown>"
}SMS
Send Message
Send a message to one or multiple recipients
POST
/
message
/
{serviceId}
cURL
curl --request POST \
--url https://api.belio.co.ke/message/{serviceId} \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"type": "SendToEach",
"messages": [
{
"text": "<string>",
"phone": "<string>"
}
]
}
'import requests
url = "https://api.belio.co.ke/message/{serviceId}"
payload = {
"type": "SendToEach",
"messages": [
{
"text": "<string>",
"phone": "<string>"
}
]
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({type: 'SendToEach', messages: [{text: '<string>', phone: '<string>'}]})
};
fetch('https://api.belio.co.ke/message/{serviceId}', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.belio.co.ke/message/{serviceId}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'type' => 'SendToEach',
'messages' => [
[
'text' => '<string>',
'phone' => '<string>'
]
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.belio.co.ke/message/{serviceId}"
payload := strings.NewReader("{\n \"type\": \"SendToEach\",\n \"messages\": [\n {\n \"text\": \"<string>\",\n \"phone\": \"<string>\"\n }\n ]\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.belio.co.ke/message/{serviceId}")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"type\": \"SendToEach\",\n \"messages\": [\n {\n \"text\": \"<string>\",\n \"phone\": \"<string>\"\n }\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.belio.co.ke/message/{serviceId}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"type\": \"SendToEach\",\n \"messages\": [\n {\n \"text\": \"<string>\",\n \"phone\": \"<string>\"\n }\n ]\n}"
response = http.request(request)
puts response.read_body{
"desc": {
"requestId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"units": {
"Safaricom": 123,
"Airtel": 123
},
"addresses": {
"Safaricom": 123,
"Airtel": 123
}
}
}{
"desc": "<string>",
"result": "<unknown>"
}{
"desc": "<string>",
"result": "<unknown>"
}{
"desc": "<string>",
"result": "<unknown>"
}{
"desc": "<string>",
"result": "<unknown>"
}{
"error": "<string>",
"error_description": "<string>"
}{
"desc": "<string>",
"result": "<unknown>"
}Refer to Authentication for information on how to obtain a
bearer token. This endpoint requires the
message.sms.send.oneway API client
authorization scope to send one way messages. You can set up scopes on the
API Clients page.Rate Limit
This endpoint is rate limited to 400 messages per second.Request Modes
The requiredtype field selects one of four request modes. Free-form modes
compose raw message text; template modes send a pre-approved template by
referencing its templateId (see SMS Templates).
type | Kind | Description |
|---|---|---|
SendToEach | Free-form | Message tailored per recipient |
SendToMany | Free-form | Same message sent to multiple recipients |
TemplateSendToEach | Template | Each recipient receives their own parameter values |
TemplateSendToMany | Template | One shared parameter set sent to multiple recipients |
Limits
| Rule | Free-form (SendToEach / SendToMany) | Template (TemplateSendToEach / TemplateSendToMany) |
|---|---|---|
| Maximum recipients per request | 100 | 10 |
| Maximum message length | 960 characters | 960 characters (after parameters are resolved) |
| SMS message unit size | 160 characters | 160 characters |
Request Schemas
- SendToEach
- SendToMany
- TemplateSendToEach
- TemplateSendToMany
Send individually tailored free-form messages to multiple recipients.
Each item in
| Field | Type | Required | Description |
|---|---|---|---|
type | string | yes | Must be "SendToEach" |
messages | object[] | yes | Per-recipient entries (1–100) |
receiptRequest | object | no | Optional delivery receipt callback |
messages:| Field | Type | Required | Description |
|---|---|---|---|
phone | string | yes | Recipient phone number |
message | string | yes | Message text (max 960 characters) |
curl --request POST \
--url https://api.belio.co.ke/message/{serviceId} \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{
"type": "SendToEach",
"messages": [
{
"phone": "254700111213",
"message": "Hi Alice, your order #1042 has shipped."
},
{
"phone": "254700111214",
"message": "Hi Bob, your order #1043 has shipped."
}
]
}'
Send the same free-form message to multiple phone numbers.
| Field | Type | Required | Description |
|---|---|---|---|
type | string | yes | Must be "SendToMany" |
addresses | string[] | yes | Recipient phone numbers (1–100) |
message | string | yes | Message text (max 960 characters) |
receiptRequest | object | no | Optional delivery receipt callback |
curl --request POST \
--url https://api.belio.co.ke/message/{serviceId} \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{
"type": "SendToMany",
"addresses": [
"+254700111213",
"254750108109"
],
"message": "Your appointment is confirmed for tomorrow at 10am.",
"receiptRequest": {
"correlator": "reminder-batch-001",
"callbackUrl": "https://example.com/dlr"
}
}'
Send the same template to multiple recipients, each with their own parameter values.
Each item in
| Field | Type | Required | Description |
|---|---|---|---|
type | string | yes | Must be "TemplateSendToEach" |
templateId | string | yes | ID of the SMS template |
messages | object[] | yes | Per-recipient entries (1–10) |
receiptRequest | object | no | Optional delivery receipt callback |
messages:| Field | Type | Required | Description |
|---|---|---|---|
phone | string | yes | Recipient phone number |
params | object[] | yes | Template parameters for this recipient |
curl --request POST \
--url https://api.belio.co.ke/message/{serviceId} \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{
"type": "TemplateSendToEach",
"templateId": "tpl-sms-001",
"messages": [
{
"phone": "254700111213",
"params": [
{ "name": "sample", "value": "Alice" },
{ "name": "code", "value": "111111" }
]
},
{
"phone": "254700111214",
"params": [
{ "name": "sample", "value": "Bob" },
{ "name": "code", "value": "222222" }
]
}
]
}'
Send the same resolved template message to multiple phone numbers.
| Field | Type | Required | Description |
|---|---|---|---|
type | string | yes | Must be "TemplateSendToMany" |
templateId | string | yes | ID of the SMS template |
addresses | string[] | yes | Recipient phone numbers (1–10) |
params | object[] | yes | Template parameter values |
receiptRequest | object | no | Optional delivery receipt callback |
curl --request POST \
--url https://api.belio.co.ke/message/{serviceId} \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{
"type": "TemplateSendToMany",
"templateId": "tpl-sms-001",
"addresses": [
"+254700111213",
"254750108109"
],
"params": [
{ "name": "sample", "value": "Alice" },
{ "name": "code", "value": "654321" }
],
"receiptRequest": {
"correlator": "otp-batch-001",
"callbackUrl": "https://example.com/dlr"
}
}'
Important: for
TemplateSendToEach and TemplateSendToMany, after all
parameters are substituted, the final message must not exceed 960
characters. A longer resolved message is rejected with Resolved content cannot be longer than 960 characters. There is no truncation or fallback.Errors
General errors
| HTTP | Condition | Example desc |
|---|---|---|
| 400 | Invalid phone numbers | Invalid phone numbers: ... |
| 400 | Empty addresses / messages | No valid recipients found... or No valid messages found... |
| 400 | Too many recipients | Maximum number of recipients exceeded. (100 for free-form, 10 for template modes) |
| 400 | Template not found | Message template tpl-sms-001 not found. |
| 400 | Template not sendable | Message template tpl-sms-001 is not sendable. Current status: Active and approval status: PENDING |
| 400 | Managed service not found | Managed service descriptor not found for key: otp-service. See Belio-managed Senders and Approval. |
| 400 | Category mismatch | Template category AUTHENTICATION does not match managed service descriptor category MARKETING. See Belio-managed Senders and Approval. |
| 400 | Insufficient message units | Team balance error message |
| 400 | Invalid receiptRequest | Correlator or callback URL validation message |
| 401 | Authentication failure | — |
Template parameter validation errors
When parameter validation fails, the response isHTTP 400 with desc set to Template parameter validation failed.
For TemplateSendToEach, errors are keyed by recipient phone number:
{
"desc": "Template parameter validation failed",
"result": {
"254700111213": [
"Missing required parameter 'code'"
],
"254700111214": [
"Missing required parameter 'code'",
"Parameter 'sample': Value for parameter 'sample' must be a valid name"
]
}
}
errors key:
{
"desc": "Template parameter validation failed",
"result": {
"errors": [
"Missing required parameter 'code'",
"Parameter 'code': Value for parameter 'code' must not exceed 6 characters"
]
}
}
Common parameter validation messages
| Message | Cause |
|---|---|
Missing required parameter '{name}' | Required placeholder with no default; not supplied in params |
Unknown parameter '{name}' | Supplied parameter not defined on the template |
Duplicate parameter '{name}' | Same name appears more than once in params |
Parameter '{name}': Value for parameter '{name}' must not exceed N characters | Value longer than maxLength |
Parameter '{name}': Value for parameter '{name}' must be alphanumeric | format is Alphanumeric |
Parameter '{name}': Value for parameter '{name}' must be numeric | format is Numeric |
Parameter '{name}': Value for parameter '{name}' must be a valid name | format is Name |
Value for parameter '{name}' must be non-empty | Empty value |
Value for parameter '{name}' contains forbidden characters | Value contains {, }, or control characters |
Name must be a non-empty identifier using letters, digits, and underscores | Invalid parameter name |
Resolved content cannot be longer than 960 characters | Substituted message too long |
Success Response
On success the endpoint returnsHTTP 200:
{
"result": {
"type": "SmsResponse",
"requestId": "550e8400-e29b-41d4-a716-446655440000",
"units": {
"Safaricom": 1,
"Airtel": 1
},
"addresses": {
"Safaricom": 1,
"Airtel": 1
}
}
}
| Field | Description |
|---|---|
| requestId | Unique ID for this send request, used to correlate with delivery receipts |
| units | Message units consumed per mobile network channel |
| addresses | Number of recipients routed per channel |
Authorizations
Bearer authentication header of the form Bearer <token>, where <token> is your auth token.
Path Parameters
Unique identifier for the messaging service
Body
application/json
Message details for sending to one or multiple recipients
Response
Message(s) sent successfully
The response
Show child attributes
Show child attributes
⌘I