SMeetBot API

SMeet Bot API

Справочник API

Справочник после первых разделов собран из машиночитаемого контракта, который можно скачать как openapi.json. Имена методов, поля JSON и коды ошибок одинаковы во всех языках этой документации.

Запросы#

  • Базовый адрес: https://messenger.scrile.com/bot-api/v1. Метод вызывается как POST https://messenger.scrile.com/bot-api/v1/<method> с телом в JSON в кодировке UTF-8 и заголовком Content-Type: application/json.
  • Методы только для чтения (getMe, getMyCommands, getFile, getDeliveryInfo, getWebhookInfo) принимают и GET с параметрами в строке запроса.
  • uploadFile принимает multipart/form-data. Содержимое файла скачивается через GET /files/<file_id>/content.
  • Тело запроса может быть не больше 21 МиБ; более крупное отклоняется с 413 PAYLOAD_TOO_LARGE и limit в байтах ещё до чтения.
  • Адрес, которому не соответствует ни один метод, отвечает 404 METHOD_NOT_FOUND.

Аутентификация#

Каждый вызов передаёт токен бота в заголовке:

HTTP
POST /bot-api/v1/getUpdates HTTP/1.1
Host: messenger.scrile.com
Authorization: Bearer sbt1_EXAMPLE_REPLACE_WITH_YOUR_TOKEN
Content-Type: application/json

Отсутствующий, испорченный, неизвестный или отозванный токен, а также токен удалённого бота получают один и тот же ответ: 401 INVALID_TOKEN с заголовком WWW-Authenticate: Bearer. Запрос, который уже был в пути, когда бота удалили, может получить вместо этого 404 BOT_NOT_FOUND; после этого токен тоже не работает. Токен никогда не принимается в URL, и SMeet никуда его не передаёт, в том числе в ваш webhook.

Ответы и ошибки#

Любой ответ приходит в JSON. Успешный:

JSON
{"ok": true, "result": {"message_id": "7301", "chat": {"id": "550", "type": "private"}}}

Ошибка приходит с соответствующим HTTP-статусом и в таком виде:

JSON
{
  "ok": false,
  "error": {"code": "RATE_LIMITED", "message": "Too many requests; wait retry_after seconds", "retry_after": 2},
  "request_id": "3f1c2a7e-8b1d-4c52-9a55-0c7e1f7a1b90"
}
  • error.code стабилен, и именно его проверяет программа. error.message содержит английский текст для людей; не разбирайте его программно.
  • retry_after (в секундах) есть, только когда повтор имеет смысл, и тогда то же значение приходит в заголовке Retry-After. SMeet передаёт его с каждым 429 и 503, а также с каждым 409 CONSUMER_CONFLICT и 409 FILE_NOT_READY.
  • Некоторые коды добавляют поля в error: field называет неверный параметр при INVALID_REQUEST, confirmed_offset и max_issued_update_id приходят с ошибками offset, reason с FILE_REJECTED, limit (наибольший допустимый размер в байтах) с PAYLOAD_TOO_LARGE.
  • request_id, который приходит и в заголовке X-Request-Id, указывает на конкретный запрос. Сообщайте его при обращении в поддержку.
  • Прокси перед API отвечает на собственные ограничения в том же виде: 429 RATE_LIMITED с retry_after 1 и 413 PAYLOAD_TOO_LARGE, оба с пустым request_id. 413 от прокси может прийти без limit.

Все коды с HTTP-статусами собраны в таблице кодов ошибок.

Идентификаторы#

Все идентификаторы (update_id, chat_id, message_id, epoch, offset) передаются десятичными строками длиной до 20 цифр. Они могут превышать 2^53, поэтому никогда не превращайте их в числа с плавающей точкой; арифметика над ними тоже не нужна, потому что сервер возвращает next_offset с каждым batch. file_id и id нажатия кнопки остаются непрозрачными строками, а event_id является UUID.

Idempotency-Key#

Методы, которые что-то меняют, принимают заголовок Idempotency-Key: от 1 до 128 символов из набора A-Z a-z 0-9 . _ : -.

МетодIdempotency-Key
sendMessage, editMessageText, sendPhoto, sendDocument, uploadFile, rejectUpdateОбязателен. Без него: 400 IDEMPOTENCY_KEY_REQUIRED.
answerCallbackQuery, setMyCommandsПринимается, но не нужен: оба метода идемпотентны сами по себе (на нажатие отвечают один раз, а список команд заменяет прежний), поэтому ключ не сохраняется.
  • Тот же ключ и те же параметры: возвращается результат первого вызова, и ничего не выполняется дважды. Перед ответом SMeet заново проверяет токен и чат, поэтому повтор после того, как пользователь остановил бота, получает BOT_STOPPED_OR_BLOCKED.
  • Тот же ключ и другие параметры: 409 IDEMPOTENCY_CONFLICT. Параметры сравниваются как JSON, так что порядок полей и пробелы не важны.
  • Ошибка до выполнения действия (FILE_NOT_READY, 429, 503, ошибка проверки параметров) ключ не расходует. Повторите тот же запрос с тем же ключом.
  • Повтор, который пришёл, пока первый вызов ещё выполняется, дожидается его и возвращает его результат.
  • Ключи принадлежат одному боту и одному методу: один и тот же ключ в sendMessage и в editMessageText считается двумя разными ключами. Ключи uploadFile уникальны в пределах бота. SMeet хранит ключи 7 дней.

