Skip to content

API Reference

Обзор

API BotBell позволяет вашим программам и скриптам отправлять push-уведомления на устройства Apple и получать ответы. SDK не требуется — только простые HTTP-запросы.

Base URL: https://api.botbell.app

Как это работает

  1. Создайте бота в приложении BotBell и скопируйте его URL для push-сообщений
  2. Отправьте JSON-сообщение POST-запросом на URL для push-сообщений из любого языка или инструмента
  3. Получите мгновенное push-уведомление на всех ваших устройствах
  4. При необходимости получайте ваши ответы через URL обратного вызова или API опроса

Формат ответа

Все ответы API используют единую JSON-обёртку. Код 0 означает успех; любой другой код указывает на ошибку.

// Success
{
  "code": 0,
  "message": "success",
  "data": { ... }
}

// Error
{
  "code": 40001,
  "message": "Invalid bot token"
}

Отправить сообщение

Отправьте уведомление от вашего бота в приложение BotBell. URL для push-сообщений вашего бота уже содержит токен аутентификации — просто отправьте POST-запрос.

Конечная точка

POST https://api.botbell.app/v1/push/<your_bot_token>

Заголовок Authorization не требуется. Токен в URL является вашей аутентификацией.

Также вы можете использовать POST /v1/messages/push с заголовком X-Bot-Token: <token>.

Тело запроса (JSON)

ПолеТипОбязательноОписание
messagestringДаТекст сообщения (макс. 4 096 символов)
titlestringНетЗаголовок, отображаемый над сообщением (макс. 256 символов)
urlstringНетНажимаемая ссылка, прикреплённая к уведомлению
imageUrlstringНетURL изображения для отображения в уведомлении
actionsarrayНетQuick reply buttons (max 5). See Actions below.

Action Fields

ПолеТипОбязательноОписание
keystringДаAction identifier returned when user taps (max 64 chars)
labelstringДаButton text displayed to user (max 64 chars)
typestringНет"button" (default) sends label as reply; "input" opens a text field for custom input
placeholderstringНетPlaceholder text for input field (max 128 chars, only for type "input")
Actions Example
{
  "message": "Deploy v2.3.0 to production?",
  "title": "Deploy Request",
  "actions": [
    {"key": "approve", "label": "Approve"},
    {"key": "reject", "label": "Reject"},
    {"key": "custom", "label": "Other...", "type": "input", "placeholder": "Enter your reason"}
  ]
}

Пример запроса

curl
curl -X POST https://api.botbell.app/v1/push/bt_your_token \
  -H "Content-Type: application/json" \
  -d '{
    "message": "Server CPU is at 95%!",
    "title": "Alert",
    "url": "https://grafana.example.com/dashboard"
  }'

Ответ

{
  "code": 0,
  "message": "success",
  "data": {
    "message_id": "msg_abc123",
    "delivered": true,
    "timestamp": 1709827200
  }
}
ПолеОписание
message_idУникальный идентификатор этого сообщения
deliveredtrue, если push-уведомление было успешно отправлено в APNs; false, если нет зарегистрированного устройства
timestampUnix-метка времени (секунды) создания сообщения

Попробовать сейчас

Вставьте токен вашего бота и отправьте реальное push-уведомление на ваше устройство.


Получение ответов

Когда вы отвечаете боту в приложении, BotBell может автоматически переслать ваш ответ на ваш сервер. Это опционально — вам это нужно только если ваш бот поддерживает двустороннюю переписку.

Настройка URL обратного вызова

