SMS

4 vues Markdown

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

get/api/v1/client/sms/senders
Sms/GetSmsSenders the key's owner

Returns the sender names a send can use, along with their country registrations.

Response fields data[] — 4
idintThe sender id.
namestringThe text the recipient sees. This is what the send endpoint takes.
is_defaultboolWhether it is the account's default.
countriesobject[]Where it stands in the countries wanting pre-registration. Each carries a country code and a state, and the countries wanting none never appear.
Errors 3
sms_disabled422The operator closed the international message service.
sms_api_disabled422The operator closed the message interface. The panel keeps working.
insufficient_scope403The key lacks the required scope.
Request
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

post/api/v1/client/sms/quote
Sms/QuoteSms nothing is sent

Says what a send will cost and what will be left out.

Body 3
senderstringreqThe sender name. It has to be one of the account's live names.
messagestringreqThe message text. Text past the part ceiling is cut and the cut is reported.
numbersarrayreqThe recipient numbers. In international form, and one string is split on lines, commas and semicolons.
Response fields data — 6
messageobjectThe text analysis. The encoding, the length, how many parts, whether it was cut and the text that will truly go.
recipientsintHow many numbers will be charged and sent.
total_partsintThe parts in all. This is the billing unit.
totalobjectWhat the send endpoint will take.
countriesobject[]The breakdown per destination. A code, a name, a count, the parts, the unit price and the total.
skippedobjectWhat was left out. A count, the entries that could not be read, the countries with no price and the countries where the sender is not registered.
Errors 6
sms_disabled422The operator closed the international message service.
sms_api_disabled422The operator closed the message interface. The panel keeps working.
sender_invalid422The sender is missing, not yours or not live.
message_required422The message is empty.
numbers_required422The recipient list is empty.
insufficient_scope403The key lacks the required scope.
Request
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

post/api/v1/client/sms/send
Sms/SendSms it takes from the wallet

Sends the message and takes the amount from the wallet.

Body 3
senderstringreqThe sender name. It has to be one of the account's live names.
messagestringreqThe message text. Text past the part ceiling is cut and the cut is reported.
numbersarrayreqThe recipient numbers. In international form, and one string is split on lines, commas and semicolons.
Response fields 201 — data — 9
message_idintThe send id. The history endpoint is asked with it.
senderstringThe sender used.
acceptedintHow many recipients were charged and handed to the provider.
partsintThe parts per message.
total_partsintThe parts in all.
totalobjectWhat was taken from the wallet.
balanceobjectThe wallet after the charge.
report_idstringThe provider's batch reference.
skippedobjectWhat was left out. A count, the entries that could not be read, the countries with no price and the countries where the sender is not registered.
Errors 10
sms_disabled422The operator closed the international message service.
sms_api_disabled422The operator closed the message interface. The panel keeps working.
sender_invalid422The sender is missing, not yours or not live.
no_recipients422Every number was left out. The reason comes in the answer's detail.
price_unavailable422The amount priced to zero.
send_rejected422A hook refused the send.
sms_module_unavailable422No sending provider is configured.
insufficient_balance422The wallet does not cover it. Nothing is sent and nothing is taken.
send_failed500The provider refused the send. The whole charge is returned.
insufficient_scope403The key lacks the required scope.
Request
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

get/api/v1/client/sms/messages
Sms/GetSmsMessages the key's owner

Returns the sends made, newest first.

Query 2
pageintWhich page.
limitintRows per page. 100 at the most.
Response fields data[] — 9 + meta — 4
message_idintThe send id.
senderstringThe sender used.
textstringThe text sent.
recipientsintHow many recipients were charged.
partsintThe parts per message.
total_partsintThe parts in all.
totalobjectWhat was taken.
countriesobject[]The breakdown per destination.
created_atstringWhen it was sent.
total_countintHow many sends there are. It comes back under meta as the total.
pageintThe page you are on.
limitintThe page size.
next_pageintThe next page.
Errors 3
sms_disabled422The operator closed the international message service.
sms_api_disabled422The operator closed the message interface. The panel keeps working.
insufficient_scope403The key lacks the required scope.
Request
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

get/api/v1/client/sms/messages/{id}
Sms/GetSmsMessage the key's owner

Returns one send together with its recipient numbers.

Response fields data — 11
dataobjectThe fields of a history row.
numbersstring[]The numbers charged and sent.
report_idstringThe provider's batch reference.
Errors 4
not_found404No such send, or it belongs to another customer.
sms_disabled422The operator closed the international message service.
sms_api_disabled422The operator closed the message interface. The panel keeps working.
insufficient_scope403The key lacks the required scope.
Request
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

Dropped recipients return no error

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 quote is not a promise

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 charge is atomic and the refund is whole

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.

Leaving the basic alphabet raises the part count

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 history carries no numbers

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.

Cet article vous a-t-il été utile ?

Merci pour votre retour !

Besoin d'aide supplémentaire ?

Notre équipe d'assistance est disponible 24h/24 pour tout ce que vous ne trouvez pas ci-dessus.