Стройте ключ из события, на которое отвечаете, и действия, например <event_id>:reply: тогда повторно доставленное или повторённое владельцем событие даёт тот же ключ, и SMeet возвращает уже отправленный ответ. Ваши собственные побочные эффекты (запись в базе, платёж) требуют такой же защиты на вашей стороне по event_id (Дедупликация).

Ограничения#

Запросы сверх ограничения получают 429 RATE_LIMITED с retry_after. Короткий всплеск сверх темпа допускается до числа в последнем столбце.

Что учитываетсяТемпВсплеск
Сообщения в один чат: sendMessage, editMessageText, sendPhoto, sendDocument1 в секунду3
Те же методы на бота10 в секунду20
Те же методы для всех ботов одного владельца вместе20 в секунду40
getUpdates на бота5 в секунду10
uploadFile на бота2 в секунду5
Скачивание файлов на бота5 в секунду10
Все запросы с одного IP-адреса20 в секунду40

Другие ограничения: текст до 4096 символов и кнопки, как в разделе Сообщения и кнопки; фото до 10 МиБ, документы до 20 МиБ и 200 МиБ загрузок на бота за сутки UTC (Фото и документы); очередь до 10 000 событий или 50 МиБ на бота (Получение событий). Платформа может задать другие ограничения отдельному боту и другой общий темп для всех ботов одного владельца.

Версии и совместимость#

  • Основная версия входит в путь: /bot-api/v1. Изменение, которое может сломать работающую программу, получает новый путь; v1 продолжает работать.
  • Внутри v1 SMeet добавляет новое: поля в объектах, типы событий, значения reason и status, коды ошибок. Программа должна пропускать незнакомые поля и типы событий, а незнакомые значения считать «прочими».
  • Каждое изменение контракта записывается в истории изменений API.
  • Сам контракт опубликован как openapi.json (OpenAPI 3.1); по нему можно сгенерировать клиент или проверять им свои тесты.

Справочник собран из контракта SMeet Bot API 1.0.0 (OpenAPI 3.1.0). Базовый адрес: https://messenger.scrile.com/bot-api/v1. Машиночитаемый файл: openapi.json.

HTTP API для программ, которые работают как боты SMeet.

Методы#

Каждый метод вызывается как POST https://messenger.scrile.com/bot-api/v1/<method> с телом в JSON; методы только для чтения принимают и GET с параметрами в строке запроса. Аутентификация: Токен бота, который один раз показывается в «Моих ботах» или в SMeet BotFather. Формат sbt1_<key id>_<secret>; считайте его непрозрачной строкой. SMeet хранит только хэш. Отозванный токен сразу на всех серверах получает 401 INVALID_TOKEN.

МетодHTTPIdempotency-KeyОписание
BotПрофиль бота и его команды.
getMeGET POSTПроверить токен и получить публичный профиль бота.
getMyCommandsGET POSTПрочитать меню команд.
setMyCommandsPOSTможноЗаменить меню команд, которое пользователь видит после ввода «/».
UpdatesПолучение событий через long polling.
getUpdatesPOSTПолучить ожидающие события через long polling.
rejectUpdatePOSTобязателенПеревести одно выданное и неподтверждённое событие в FAILED.
MessagesОтправка и редактирование сообщений, ответы на нажатия кнопок.
sendMessagePOSTобязателенОтправить текстовое сообщение в чат, где пользователь запустил этого бота.
editMessageTextPOSTобязателенИзменить текст и кнопки сообщения, которое отправил этот бот.
answerCallbackQueryPOSTможноПодтвердить, что нажатие кнопки обработано.
FilesФото и документы.
uploadFilePOSTобязателенЗагрузить фото или документ, чтобы отправить его позже.
sendPhotoPOSTобязателенОтправить готовое фото в чат.
sendDocumentPOSTобязателенОтправить готовый документ в чат.
getFileGET POSTПрочитать сведения о файле и состояние его обработки.
downloadFileGETСкачать содержимое готового файла.
DeliveryДиагностика доставки, только чтение.
getDeliveryInfoGET POSTПрочитать режим доставки, размер очередей и последнюю ошибку.
getWebhookInfoGET POSTПрочитать состояние webhook (часть getDeliveryInfo).

getMe#

GET POST /getMeBot

Проверить токен и получить публичный профиль бота.

Результат#

В поле result приходит Bot. Бот.

При ошибке приходит объект ошибки с кодом из таблицы кодов ошибок.

getMyCommands#

GET POST /getMyCommandsBot

Прочитать меню команд.

Параметры#

ИмяТипОбязательно
language_codeLanguageCodeНет

С GET передавайте их в строке запроса, с POST как поля тела в JSON.

Результат#

В поле result приходит массив BotCommand. Команды.

При ошибке приходит объект ошибки с кодом из таблицы кодов ошибок.

setMyCommands#

POST /setMyCommandsBot

Заменить меню команд, которое пользователь видит после ввода «/».

Заголовки#

ЗаголовокТипОбязательноОписание
Idempotency-KeyIdempotencyKeyНетПринимается, но не нужен: метод идемпотентен сам по себе (повтор того же вызова даёт тот же результат), поэтому ключ не сохраняется.

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

