API для отправки SMS через ваш Android-телефон
REST API для отправки SMS с ваших Android-телефонов из любой CRM, сайта или своей системы. SMS уходят с SIM-карт телефонов, подключённых к аккаунту, ответы клиентов и статусы доставки приходят вебхуками. Отправка через API учитывается в лимите тарифа так же, как из Битрикс24.
https://smstrubka.ru/api/v1 · Формат: JSON, UTF-8 · Спецификация: OpenAPI 3 (openapi.json) — можно импортировать в Postman или Swagger.- Быстрый старт
- Авторизация и лимиты
- Ошибки
- Отправить SMS
- Статусы сообщений
- Журнал и статус сообщения
- Телефоны и аккаунт
- Вебхуки
- Проверка подписи
- Советы
Быстрый старт
- Подключите Android-телефон в личном кабинете (раздел «Телефон»).
- Создайте ключ в разделе «API». Ключ показывается один раз.
- Отправьте SMS:
curl https://smstrubka.ru/api/v1/messages \
-H "Authorization: Bearer trk_ваш_ключ" \
-H "Content-Type: application/json" \
-d '{"phone": "+79001234567", "text": "Ваш заказ готов", "clientRef": "order-1043"}'
Ответ 202 Accepted — SMS передано телефону:
{
"message": {
"id": "7c0b8f3e-2d1a-4a7e-9f43-0f6c2a1b5d90",
"direction": "out",
"phone": "+79001234567",
"text": "Ваш заказ готов",
"status": "queued",
"error": null,
"source": "api",
"sim": null,
"deviceId": null,
"clientRef": "order-1043",
"createdAt": "2026-10-23T12:00:00.000Z",
"updatedAt": "2026-10-23T12:00:00.000Z"
}
}
Авторизация и лимиты
Ключ передаётся в заголовке Authorization: Bearer trk_… (или X-Api-Key: trk_…). Ключей может быть несколько — например, отдельный для каждой системы; ненужный ключ отзывается в кабинете. Храните ключ на сервере: не вставляйте его в код страниц и мобильных приложений.
- Не больше 20 запросов в секунду на ключ, при превышении —
429и заголовокRetry-After. - Количество SMS ограничено тарифом; скорость отправки с телефона (пауза между SMS, лимит, рабочие часы) настраивается в кабинете: «Телефон» → «Скорость отправки».
Ошибки
При ошибке API возвращает HTTP-статус и тело {"error": {"code": "…", "message": "…"}}. Сообщение — по-русски, его можно показать пользователю.
| HTTP | code | Что значит |
|---|---|---|
| 400 | invalid_request, invalid_phone, text_too_long, missing_vars, invalid_json | Ошибка в запросе |
| 401 | unauthorized | Нет ключа, ключ неверный или отозван |
| 403 | account_blocked | Аккаунт заблокирован |
| 404 | not_found | Нет такого сообщения, вебхука или метода |
| 409 | limit_reached | Превышено число вебхуков или ключей |
| 422 | rejected | SMS не принято: исчерпан лимит тарифа, не подключён телефон, превышено число телефонов. Сообщение сохраняется со статусом rejected и возвращается в поле message |
| 429 | rate_limited | Слишком много запросов |
| 502 | gateway_error | Сервер телефонов не принял сообщение — можно повторить запрос с тем же clientRef |
Отправить SMS
POST/messages
| Поле | Тип | Описание |
|---|---|---|
phone | string | Обязательно. Номер получателя: +79001234567; 89001234567 и 9001234567 приводятся к +7 |
text | string | Обязательно. Текст, до 1600 символов (длинные SMS телефон отправит частями) |
sim | integer | SIM-карта: 1, 2 или 3. По умолчанию — основная SIM телефона |
deviceId | string | Телефон из GET /devices. По умолчанию — любой подключённый |
template | string | Вместо text: id или название шаблона из кабинета (раздел «Шаблоны») |
vars | object | Значения подстановок: для текста «Здравствуйте, {имя}!» — {"имя": "Анна"}. Регистр не важен. Не хватает значения — ошибка 400 missing_vars |
sendAt | string | Отправить позже: дата и время ISO 8601 с часовым поясом, например 2026-11-01T10:00:00+03:00, не дальше 90 дней. Сообщение получает статус scheduled; лимит тарифа и выбор телефона проверяются в момент отправки |
shortLinks | boolean | Заменить длинные ссылки в тексте на короткие smstrubka.ru/s/… со счётчиком переходов (отчёт — в кабинете, раздел «Сообщения»). По умолчанию — как включено в кабинете; false оставит ссылки как есть |
clientRef | string | Ваш идентификатор (номер заказа, id записи в CRM), до 100 символов. Повторный запрос с тем же clientRef не отправит SMS второй раз — вернётся то же сообщение с кодом 200 и заголовком Idempotent-Replayed: true. То же самое — заголовок Idempotency-Key |
POST /api/v1/messages
{ "phone": "+79001234567", "template": "Напоминание о записи", "vars": { "имя": "Анна", "время": "11:30" },
"sendAt": "2026-10-25T09:00:00+03:00", "clientRef": "visit-5512" }
DELETE/messages/{id} — отменить запланированное сообщение (статус станет cancelled). Отправленные отменить нельзя.
GET/templates — шаблоны аккаунта: {"templates": [{"id", "name", "text", "vars": ["имя", "время"]}]}.
Ответы: 202 — принято; 200 — повтор по clientRef; 422 — не принято (см. ошибки).
Статусы сообщений
| status | Описание |
|---|---|
scheduled | Запланировано на sendAt |
cancelled | Запланированное сообщение отменено |
queued | Передано телефону, ждёт отправки (телефон соблюдает паузу и лимит из настроек) |
sent | Телефон отправил SMS оператору |
delivered | Оператор подтвердил доставку получателю |
failed | Не отправлено или не доставлено; причина — в поле error |
rejected | Не принято «Трубкой» (лимит тарифа, нет телефона) |
received | Входящее SMS получено |
forwarded | Входящее SMS передано в Битрикс24 |
Статусы идут только вперёд: queued → sent → delivered или failed. Не все операторы присылают отчёт о доставке — тогда сообщение остаётся в статусе sent. Если телефон был выключен больше суток, сообщение не отправляется.
Журнал и статус сообщения
GET/messages/{id} — одно сообщение: {"message": {…}}.
GET/messages — журнал, сначала новые. Параметры: direction (out | in), status, phone, since (ISO 8601), before, limit (1–100, по умолчанию 50).
GET /api/v1/messages?direction=in&since=2026-10-01T00:00:00Z&limit=100
{ "messages": [ … ], "next": "2026-10-20T08:15:00.000Z" }
Если next не null, следующая страница — тот же запрос с before=<next>. Журнал хранится 12 месяцев.
Телефоны и аккаунт
GET/devices — подключённые телефоны:
{ "devices": [ { "id": "Zx8kP…", "name": "Samsung Galaxy A54", "online": true, "lastSeen": "2026-10-23T11:58:00Z",
"sims": [ { "sim": 1, "operator": "МТС", "phone": null }, { "sim": 2, "operator": "Билайн", "phone": null } ] } ] }
GET/account — тариф и использование:
{ "email": "info@example.ru", "name": "ООО «Ромашка»",
"plan": { "code": "month", "name": "Безлимит на месяц", "monthlyLimit": null, "devices": 3, "paidUntil": "2026-11-16T09:00:00Z" },
"usage": { "month": 1250, "remaining": null }, "phoneConnected": true }
Вебхуки
Вебхук — адрес вашей системы, куда «Трубка» отправляет события запросом POST с JSON. Добавляется в кабинете (раздел «API») или через API:
GET/webhooks · POST/webhooks · DELETE/webhooks/{id} · POST/webhooks/{id}/test
POST /api/v1/webhooks
{ "url": "https://crm.example.ru/trubka/webhook", "events": ["message.received", "message.status"] }
201 Created
{ "webhook": { "id": "12", "url": "https://crm.example.ru/trubka/webhook", "events": [ … ], "active": true,
"secret": "whsec_…" } }
Секрет нужен для проверки подписи; через API он возвращается только при создании, в кабинете — всегда. Адрес — только https:// и доступный из интернета. До 5 вебхуков на аккаунт.
События
| event | Когда |
|---|---|
message.received | Клиент прислал SMS на номер подключённого телефона |
message.status | Статус исходящего изменился: sent, delivered, failed, rejected — для SMS из API, Битрикс24 и кабинета |
webhook.test | Проверка из кабинета или POST /webhooks/{id}/test |
POST https://crm.example.ru/trubka/webhook
Content-Type: application/json
X-Trubka-Event: message.received
X-Trubka-Delivery: 3f1e0c2a-…
X-Trubka-Signature: t=1761220800,v1=5d2c…
{
"id": "3f1e0c2a-…",
"event": "message.received",
"createdAt": "2026-10-23T12:00:00.000Z",
"data": {
"message": { "id": "…", "direction": "in", "phone": "+79001234567", "text": "Когда доставка?",
"status": "received", "sim": 1, "deviceId": "Zx8kP…", "createdAt": "…", … }
}
}
Ответьте кодом 2xx в течение 10 секунд. Иначе «Трубка» повторит запрос через 1, 5, 15 минут, 1, 3, 6, 12 и 24 часа. Одно событие может прийти дважды (например, если ответ потерялся в сети) — сверяйте id события. Журнал доставок — в кабинете, раздел «API».
Проверка подписи
Заголовок X-Trubka-Signature: t=<unix-время>,v1=<подпись>, где подпись — HMAC-SHA256 в hex от строки <t>.<тело запроса> на секрете вебхука. Проверяйте по исходному телу (до разбора JSON) и отклоняйте запросы старше 5 минут.
PHP
$body = file_get_contents('php://input');
preg_match('/t=(\d+),v1=([0-9a-f]+)/', $_SERVER['HTTP_X_TRUBKA_SIGNATURE'] ?? '', $m);
$ok = $m && abs(time() - (int)$m[1]) < 300
&& hash_equals(hash_hmac('sha256', $m[1] . '.' . $body, $secret), $m[2]);
if (!$ok) { http_response_code(401); exit; }
$event = json_decode($body, true);
Python
import hmac, hashlib, re, time
def verify(body: bytes, header: str, secret: str) -> bool:
m = re.match(r"t=(\d+),v1=([0-9a-f]+)", header or "")
if not m or abs(time.time() - int(m[1])) > 300:
return False
expected = hmac.new(secret.encode(), m[1].encode() + b"." + body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, m[2])
Node.js
import crypto from 'node:crypto';
export function verify(rawBody, header, secret) {
const m = /t=(\d+),v1=([0-9a-f]+)/.exec(header || '');
if (!m || Math.abs(Date.now() / 1000 - Number(m[1])) > 300) return false;
const expected = crypto.createHmac('sha256', secret).update(`${m[1]}.${rawBody}`).digest('hex');
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(m[2]));
}
Советы
- Всегда передавайте
clientRef— тогда повтор запроса после обрыва сети не отправит клиенту второе SMS. - Для массовых уведомлений включите паузу между SMS и лимит в кабинете: операторы ограничивают SIM-карты за непрерывный поток. Сервис не предназначен для рекламных рассылок без согласия получателей.
- Статус узнавайте вебхуком
message.status; если вебхук недоступен — запрашивайтеGET /messages/{id}не чаще раза в минуту. - Версия API — в адресе (
/v1). Новые поля могут добавляться в ответы без смены версии — не считайте их ошибкой.