Швидкий старт
- 1. Створіть ключ. У робочому просторі відкрийте API та вебхуки. Ключі стають доступними після першого поповнення балансу; кожен ключ показується один раз, тому скопіюйте його або збережіть як текстовий файл.
- 2. Перевірте вартість. Відправте той самий запит на
/costs, щоб побачити одержувачів, сегменти та загальну вартість без відправлення. - 3. Відправте. Відправте запит на
/sends. У відповіді буде ідентифікатор розсилки; повідомлення будуть відправлені негайно або за вказаним часомsend_at.
curl https://sendsmsonline.cc/api/v1/sends \
-H "Authorization: Bearer $SSO_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "recipients": [ { "to": "+447700900461", "fields": { "first_name": "Tom" } }, { "to": "+12015550142", "fields": { "first_name": "Ava" } } ], "text": "Hi {{first_name}}, your table is ready at 7:30.", "name": "Friday bookings", "client_ref": "bookings-1107" }'const res = await fetch("https://sendsmsonline.cc/api/v1/sends", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.SSO_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
recipients: [{ to: "+447700900461", fields: { first_name: "Tom" } }],
text: "Hi {{first_name}}, your table is ready at 7:30.",
}),
});
const data = await res.json();
if (!res.ok) throw new Error(data.error.message);
console.log(data.send.id, data.send.cost_usd);import os, requests
r = requests.post(
"https://sendsmsonline.cc/api/v1/sends",
headers={"Authorization": f"Bearer {os.environ['SSO_API_KEY']}"},
json={
"recipients": [{"to": "+447700900461", "fields": {"first_name": "Tom"}}],
"text": "Hi {{first_name}}, your table is ready at 7:30.",
},
timeout=30,
)
data = r.json()
if r.status_code >= 400:
raise RuntimeError(data["error"]["message"])
print(data["send"]["id"], data["send"]["cost_usd"])<?php
$ch = curl_init("https://sendsmsonline.cc/api/v1/sends");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("SSO_API_KEY"),
"Content-Type: application/json",
],
CURLOPT_POSTFIELDS => json_encode([
"recipients" => [["to" => "+447700900461", "fields" => ["first_name" => "Tom"]]],
"text" => "Hi {{first_name}}, your table is ready at 7:30.",
]),
]);
$data = json_decode(curl_exec($ch), true);
echo $data["send"]["id"] ?? $data["error"]["message"];Аутентифікація
Надсилайте ваш ключ у заголовку Authorization: Authorization: Bearer sso_live_.... Ключі мають 49 символів і починаються з sso_live_. До 5 ключів на рахунок; скасуйте один у робочому просторі, і він перестане працювати негайно. Прайс-лист є публічним і не потребує ключа.
Помилки
Помилки використовують HTTP статусні коди та JSON тіло зі стабільним кодом і реченням, яке можна показати людині. Нічого не стягується, якщо запит завершується помилкою.
HTTP/1.1 402 Payment Required
{ "error": { "code": "insufficient_credit", "message": "Short by $3.20. Add funds to send.", "total": "8.62480", "credit": "5.42000" } }| Поле | Тип | Значення |
|---|---|---|
| 400 | invalid_json, text_missing, recipients_missing, too_many_recipients, too_many_segments, schedule_range, bad_send_at | Запит потребує змін. |
| 401 | unauthorized | Відсутній, неправильний або скасований ключ. |
| 402 | insufficient_credit | Недостатньо коштів для загальної вартості. |
| 404 | not_found | Немає такої розсилки, повідомлення, країни або кінцевої точки. |
| 409 | not_cancelable | Надсилання вже розпочалося. |
| 429 | rate_limited | Більше 240 запитів за хвилину. |
Обмеження
- 240 запитів за хвилину на ключ, відображається в X-RateLimit-Limit, X-RateLimit-Remaining та X-RateLimit-Reset (unix секунди).
- 5,000 номерів на запит, 2,000 символів у тексті, 10 сегментів у повідомленні.
- Повідомлення надсилаються зі швидкістю до 300 на хвилину для одного акаунта.
- send_at від 10 хвилин до 30 днів наперед.
Ціни
GET/api/v1/prices
GET/api/v1/prices/{country}
Ціна за повідомлення (один сегмент) для кожної країни, у USD з 5 знаками після коми. Ключ не потрібен.
{ "price": { "country": "GB", "name": "United Kingdom", "dial_code": "+44", "price_usd": "0.00868" }, "updated": "2026-11-07" }Баланс рахунку
GET/api/v1/account
{ "account": "SO8K2M4Q7X9TBA", "credit_usd": "41.27660", "currency": "USD" }Оцінити вартість
POST/api/v1/costs
Приймає ті самі дані, що й надсилання, і повертає вартість без фактичного відправлення чи списання коштів. Власний об’єднаний текст кожного отримувача визначає кількість сегментів, тому сума точна.
{
"recipients": 2, "segments_min": 1, "segments_max": 1, "encoding": "gsm7", "messages_billed": 2,
"total_usd": "0.01426", "credit_usd": "41.27660", "enough_credit": true,
"countries": [ { "country": "GB", "recipients": 1, "price_usd": "0.00868", "subtotal_usd": "0.00868" },
{ "country": "US", "recipients": 1, "price_usd": "0.00558", "subtotal_usd": "0.00558" } ],
"dropped": [], "too_long": null
}Створити розсилку
POST/api/v1/sends
Один текст для одного або багатьох номерів. Кошти за все надсилання списуються при його прийнятті; невдалі повідомлення повертаються по одному.
| Поле | Тип | Значення |
|---|---|---|
| to | string or array | Номери у міжнародному форматі (+447700900461). Використовуйте це або recipients. |
| recipients | array | Об’єкти з to та fields, для полів злиття: { "to": "+1...", "fields": { "first_name": "Ava" } }. |
| text | string | Повідомлення, до 2,000 символів. {{field}} замінюється для кожного отримувача; відсутні значення стають порожніми. |
| send_at | string | Необов’язковий час у форматі ISO 8601 зі зміщенням або Z. Без нього надсилання розпочинається негайно. |
| name | string | Необов’язкова мітка, яка відображається у робочому просторі (80 символів). |
| client_ref | string | Необов’язкове посилання, що повертається у вебхуках (64 символи). |
| opt_out | boolean | Додає рядок відмови, збережений у Налаштуваннях, в кінець тексту. |
| default_country | string | Код ISO, що використовується для номерів, записаних без коду країни. |
HTTP/1.1 201 Created
{
"send": { "id": "CA7Q29XMAB4DFK", "status": "queued", "source": "api", "recipients": 2, "segments_max": 1,
"encoding": "gsm7", "cost_usd": "0.01426", "refunded_usd": "0.00000", "client_ref": "bookings-1107",
"send_at": null, "created_at": "2026-11-07T10:12:03.120Z", "started_at": null, "finished_at": null },
"dropped": [ { "value": "+4412", "code": "too_short", "message": "Too short" } ]
}Номери, які не можна надіслати, перелічені в dropped і ніколи не списуються. Коди: no_country_code, too_short, too_long, not_a_number, wrong_length, not_covered, duplicate та suppressed (у вашому списку не надсилання).
Отримати або переглянути розсилки
GET/api/v1/sends/{id}
GET/api/v1/sends?limit=25&before={id}
Одне надсилання включає counts для кожного статусу повідомлення. Списки відсортовані від новіших до старіших; передайте next_before як before для наступної сторінки. Надсилання з робочого простору також відображаються тут, з source: "web".
Повідомлення
GET/api/v1/sends/{id}/messages?status=failed&limit=100
GET/api/v1/messages/{id}
{ "message": { "id": "MS3D8RW0K2QJ7N", "send_id": "CA7Q29XMAB4DFK", "to": "+447700900461", "country": "GB",
"segments": 1, "price_usd": "0.00868", "status": "delivered", "reason": null, "reason_text": null,
"created_at": "2026-11-07T10:12:03.120Z", "sent_at": "2026-11-07T10:12:04.010Z", "done_at": "2026-11-07T10:12:09.400Z" } }Скасувати розсилку
POST/api/v1/sends/{id}/cancel
Скасувати можна лише заплановані надсилання, які ще не розпочалися. Повна сума повертається на ваш баланс, а відповідь містить refunded_usd.
Вебхуки
Встановіть HTTPS-URL у робочому просторі (API та вебхуки). Ми надсилаємо POST-запит з подією, коли повідомлення доставлено або не вдалося, для надсилань як через API, так і з робочого простору. Відповідайте будь-яким статусом 2xx протягом 10 секунд. Невдалі спроби доставки повторюються до 7 разів: відразу, потім через 1 хвилину, 5 хвилин, 30 хвилин, 1 годину, 2 години та 3 години. Робочий простір відображає кожну спробу з її статусом та відповіддю.
POST https://your-server.example/sms-events
X-SSO-Event: message.delivered
X-SSO-Delivery: evt_5a0c9e31d2b7f4a68c
X-SSO-Timestamp: 1794132729
X-SSO-Signature: sha256=8f1c...e42a
{ "id": "evt_5a0c9e31d2b7f4a68c", "event": "message.delivered", "created": "2026-11-07T10:12:09.000Z",
"data": { "message": { "id": "MS3D8RW0K2QJ7N", "send_id": "CA7Q29XMAB4DFK", "to": "+447700900461", "country": "GB",
"segments": 1, "price_usd": "0.00868", "status": "delivered", "reason": null, "reason_text": null,
"client_ref": "bookings-1107", "created_at": "...", "sent_at": "...", "done_at": "..." } } }Підпис є HMAC-SHA256 від timestamp + "." + raw body з вашим секретним ключем (він починається з sso_whk_). Перевіряйте його на необробленому тілі перед аналізом, відхиляйте часові мітки старші 5 хвилин і використовуйте ідентифікатор доставки, щоб ігнорувати повторення.
import crypto from "node:crypto";
// rawBody: the request body exactly as received (a string), before JSON.parse
export function verify(rawBody, headers, secret) {
const ts = headers["x-sso-timestamp"];
const sig = headers["x-sso-signature"] || "";
const want = "sha256=" + crypto.createHmac("sha256", secret).update(`${ts}.${rawBody}`).digest("hex");
const fresh = Math.abs(Date.now() / 1000 - Number(ts)) < 300;
return fresh && sig.length === want.length && crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(want));
}import hashlib, hmac, time
def verify(raw_body: bytes, headers, secret: str) -> bool:
ts = headers.get("X-SSO-Timestamp", "")
sig = headers.get("X-SSO-Signature", "")
mac = hmac.new(secret.encode(), f"{ts}.".encode() + raw_body, hashlib.sha256)
want = "sha256=" + mac.hexdigest()
fresh = ts.isdigit() and abs(time.time() - int(ts)) < 300
return fresh and hmac.compare_digest(sig, want)<?php
function verify(string $rawBody, string $ts, string $sig, string $secret): bool {
$want = "sha256=" . hash_hmac("sha256", $ts . "." . $rawBody, $secret);
return ctype_digit($ts) && abs(time() - (int)$ts) < 300 && hash_equals($want, $sig);
}Події: message.delivered, message.failed та test з кнопки Надіслати тестову подію.
Статуси та причини
| Поле | Тип | Значення |
|---|---|---|
| queued | status | Очікує на відправлення з нашої черги. |
| sent | status | Передано до мережі, очікується звіт про доставку. Більшість звітів надходять протягом кількох хвилин; деякі мережі можуть зайняти до 24 годин. |
| delivered | status | Мережа підтвердила, що телефон отримав повідомлення. |
| failed | status | Не доставлено. Вартість повернуто на ваш баланс. |
| canceled | status | Не відправлено, оскільки ви скасували. Повернуто кошти. |
Причини невдачі (поле reason):
| Поле | Тип | Значення |
|---|---|---|
| network_error | reason | Помилка мережі |
| absent | reason | Телефон вимкнено або поза зоною покриття |
| not_in_service | reason | Номер не обслуговується |
| blocked | reason | Заблоковано мережею або одержувачем |
| landline | reason | Стаціонарний номер або номер, який не може отримувати SMS |
| content_filter | reason | Відхилено фільтром вмісту мережі |
| expired | reason | Закінчився термін: звіт не надійшов до закінчення терміну мережі |
| canceled | reason | Скасовано перед відправкою |
| unknown | reason | Невідома відповідь мережі |
Перевірки сервісу знаходяться на сторінці статусу, а зміни в API, у журналі змін.