ПолеТипОбязательноОписание
commandsмассив BotCommandДаДо 100 элементов.
language_codeLanguageCodeНетНе передавайте для списка по умолчанию, который видят все языки.

Результат#

В поле result приходит true. Сохранено.

При ошибке приходит объект ошибки с кодом из таблицы кодов ошибок.

getUpdates#

POST /getUpdatesUpdates

Получить ожидающие события через long polling.

Возвращает ожидающие события начиная с подтверждённой позиции. Переданный offset подтверждает все события с update_id < offset; подтверждённые события больше не возвращаются. Неподтверждённые события возвращаются снова при следующем вызове (после потерянного ответа или перезапуска), поэтому программа должна спокойно переносить повторы.

Запрос ждёт до timeout секунд и завершается сразу, как только появляется событие, а не в конце тайм-аута. Пустой ответ по тайм-ауту ничего не меняет.

У бота один активный получатель. Первый вызов называет consumer_id и получает аренду с epoch; следующие вызовы передают те же consumer_id и epoch и продлевают аренду (TTL 60 секунд). Второй одновременный вызов, даже с тем же consumer_id, получает CONSUMER_CONFLICT. Вызов с чужой epoch (аренду с тех пор взял другой получатель, владелец её сбросил или сменил режим) получает новую аренду, но ничего не подтверждает: его offset не учитывается, и неподтверждённые события приходят снова. Если истекла ваша собственная аренда и за это время её никто не взял, следующий вызов получает новую аренду, и его offset по-прежнему подтверждает события.

Правила offset (confirmed_offset означает позицию после подтверждённого префикса, max_issued_update_id означает наибольший когда-либо выданный update_id):

  • offset = confirmed_offset принимается всегда (подтверждение без изменений).
  • confirmed_offset < offset <= max_issued_update_id + 1 подтверждает префикс.
  • offset < confirmed_offset получает 409 CURSOR_BEHIND; в ошибке есть текущий confirmed_offset. Ничего не отправляется повторно.
  • offset > max_issued_update_id + 1 получает 400 OFFSET_NOT_ISSUED, если только он не равен next_offset, который сервер выдал для текущей аренды.
  • Без offset выдача продолжается с позиции, сохранённой на сервере.

Пример: после выдачи события 42 offset=43 подтверждает его; offset=44 отклоняется; когда 43 уже подтверждён, offset=42 получает CURSOR_BEHIND.

События, которые были отменены, истекли или ушли в FAILED до подтверждения, перечисляются в skipped, и next_offset перепрыгивает через них. Подтверждайте всегда полученным next_offset, после того как весь batch надёжно сохранён.

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

ПолеТипОбязательноОписание
offsetIdНет
limitintegerНетОт 1 до 100. По умолчанию: 100.
timeoutintegerНетСколько секунд ждать, если ничего нет. 0 возвращает ответ сразу. От 0 до 25. По умолчанию: 25.
consumer_idstringДаУстойчивое имя этого процесса-получателя, например имя хоста. От 1 до 64 символов. Шаблон: ^[A-Za-z0-9._-]+$.
epochIdНетEpoch аренды, полученной раньше. Храните её вместе с offset и передавайте и после перезапуска; не передавайте, только если её нет. Без неё аренда, которую этот получатель ещё держит, отвечает 409 CONSUMER_CONFLICT до своего истечения (retry_after говорит, сколько ждать); затем повторите запрос.

Результат#

В поле result приходит GetUpdatesResult. Пачка событий (возможно, пустая).

При ошибке приходит объект ошибки с кодом из таблицы кодов ошибок.

rejectUpdate#

POST /rejectUpdateUpdates

Перевести одно выданное и неподтверждённое событие в FAILED.

Нужен, когда программа не может обработать одно конкретное событие и не хочет, чтобы оно задерживало остальной batch. Событие выходит из активной последовательности, хранит данные до конца исходного срока хранения и появляется в списке FAILED у владельца, где его можно повторить или пропустить. Отклонить можно только событие, которое уже выдано текущей аренде и ещё не подтверждено.

Заголовки#

ЗаголовокТипОбязательноОписание
Idempotency-KeyIdempotencyKeyДаУникален для каждой логической операции (подойдёт UUID). При повторе после тайм-аута, 429 или 503 передавайте тот же ключ. Привязывайте его к event_id события, на которое отвечаете, и к действию, чтобы повторно доставленное событие не дало второго ответа.

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

ПолеТипОбязательноОписание
update_idIdДа
reasonstringДаВидна владельцу. Не должна содержать секретов и персональных данных. От 1 до 256 символов.
consumer_idstringДа
epochIdДа

Результат#

Событие в статусе FAILED. В поле result приходит объект с такими полями.

ПолеТипОбязательноОписание
update_idIdДа
statusstringДаВсегда failed.

При ошибке приходит объект ошибки с кодом из таблицы кодов ошибок.

sendMessage#

POST /sendMessageMessages

Отправить текстовое сообщение в чат, где пользователь запустил этого бота.

Отправитель всегда бот, которому принадлежит токен; параметра sender_id нет. Чат должен быть личным: пользователь нажал в нём «Начать» и не остановил и не заблокировал бота.

Заголовки#