В приложении BotBell перейдите в настройки бота и введите URL обратного вызова (например, https://your-server.com/botbell/reply). BotBell будет отправлять POST-запросы с вашими ответами на этот URL по мере их поступления.

Полезная нагрузка обратного вызова

Когда вы отвечаете, BotBell отправляет POST-запрос на ваш URL обратного вызова в следующем формате:

POST https://your-server.com/botbell/reply
Headers:
  Content-Type: application/json
  X-Webhook-Signature: sha256=abc123...
  X-Webhook-Timestamp: 1709827300

Body:
{
  "event": "user_reply",
  "bot_id": "bot_abc123",
  "message_id": "msg_xyz789",
  "content": "Got it, thanks!",
  "timestamp": 1709827300,
  "action": "approve"
}

Проверка подписи

Каждый обратный вызов включает подпись HMAC-SHA256, чтобы вы могли убедиться, что он пришёл от BotBell. Ключом подписи является секрет Webhook, указанный в настройках вашего бота.

signature = HMAC-SHA256(
  key:     <your_webhook_secret>,
  message: "<timestamp>.<raw_request_body>"
)

Для проверки подписи:

  1. Извлеките заголовки X-Webhook-Timestamp и X-Webhook-Signature
  2. Убедитесь, что временная метка не старше 5 минут (для защиты от атак повторного воспроизведения)
  3. Вычислите HMAC-SHA256 от строки "<timestamp>.<raw_body>", используя секрет Webhook в качестве ключа
  4. Сравните вычисленное значение с подписью после префикса "sha256="

Ответ вашего сервера

Верните любой HTTP-код 2xx для подтверждения получения. Ответы с кодом, отличным от 2xx, или тайм-ауты (5 секунд) считаются ошибками — ответ будет сохранён в очереди опроса на 24 часа, чтобы вы могли получить его позже.


Опрос ответов

Если у вас нет публичного сервера для приёма обратных вызовов, вы можете получать ответы через опрос. Это также полезно в качестве запасного варианта, когда ваш URL обратного вызова временно недоступен.

Конечная точка

GET https://api.botbell.app/v1/messages/poll
Headers:
  X-Bot-Token: <your_bot_token>

Параметры запроса

ПолеТипПо умолчаниюОписание
sinceinteger-Возвращать только сообщения после указанной Unix-метки времени
limitinteger20Количество возвращаемых сообщений (макс. 100)

Ответ

{
  "code": 0,
  "message": "success",
  "data": {
    "messages": [
      {
        "message_id": "msg_xyz789",
        "content": "Got it, thanks!",
        "timestamp": 1709827300,
        "action": "approve"
      }
    ],
    "has_more": false
  }
}

Примечание: Опрошенные сообщения помечаются как прочитанные и не будут возвращены повторно. Неопрошенные сообщения истекают через 24 часа.


Коды ошибок

КодHTTP-статусОписание
40001401Неверный или отсутствующий токен бота
40010400Ошибка валидации запроса (например, отсутствует поле message, тело слишком большое)
40029429Превышен лимит запросов — слишком много запросов в минуту
40030429Исчерпана месячная квота сообщений
40031403Достигнут лимит ботов для вашего тарифа
50000500Внутренняя ошибка сервера

Ограничения и квоты

ОграничениеЗначение
Частота push (на бота)60
Тело сообщения (символы)4,096
Заголовок (символы)256
Бесплатная месячная квота300 сообщений / аккаунт
Хранение сообщений опроса24h
Тайм-аут URL обратного вызова5s

Статус ограничений возвращается в заголовках ответа:

X-RateLimit-Limit: 60
X-RateLimit-Remaining: 58
X-RateLimit-Reset: 1709827260

Примеры кода

Отправка сообщений

curl
curl -X POST https://api.botbell.app/v1/push/bt_your_token \
  -H "Content-Type: application/json" \
  -d '{"message":"Server CPU at 95%","title":"Alert"}'
Python
import requests

resp = requests.post(
    "https://api.botbell.app/v1/push/bt_your_token",
    json={
        "message": "Build #42 succeeded",
        "title": "CI/CD",
        "url": "https://ci.example.com/builds/42",
    },
)
print(resp.json())  # {"code": 0, "data": {"message_id": "...", ...}}
JavaScript / Node.js
const resp = await fetch("https://api.botbell.app/v1/push/bt_your_token", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    message: "New order received!",
    title: "E-Commerce",
  }),
});
const data = await resp.json();
console.log(data);
Go
package main

import (
    "bytes"
    "encoding/json"
    "net/http"
)

func main() {
    body, _ := json.Marshal(map[string]string{
        "message": "Disk usage above 90%",
        "title":   "Alert",
    })
    http.Post(
        "https://api.botbell.app/v1/push/bt_your_token",
        "application/json",
        bytes.NewReader(body),
    )
}
PHP
<?php
$ch = curl_init('https://api.botbell.app/v1/push/bt_your_token');
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
    CURLOPT_POSTFIELDS => json_encode([
        'message' => 'Payment received: $49.99',
        'title' => 'Stripe',
    ]),
    CURLOPT_RETURNTRANSFER => true,
]);
$response = curl_exec($ch);
curl_close($ch);

Проверка подписи ответов

Python
import hmac, hashlib

def verify_signature(payload: bytes, timestamp: str, signature: str, secret: str) -> bool:
    message = f"{timestamp}.{payload.decode()}"
    expected = hmac.new(secret.encode(), message.encode(), hashlib.sha256).hexdigest()
    return hmac.compare_digest(f"sha256={expected}", signature)
Node.js
const crypto = require("crypto");

function verifySignature(payload, timestamp, signature, secret) {
  const message = `${timestamp}.${payload}`;
  const expected = crypto.createHmac("sha256", secret).update(message).digest("hex");
  return crypto.timingSafeEqual(
    Buffer.from(`sha256=${expected}`),
    Buffer.from(signature),
  );
}