SMS
The five endpoints that price a bulk send, make it and show its history.
Overview
A customer can send messages in bulk, paying from their own wallet. The flow runs in three steps: pick a sender name, get a price, send.
The price follows the destination country and the message's part count. A longer text, or one leaving the basic alphabet, raises the part count and the amount grows with it.
Some countries want the sender name registered beforehand. Numbers going to a country without that registration do not drop quietly: they appear in the skipped list with the reason.
Reference
Listing the Sender Names
Returns the sender names a send can use, along with their country registrations.
curl 'https://panel.example.com/api/v1/client/sms/senders' \
-H "Authorization: Bearer $CLIENT_KEY"const res = await fetch('https://panel.example.com/api/v1/client/sms/senders', {
headers: { Authorization: `Bearer ${clientKey}` },
});
const { data } = await res.json();
const preferred = data.find((s) => s.is_default) ?? data[0];$ch = curl_init('https://panel.example.com/api/v1/client/sms/senders');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $clientKey],
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// An empty country list is NO BAR: most countries want no pre-registration and the name works there straight away.
$senders = Kernel::internal('client:Sms/GetSmsSenders', ['owner_id' => $uid])['data'];
$name = $senders[0]['name'] ?? null;Getting a Quote
Says what a send will cost and what will be left out.
curl -X POST 'https://panel.example.com/api/v1/client/sms/quote' \
-H "Authorization: Bearer $CLIENT_KEY" \
-H 'Content-Type: application/json' \
-d '{"sender":"TESTBRAND","message":"Your code is 482913","numbers":["+15551112233"]}'const res = await fetch('https://panel.example.com/api/v1/client/sms/quote', {
method: 'POST',
headers: {
Authorization: `Bearer ${clientKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ sender, message, numbers }),
});
const { data } = await res.json();
if (data.skipped.total) reviewSkipped(data.skipped);$ch = curl_init('https://panel.example.com/api/v1/client/sms/quote');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $clientKey,
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode(compact('sender', 'message', 'numbers')),
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// A dropped destination is NO ERROR: the quote always succeeds and the loss is read from the skipped block.
$q = Kernel::internal('client:Sms/QuoteSms',
['owner_id' => $uid, 'sender' => $sender, 'message' => $text, 'numbers' => $nums])['data'];
$willCost = $q['total']['amount'];
$willDrop = $q['skipped']['total'];Sending in Bulk
Sends the message and takes the amount from the wallet.
curl -X POST 'https://panel.example.com/api/v1/client/sms/send' \
-H "Authorization: Bearer $CLIENT_KEY" \
-H 'Content-Type: application/json' \
-d '{"sender":"TESTBRAND","message":"Your code is 482913","numbers":["+15551112233"]}'const res = await fetch('https://panel.example.com/api/v1/client/sms/send', {
method: 'POST',
headers: {
Authorization: `Bearer ${clientKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ sender, message, numbers }),
});
const { data } = await res.json();
console.log(data.accepted, data.total, data.balance);$ch = curl_init('https://panel.example.com/api/v1/client/sms/send');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $clientKey,
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode(compact('sender', 'message', 'numbers')),
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// The SERVER prices it again: the figure in the quote is no promise and the send knows its own price.
$r = Kernel::internal('client:Sms/SendSms',
['owner_id' => $uid, 'sender' => $sender, 'message' => $text, 'numbers' => $nums])['data'];
$charged = $r['total']['amount'];The Sending History
Returns the sends made, newest first.
curl 'https://panel.example.com/api/v1/client/sms/messages' \
-H "Authorization: Bearer $CLIENT_KEY"const res = await fetch('https://panel.example.com/api/v1/client/sms/messages', {
headers: { Authorization: `Bearer ${clientKey}` },
});
const { data, meta } = await res.json();$ch = curl_init('https://panel.example.com/api/v1/client/sms/messages');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $clientKey],
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// The history holds NO RECIPIENT NUMBERS: they come on the single-send endpoint alone.
$rows = Kernel::internal('client:Sms/GetSmsMessages', ['owner_id' => $uid])['data'];
$one = Kernel::internal('client:Sms/GetSmsMessage',
['owner_id' => $uid, 'id' => $rows[0]['message_id']])['data'];Reading One Send
Returns one send together with its recipient numbers.
curl 'https://panel.example.com/api/v1/client/sms/messages/4520' \
-H "Authorization: Bearer $CLIENT_KEY"const res = await fetch(`https://panel.example.com/api/v1/client/sms/messages/${id}`, {
headers: { Authorization: `Bearer ${clientKey}` },
});
const { data } = await res.json();
console.log(data.numbers.length, data.report_id);$ch = curl_init('https://panel.example.com/api/v1/client/sms/messages/' . $id);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $clientKey],
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// A per-recipient DELIVERY REPORT is ABSENT here: the live provider report stays in the panel view.
$m = Kernel::internal('client:Sms/GetSmsMessage', ['owner_id' => $uid, 'id' => $id])['data'];
$sentTo = $m['numbers'];Pitfalls
The quote and send endpoints do not drop invalid numbers, unpriced countries and countries where the sender is unregistered in silence, and they do not stop the send either: the rest goes and the dropped ones are listed in the answer. Without reading that block you never notice part of the message never left.
The send endpoint prices it again itself and never trusts a figure the caller sends. A price or a sender registration moving between the quote and the send can change what is taken. Read the settled figure from the send answer.
The wallet is charged with one conditional update: two sends at once cannot push the balance below zero. Where the provider refuses, the whole charge comes back and no message leaves. A failed send costs nothing.
A message in the basic alphabet fits more characters per part, while a single letter outside it drops the text into the other encoding and the capacity per part falls by more than half. The amount rests on parts, so the cost jumps. Read the encoding field in the quote.
The sending history gives summary rows and the recipient numbers come only when you read one send. A per-recipient delivery report is absent from the API entirely and stays in the panel, since it is pulled live from the provider.
Related Articles
Thanks for your feedback!
Our support team is here around the clock for anything you can't find above.