ЗаголовокТипОбязательноОписание
Idempotency-KeyIdempotencyKeyДаУникален для каждой логической операции (подойдёт UUID). При повторе после тайм-аута, 429 или 503 передавайте тот же ключ. Привязывайте его к event_id события, на которое отвечаете, и к действию, чтобы повторно доставленное событие не дало второго ответа.

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

ПолеТипОбязательноОписание
chat_idIdДа
textstringДаОбычный текст. Строки, которые начинаются с "__META__:" или "__SYSTEM__", отклоняются. От 1 до 4096 символов.
reply_to_message_idIdНетСообщение из этого же чата.
reply_markupInlineKeyboardMarkupНет

Результат#

В поле result приходит Message. Отправленное сообщение.

При ошибке приходит объект ошибки с кодом из таблицы кодов ошибок.

editMessageText#

POST /editMessageTextMessages

Изменить текст и кнопки сообщения, которое отправил этот бот.

Изменять можно только сообщения этого бота. Если reply_markup не передан, после правки у сообщения не будет кнопок.

Заголовки#

ЗаголовокТипОбязательноОписание
Idempotency-KeyIdempotencyKeyДаУникален для каждой логической операции (подойдёт UUID). При повторе после тайм-аута, 429 или 503 передавайте тот же ключ. Привязывайте его к event_id события, на которое отвечаете, и к действию, чтобы повторно доставленное событие не дало второго ответа.

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

ПолеТипОбязательноОписание
chat_idIdДа
message_idIdДа
textstringДаОт 1 до 4096 символов.
reply_markupInlineKeyboardMarkupНет

Результат#

В поле result приходит Message. Изменённое сообщение.

При ошибке приходит объект ошибки с кодом из таблицы кодов ошибок.

answerCallbackQuery#

POST /answerCallbackQueryMessages

Подтвердить, что нажатие кнопки обработано.

Убирает у пользователя индикатор ожидания и при желании показывает короткое уведомление. Если на нажатие не ответить за 15 секунд, пользователь увидит «Бот не ответил»; бот всё равно может ответить позже и изменить своё сообщение. На нажатие можно ответить один раз и в течение 1 часа; повтор такого же ответа возвращает тот же результат, а другой второй ответ получает 409 IDEMPOTENCY_CONFLICT.

Заголовки#

ЗаголовокТипОбязательноОписание
Idempotency-KeyIdempotencyKeyНетПринимается, но не нужен: метод идемпотентен сам по себе (повтор того же вызова даёт тот же результат), поэтому ключ не сохраняется.

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

ПолеТипОбязательноОписание
callback_query_idstringДа
textstringНетКороткое уведомление для пользователя. До 200 символов.
show_alertbooleanНетПоказать уведомление диалогом, а не всплывающей подсказкой. По умолчанию: false.

Результат#

В поле result приходит true. Ответ принят.

При ошибке приходит объект ошибки с кодом из таблицы кодов ошибок.

uploadFile#

POST /uploadFileFiles

Загрузить фото или документ, чтобы отправить его позже.

Файл сохраняется в карантин и проверяется. Возвращённый file_id имеет статус scanning; отправить файл можно только после того, как getFile сообщит ready. Ограничения: фото до 10 МиБ (JPEG, PNG, WebP, HEIC), документы до 20 МиБ и суточная квота объёма на бота. Один и тот же Idempotency-Key никогда не создаёт второй файл.

Заголовки#

ЗаголовокТипОбязательноОписание
Idempotency-KeyIdempotencyKeyДаУникален для каждой логической операции (подойдёт UUID). При повторе после тайм-аута, 429 или 503 передавайте тот же ключ. Привязывайте его к event_id события, на которое отвечаете, и к действию, чтобы повторно доставленное событие не дало второго ответа.

Тело запроса (multipart/form-data)#

ПолеТипОбязательноОписание
fileфайлДа
kindstringНетОдно из значений: photo, document. По умолчанию: document.
file_namestringНетДо 255 символов.

Результат#

В поле result приходит File. Сохранённый файл (обычно в статусе scanning).

При ошибке приходит объект ошибки с кодом из таблицы кодов ошибок.

sendPhoto#

POST /sendPhotoFiles

Отправить готовое фото в чат.

Файл должен иметь вид photo (JPEG, PNG, WebP или HEIC по содержимому). Любой другой готовый файл получает 400 INVALID_REQUEST с field: file_id; отправьте его через sendDocument.

Заголовки#

ЗаголовокТипОбязательноОписание
Idempotency-KeyIdempotencyKeyДаУникален для каждой логической операции (подойдёт UUID). При повторе после тайм-аута, 429 или 503 передавайте тот же ключ. Привязывайте его к event_id события, на которое отвечаете, и к действию, чтобы повторно доставленное событие не дало второго ответа.

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

ПолеТипОбязательноОписание
chat_idIdДа
file_idFileIdДаФайл, который загрузил этот бот, или вложение сообщения, которое этот бот получил в том же пространстве. Должен быть в статусе ready: иначе FILE_NOT_READY (повторите позже с тем же Idempotency-Key) или FILE_REJECTED.
captionstringНетДо 1024 символов.
reply_to_message_idIdНет
reply_markupInlineKeyboardMarkupНет

Результат#

В поле result приходит Message. Отправленное сообщение.

При ошибке приходит объект ошибки с кодом из таблицы кодов ошибок.

sendDocument#

POST /sendDocumentFiles

Отправить готовый документ в чат.

Заголовки#

ЗаголовокТипОбязательноОписание
Idempotency-KeyIdempotencyKeyДаУникален для каждой логической операции (подойдёт UUID). При повторе после тайм-аута, 429 или 503 передавайте тот же ключ. Привязывайте его к event_id события, на которое отвечаете, и к действию, чтобы повторно доставленное событие не дало второго ответа.

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

ПолеТипОбязательноОписание
chat_idIdДа
file_idFileIdДаФайл, который загрузил этот бот, или вложение сообщения, которое этот бот получил в том же пространстве. Должен быть в статусе ready: иначе FILE_NOT_READY (повторите позже с тем же Idempotency-Key) или FILE_REJECTED.
captionstringНетДо 1024 символов.
reply_to_message_idIdНет
reply_markupInlineKeyboardMarkupНет

Результат#

В поле result приходит Message. Отправленное сообщение.

При ошибке приходит объект ошибки с кодом из таблицы кодов ошибок.

getFile#

GET POST /getFileFiles

Прочитать сведения о файле и состояние его обработки.

Работает для файлов, которые загрузил бот, и для вложений полученных им сообщений. Поле download_path есть, только пока файл в статусе ready и доступ к нему сохраняется. Для scanning в ответе есть retry_after.

Параметры#

ИмяТипОбязательно
file_idFileIdДа

С GET передавайте их в строке запроса, с POST как поля тела в JSON.

Результат#

В поле result приходит File. Файл.

При ошибке приходит объект ошибки с кодом из таблицы кодов ошибок.

downloadFile#

GET /files/{file_id}/contentFiles

Скачать содержимое готового файла.

При каждом вызове нужны токен бота и действующие права. Публичного или постоянного адреса в хранилище нет. Доступ заканчивается, когда исходное сообщение удалено, пользователь остановил или заблокировал бота либо вышел из пространства.

Параметры#

ИмяТипОбязательно
file_idFileIdДа

Параметры пути входят в сам адрес.

Результат#

Ответом служит сам файл, а не JSON. Содержимое файла. Заголовки ответа: Content-Disposition.

При ошибке приходит объект ошибки с кодом из таблицы кодов ошибок.

getDeliveryInfo#

GET POST /getDeliveryInfoDelivery

Прочитать режим доставки, размер очередей и последнюю ошибку.

Секрет webhook никогда не возвращается.

Результат#

В поле result приходит DeliveryInfo. Состояние доставки.

При ошибке приходит объект ошибки с кодом из таблицы кодов ошибок.

getWebhookInfo#

GET POST /getWebhookInfoDelivery

Прочитать состояние webhook (часть getDeliveryInfo).

Результат#

В поле result приходит WebhookInfo. Состояние webhook.

При ошибке приходит объект ошибки с кодом из таблицы кодов ошибок.

Запрос webhook#

POST ваш HTTPS-адрес

SMeet доставляет одно событие на HTTPS-адрес владельца.

Отправляется только в режиме доставки webhook. На бота один запрос в полёте; сетевой тайм-аут 10 секунд; перенаправления не выполняются.

Проверьте X-SMeet-Signature, прежде чем доверять телу: t содержит Unix-время, а v1 содержит HMAC-SHA256 от "<t>.<сырое тело запроса>" в шестнадцатеричном виде строчными буквами, с ключом, равным секрету webhook, который один раз показывается в «Моих ботах». Отклоняйте timestamp, который отличается от ваших часов больше чем на 5 минут. API-токен в webhook не передаётся.

Надёжно сохраните событие (уникальность по event_id) и быстро ответьте любым 2xx; долгую бизнес-логику выполняйте после ответа. 2xx подтверждает приём, а не успех бизнес-операции. Потерянные ответы приводят к повторам с тем же update_id и event_id.

Обработка ответа: 2xx подтверждает. 400, 413 или 422 переводит это событие в FAILED, очередь идёт дальше. 401, 403, 404, 410, сертификат, который не проходит проверку, адрес, куда webhook не отправляются, перенаправление и любой другой неподдерживаемый ответ приостанавливают endpoint, пока владелец не исправит настройку и не возобновит доставку в «Моих ботах» (состояние paused, причина в last_error); в это время ничего не повторяется, попытки событий не расходуются, очередь сохраняется. 429 замедляет доставку с учётом Retry-After. Ошибки DNS и соединения, а также тайм-ауты соединения и TLS-рукопожатия до отправки запроса оставляют событие в ожидании и увеличивают паузу (5 секунд с удвоением до 5 минут). 5xx, 408 и сбои после начала отправки расходуют общий бюджет: 5 попыток или 10 минут на событие, после чего оно уходит в FAILED; три таких события подряд открывают circuit breaker, который перед продолжением делает пробную доставку текущего события.

Заголовки#

ЗаголовокТипОбязательноОписание
X-SMeet-SignaturestringДаПример: t=1790416800,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd
X-SMeet-Event-Idstring (uuid)Да
X-SMeet-Update-IdIdДа
X-SMeet-Delivery-AttemptintegerДаНе меньше 1

Тело#

Объект Update в JSON.

Ваш ответ#

Принято. Любой 2xx обрабатывается одинаково; тело ответа не читается.

Типы событий#

Поле type у Update называет ровно одно поле с данными, которое присутствует в событии. Незнакомые значения нужно пропускать.

typeПоле с даннымиТип данных
messagemessageMessage
message_editededited_messageMessage
message_deleteddeleted_messageDeletedMessage
callback_querycallback_queryCallbackQuery
chat_access_changedchat_accessChatAccess
file_status_changedfile_statusFileStatusChange

Типы#

Простые типы#

Строки с заданным форматом. Поля ссылаются на них по имени.

ИмяТипОписание
IdstringДесятичный идентификатор, передаётся строкой. Не секрет и не разрешение. Шаблон: ^[0-9]{1,20}$. Пример: 1842.
FileIdstringНепрозрачный идентификатор файла, действует только для этого бота. Шаблон: ^[A-Za-z0-9_-]{16,64}$. Пример: f_Qm9vdGZpbGVfMDAwMDAx.
IdempotencyKeystringОт 1 до 128 символов. Шаблон: ^[A-Za-z0-9._:-]+$.
LanguageCodestringШаблон: ^[a-z]{2}$. Пример: en.
Timestampstring (date-time)RFC 3339, UTC. Пример: 2026-09-26T10:00:00Z.
UpdateTypestringНезнакомые значения нужно пропускать. Одно из значений: message, message_edited, message_deleted, callback_query, chat_access_changed, file_status_changed.
FileStatusstringОдно из значений: scanning, ready, rejected, scan_failed.

User#

Только то, что нужно для чата. E-mail, телефон, другие чаты, другие аккаунты и сессии не передаются никогда.

ПолеТипОбязательноОписание
idIdДа
display_namestringДа
usernamestringНетПубличный адрес без «@», если он есть у пользователя.
is_botbooleanДа

Chat#

ПолеТипОбязательноОписание
idIdДа
typestringДаВ версии 1 только личные чаты. Незнакомые значения нужно пропускать. Одно из значений: private.

Space#

ПолеТипОбязательноОписание
idIdДа
kindstringДаОдно из значений: public, organization.
namestringНет

Bot#

ПолеТипОбязательноОписание
idIdДаИдентификатор аккаунта бота; то же значение, что from.id в сообщениях, которые отправляет бот.
usernamestringДа
display_namestringДа
descriptionstringНет
is_botbooleanДаВсегда true.
spaceSpaceДа
statusstringДаОдно из значений: active, paused_by_owner, suspended_by_admin.
delivery_modestringДаОдно из значений: polling, webhook.
can_send_filesbooleanНетfalse, пока работа с файлами выключена в этой установке SMeet.

InlineKeyboardButton#

ПолеТипОбязательноОписание
textstringДаОт 1 до 64 символов.
callback_datastringНетПри нажатии возвращается в callback_query.data. Нужно ровно одно из полей callback_data или url. От 1 до 64 символов.
urlstring (uri)НетСсылка https://, которую открывает клиент. Нужно ровно одно из полей callback_data или url. До 2048 символов.

InlineKeyboardMarkup#

ПолеТипОбязательноОписание
inline_keyboardмассив массив InlineKeyboardButtonДаРяды кнопок; не больше 8 кнопок в ряду и 40 всего. От 1 до 10 элементов.

Attachment#

ПолеТипОбязательноОписание
file_idFileIdДа
kindstringДаОдно из значений: photo, document.
file_namestringДаОчищенное имя, его безопасно показывать и использовать при сохранении.
mime_typestringНетКак его указало приложение отправителя. Тип, определённый по содержимому, есть в getFile после проверки: разметка, программы и другие опасные типы там отклоняются (type_not_allowed), а при остальных расхождениях файл хранится под определённым типом («фото», которое не картинка, становится документом).
sizeintegerДаКак его указало приложение отправителя; в getFile есть размер сохранённой копии. Не меньше 0.
processing_statusFileStatusДа
reasonstringНетТолько если вложение уже в статусе rejected или scan_failed, например too_large для вложения больше размера, который бот может получить. Значения те же, что у File.reason.

Message#

ПолеТипОбязательноОписание
message_idIdДа
chatChatДа
fromUserДа
dateTimestampДа
edit_dateTimestampНет
textstringНетДо 4096 символов.
reply_to_message_idIdНет
attachmentsмассив AttachmentНет
reply_markupInlineKeyboardMarkupНет
has_unsupported_contentbooleanНетtrue, если в сообщении есть содержимое, которое эта версия API не передаёт (голосовое, геопозиция, опрос и подобное). Тогда text содержит читаемую замену, если она есть.

CallbackQuery#

ПолеТипОбязательноОписание
idstringДаПередайте в answerCallbackQuery.
fromUserДа
messageобъектДаСообщение бота, на кнопку которого нажали.
message.message_idIdДа
message.chatChatДа
datastringДа
dateTimestampДа

ChatAccess#

ПолеТипОбязательноОписание
chatChatДа
userUserДа
statusstringДаstarted: пользователь нажал «Начать» (снова). stopped: пользователь остановил бота. blocked: пользователь заблокировал бота. removed: пользователь вышел из пространства бота, и платформа отозвала доступ бота. После любого статуса, кроме started, отправка в этот чат получает BOT_STOPPED_OR_BLOCKED. Одно из значений: started, stopped, blocked, removed.
start_paramstringНетПараметр ссылки ?start=, только для started. Сам по себе он никогда не команда, но Start также добавляет в чат видимое сообщение пользователя /start или /start <param>, которое приходит отдельным событием message сразу после этого. Бот, который приветствует при Start, должен реагировать на одно из двух, а не на оба. До 64 символов. Шаблон: ^[A-Za-z0-9_-]*$.
dateTimestampДа

DeletedMessage#

ПолеТипОбязательно
message_idIdДа
chatChatДа
dateTimestampДа

FileStatusChange#

Отправляется, когда проверка закончена: для вложений, полученных ботом (с message_id и chat), и для собственных загрузок бота (без них). Никогда не отправляется в чат, где пользователь остановил бота или из которого вышел. scan_failed не всегда окончателен: файл, который сканер не смог проверить (scanner_unavailable, scanner_outdated, scan_timeout), проверяется снова примерно через час, всего до трёх раундов, и после этого приходит ещё один file_status_changed; в это время getFile снова показывает scanning.

ПолеТипОбязательноОписание
file_idFileIdДа
statusFileStatusДа
message_idIdНетСообщение, к которому прикреплён файл, для полученных вложений.
chatChatНет
reasonstringНетДля rejected и scan_failed. Значения: malware_detected, type_not_allowed (разметка, программы и другие запрещённые типы), too_large (файл больше лимита отклоняется; файл, который сканер отказался проверять из-за размера, получает scan_failed), empty (нет содержимого), scanner_unavailable, scanner_outdated (сигнатуры старше 3 дней), scan_timeout, source_missing (вложение исчезло до проверки). Незнакомые значения нужно принимать.

Update#

Присутствует ровно одно поле с данными, его выбирает type:

typeполе с данными
messagemessage
message_editededited_message
message_deleteddeleted_message
callback_querycallback_query
chat_access_changedchat_access
file_status_changedfile_status

Правка старого сообщения не новая команда: считайте message_edited исправлением.

ПолеТипОбязательноОписание
update_idIdДа
event_idstring (uuid)Да
typeUpdateTypeДа
replay_of_update_idIdНетЕсть, если владелец повторил событие из FAILED.
dateTimestampДа
messageMessageНет
edited_messageMessageНет
deleted_messageDeletedMessageНет
callback_queryCallbackQueryНет
chat_accessChatAccessНет
file_statusFileStatusChangeНет

Lease#

ПолеТипОбязательно
consumer_idstringДа
epochIdДа
expires_atTimestampДа

SkipRange#

ПолеТипОбязательноОписание
from_update_idIdДа
to_update_idIdДаВключительно.
reasonstringДаcancelled: доступ к чату или источник события пропал до доставки. failed: событие ушло в FAILED (rejectUpdate или бюджет webhook). expired: истёк срок хранения. skipped_by_owner: владелец пропустил событие из FAILED. delivered: событие уже подтверждено другим путём, например через webhook до того, как владелец вернул бота на long polling. Незнакомые значения нужно принимать. Одно из значений: cancelled, failed, expired, skipped_by_owner, delivered.

GetUpdatesResult#

ПолеТипОбязательноОписание
updatesмассив UpdateДа
confirmed_offsetIdДаПозиция после подтверждённого префикса, уже с учётом offset этого запроса.
next_offsetIdДаПередайте как offset после того, как каждое событие этого batch надёжно сохранено.
skippedмассив SkipRangeДа
leaseLeaseДа
queue_gapbooleanНетtrue, если после последнего подтверждения часть событий истекла до доставки.

BotCommand#

ПолеТипОбязательноОписание
commandstringДаБез ведущей косой черты. Шаблон: ^[a-z0-9_]{1,32}$.
descriptionstringДаОт 1 до 256 символов.

DeliveryError#

ПолеТипОбязательноОписание
dateTimestampДа
codestringДаДо отправки запроса: DNS_ERROR, CONNECT_ERROR, CONNECT_TIMEOUT, TLS_ERROR. После отправки: TIMEOUT, NO_RESPONSE, IO_ERROR, HTTP_408, HTTP_5xx (сам статус, например HTTP_503). Пауза до возобновления владельцем: HTTP_401, HTTP_403, HTTP_404, HTTP_410, TLS_CERTIFICATE_ERROR, ADDRESS_FORBIDDEN, REDIRECT_NOT_FOLLOWED и HTTP_<статус> любого другого неподдерживаемого ответа. Кроме того, HTTP_400, HTTP_413, HTTP_422 (событие ушло в FAILED), HTTP_429 (доставка замедлена) и DISABLED_BY_PLATFORM. Незнакомые значения нужно принимать.
messagestringНетОчищено; никогда не содержит секретов или текста сообщений.

DeliveryInfo#

ПолеТипОбязательноОписание
modestringДаОдно из значений: polling, webhook.
config_versionIdДа
pending_update_countintegerДа
failed_update_countintegerДа
oldest_pending_dateTimestampНет
last_delivery_dateTimestampНет
last_errorDeliveryErrorНет
confirmed_offsetIdНетРежим polling.
leaseLeaseНетРежим polling, когда аренду держит получатель.
webhookWebhookInfoНет
maintenancebooleanДаtrue во время технической паузы платформы.
retention_secondsintegerНетСколько хранятся неподтверждённые события (604800 = 7 дней).

WebhookInfo#

ПолеТипОбязательноОписание
urlstringДаПустая строка в режиме polling.
statestringДаpaused: ждёт владельца после ответа, который может исправить только он (см. last_error); владелец возобновляет доставку в «Моих ботах». circuit_open: сбои повторяются, SMeet сам время от времени пробует доставить событие. disabled_by_platform: администратор выключил webhook. Одно из значений: none, active, paused, circuit_open, disabled_by_platform.
pending_update_countintegerДа
next_attempt_dateTimestampНет
last_errorDeliveryErrorНет
max_connectionsintegerНетВсегда 1.

File#

ПолеТипОбязательноОписание
file_idFileIdДа
kindstringДаОдно из значений: photo, document.
file_namestringДа
mime_typestringНет
sizeintegerДа
statusFileStatusДа
reasonstringНетДля rejected и scan_failed. Значения: malware_detected, type_not_allowed (разметка, программы и другие запрещённые типы), too_large (файл больше лимита отклоняется; файл, который сканер отказался проверять из-за размера, получает scan_failed), empty (нет содержимого), scanner_unavailable, scanner_outdated (сигнатуры старше 3 дней), scan_timeout, source_missing (вложение исчезло до проверки). Незнакомые значения нужно принимать.
retry_afterintegerНетДля scanning; через сколько секунд спросить снова.
download_pathstringНетЕсть только у готовых файлов; путь относительно базового адреса API, нужен токен.

Ошибки#

Любая ошибка. Значение каждого кода и HTTP-статус, с которым он приходит, указаны в ErrorCode. Заголовок Retry-After ставится всегда, когда в теле есть retry_after (429, 503, а также 409 CONSUMER_CONFLICT и FILE_NOT_READY).

Заголовки ответа с ошибкой: Retry-After, X-Request-Id.

Объект ошибки#

Любой неудачный вызов отвечает этой обёрткой и соответствующим HTTP-статусом.

ПолеТипОбязательноОписание
okbooleanДаВсегда false.
errorErrorДа
request_idstringДа

Поле error:

ПолеТипОбязательноОписание
codeErrorCodeДа
messagestringДаПонятный человеку текст на английском, без секретов. Не разбирайте его программно.
retry_afterintegerНетСколько секунд подождать перед повтором. Есть, только когда повтор имеет смысл. Не меньше 1.
confirmed_offsetIdНетЕсть при CURSOR_BEHIND и OFFSET_NOT_ISSUED.
max_issued_update_idIdНетЕсть при OFFSET_NOT_ISSUED.
fieldstringНетЕсть при INVALID_REQUEST, если ошибка в одном параметре.
reasonstringНетЕсть при FILE_REJECTED: почему файл нельзя использовать (not_a_photo, type_not_allowed, malware_detected, too_large, empty, scanner_unavailable, ...). Незнакомые значения нужно принимать.
limitintegerНетЕсть при PAYLOAD_TOO_LARGE: наибольший допустимый размер в байтах. Ответ 413, который отдал прокси перед API до того, как запрос дошёл до SMeet, может его не содержать.

Коды ошибок#

КодHTTPЗначение
INVALID_REQUEST400Неверный текст, кнопки или параметры
IDEMPOTENCY_KEY_REQUIRED400Методу нужен заголовок Idempotency-Key
OFFSET_NOT_ISSUED400offset перепрыгивает через события, которые не выдавались
INVALID_TOKEN401Неизвестный или отозванный токен
BOT_STOPPED_OR_BLOCKED403Пользователь остановил или заблокировал бота
BOT_SUSPENDED403Бот приостановлен владельцем или платформой
SPACE_BOTS_DISABLED403Организация выключила работу ботов
CHAT_NOT_FOUND404У этого бота нет такого чата (чужие чаты не раскрываются)
MESSAGE_NOT_FOUND404У этого бота нет такого сообщения
UPDATE_NOT_FOUND404Событие не выдавалось, уже подтверждено или принадлежит другому боту
CALLBACK_QUERY_NOT_FOUND404Неизвестное или истёкшее нажатие кнопки
BOT_NOT_FOUND404Бот удалён, пока запрос был в пути; его токен больше не действует
FILE_NOT_FOUND404Неизвестный файл, или доступ к нему закончился
METHOD_NOT_FOUND404Такого метода нет
DELIVERY_MODE_CONFLICT409getUpdates, когда бот в режиме webhook
CONSUMER_CONFLICT409Аренду держит другой запрос, или epoch устарела; retry_after говорит, когда спросить снова
CURSOR_BEHIND409offset меньше подтверждённой позиции
IDEMPOTENCY_CONFLICT409Ключ уже использован с другими параметрами
FILE_NOT_READY409Файл ещё проверяется
PAYLOAD_TOO_LARGE413Загрузка больше допустимого размера
FILE_REJECTED422Файл не прошёл проверку или запрещён; error.reason объясняет причину (not_a_photo, type_not_allowed, malware_detected, ...)
RATE_LIMITED429Слишком много запросов; подождите retry_after
QUOTA_EXCEEDED429Суточная квота файлов исчерпана; подождите retry_after
TEMPORARILY_UNAVAILABLE503Временный сбой; повторите с тем же ключом
BOTS_MAINTENANCE503Техническая пауза платформы ботов; повторите позже с тем же ключом и offset