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, setMyConditionsModeПринимается, но не нужен: методы идемпотентны сами по себе (на нажатие отвечают один раз, а список команд или режим заменяет прежний), поэтому ключ не сохраняется.
  • Тот же ключ и те же параметры: возвращается результат первого вызова, и ничего не выполняется дважды. Перед ответом SMeet заново проверяет токен и чат, поэтому повтор после того, как пользователь остановил бота, получает BOT_STOPPED_OR_BLOCKED.
  • Тот же ключ и другие параметры: 409 IDEMPOTENCY_CONFLICT. Параметры сравниваются как JSON, так что порядок полей и пробелы не важны.
  • Ошибка до выполнения действия (FILE_NOT_READY, 429, 503, ошибка проверки параметров) ключ не расходует. Повторите тот же запрос с тем же ключом.
  • Повтор, который пришёл, пока первый вызов ещё выполняется, дожидается его не дольше 2 секунд и возвращает его результат; через 2 секунды он получает 503 TEMPORARILY_UNAVAILABLE с retry_after 1.
  • Повтор условного запроса с тем же ключом и телом возвращает результат первого успешного выполнения без повторной проверки чата и условий: они были проверены вместе с первым вызовом. Так outbox узнаёт исход в пределах 7 дней даже после Stop. Условный повтор с другим значением условия является другим телом: 409 IDEMPOTENCY_CONFLICT. Отказ, в том числе конфликт, ключ не расходует.
  • Ключи принадлежат одному боту и одному методу: один и тот же ключ в sendMessage и в editMessageText считается двумя разными ключами. Ключи uploadFile уникальны в пределах бота. SMeet хранит ключи 7 дней.

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

Возможности сервера#

getMe перечисляет в capabilities, что этот сервер обещает сверх версии 1.1.0. Незнакомые значения пропускайте.

ЗначениеСервер
chat_contextПоддерживает getChatContext.
access_revisionsКладёт access_revision в каждый Chat, а в групповые и канальные чаты ещё installation_generation, и проверяет expected_access_revision и expected_installation_generation.
message_revisionsКладёт message_revision в message, edited_message и в результаты отправки и правки и проверяет expected_message_revision.
initiator_conditionsПроверяет initiator.
strict_conditionsПоддерживает setMyConditionsMode и getMe.conditions_mode.
mini_appsПринимает кнопку mini_app и доставляет mini_app_submission (Мини-приложения).

Сервер, который не обещает ничего из этого, отвечает на getMe без capabilities, как версия 1.1.0. Правила для программы:

  1. Передавайте условие, только когда его значение есть в capabilities последнего ответа getMe. Сервер без этой возможности условие не проверяет: старый сервер молча пропускает параметр и выполняет действие без условия, а сервер 1.2.0 с выключенными условиями отклоняет вызов с 400 INVALID_REQUEST и именем параметра в field и действие не выполняет.
  2. Вызывайте getMe при запуске и не реже раза в 5 минут; сразу после 404 METHOD_NOT_FOUND от getChatContext или setMyConditionsMode, после ответа из правила 3 и после события из правила 5.
  3. Каждый успешный ответ сервера 1.2.0 на sendMessage, sendPhoto, sendDocument, editMessageText, deleteMessage, pinChatMessage, unpinChatMessage и restrictChatMember содержит result.chat.access_revision, в том числе ответ на повтор по ключу. Если его нет, условия не проверялись: остановите действия, которые от них зависят, и сообщите оператору.
  4. После любого периода без нужной возможности (откат сервера, платформа выключила условия) считайте все сохранённые версии недействительными: старый код мог менять доступ и сообщения, не выдавая новых версий.
  5. Свежее событие с chat, но без chat.access_revision, когда access_revisions есть в списке, означает откат сервера: сразу вызовите getMe и примените правило 4. Свежим считается событие с date позже момента, когда вы впервые увидели возможность, с запасом на расхождение часов. Повторы из FAILED и события без chat таким признаком не являются.

Условные запросы#

Изменяющий вызов может нести условия. Сервер проверяет их и выполняет действие в одной транзакции: Stop, новая установка или правка в тот же момент либо ждут вызова, либо уже произошли, и вызов их видит. Предварительная проверка через getChatContext этого не даёт, потому что её ответ сразу устаревает.

ПараметрВыполняется, пока
expected_access_revisionaccess_revision чата равна этой: версия диалога в личном чате, версия установки в группе, теме или канале
expected_installation_generationустановка бота в группе chat_id всё та же
expected_message_revisionу сообщения message_id эта message_revision
initiatorу человека сейчас есть роль в группе (Группы)
Методexpected_access_revisionexpected_installation_generationexpected_message_revisioninitiator
sendMessage, sendPhoto, sendDocumentдагруппы, темы, каналынетда
editMessageText, deleteMessageдагруппы, темы, каналыдада
pinChatMessage, unpinChatMessageдададада
restrictChatMemberдаданетда
  • Условие, которого метод не принимает, expected_installation_generation в личном чате, незнакомое поле, имя которого начинается с expected_, и незнакомое поле внутри initiator получают 400 INVALID_REQUEST с field: опечатка в имени условия никогда не превращает вызов в безусловный. answerCallbackQuery, rejectUpdate и setMyCommands условий не принимают.
  • Значение является строкой от 1 до 64 символов и сравнивается с сохранённым как строка; null равносилен отсутствию параметра. Значение, которое не строка, получает 400 INVALID_REQUEST.
  • Берите каждое значение из события, по которому принято решение, и передавайте его дальше без изменений. Никогда не обновляйте его из более позднего события, результата или getChatContext: так действие ожило бы после Stop и Start или после новой установки бота. Нажатие кнопки несёт версии момента нажатия; если нажатие исполняет решение, принятое при создании карточки, храните вместе с карточкой версии того события (Версии в событиях).
ОтветКогдаfieldЧто делать
409 ACCESS_REVISION_CONFLICTДоступ изменилсяexpected_access_revision или initiator.expected_access_revisionНе повторять. Отменить задание или вернуть его человеку.
409 INSTALLATION_GENERATION_CONFLICTБота установили зановоexpected_installation_generation или initiator.expected_installation_generationТо же; настройки прежней установки сами не переносить.
409 MESSAGE_REVISION_CONFLICTСообщение изменилосьexpected_message_revisionДождаться message_edited с новой версией и решить заново.
403 INITIATOR_NOT_AUTHORIZEDУ человека сейчас нет ролиinitiator.user_idНе повторять; действию нужно новое решение человека, которому оно разрешено.

Текущих значений ошибки не содержат, чтобы ни одна библиотека не подставила их в старое задание. После Stop условная отправка получает 403 BOT_STOPPED_OR_BLOCKED, после Stop и нового Start 409 ACCESS_REVISION_CONFLICT; оба ответа окончательны для задания. Ограничение частоты проверяется до блокировок, поэтому 429 может прийти раньше, а повтор после retry_after получит конфликт. Изменяющий вызов ждёт одновременного отзыва не дольше 2 секунд, затем отвечает 503 TEMPORARILY_UNAVAILABLE с retry_after 1; ничего не выполнено, и тот же ключ можно безопасно повторить.

HTTP
POST /bot-api/v1/deleteMessage HTTP/1.1
Host: messenger.scrile.com
Authorization: Bearer sbt1_EXAMPLE_REPLACE_WITH_YOUR_TOKEN
Idempotency-Key: 0b7c9a52-51f4-4c55-9a55-7e1f0c7a1b90:delete
Content-Type: application/json

{"chat_id": "90020", "message_id": "880123", "expected_message_revision": "2",
 "expected_installation_generation": "184505", "expected_access_revision": "184510"}

Если человек тем временем исправил сообщение:

JSON
{
  "ok": false,
  "error": {"code": "MESSAGE_REVISION_CONFLICT", "message": "The message changed after expected_message_revision",
            "field": "expected_message_revision"},
  "request_id": "5d0e7c1a-2f8b-4d6e-9a31-1c2b3d4e5f60"
}

Строгий режим#

setMyConditionsMode с {"mode": "strict"} заставляет сервер отклонять новый изменяющий вызов без нужных ему условий: 400 INVALID_REQUEST с именем недостающего параметра в field; {"mode": "optional"} выключает режим. Режим виден в getMe.conditions_mode и по умолчанию выключен.

Чат вызоваОбязательны
Личныйexpected_access_revision
Группа, тема, каналexpected_access_revision и expected_installation_generation; для deleteMessage ещё expected_message_revision

initiator никогда не обязателен. Повтор по ключу вызова, сделанного до включения режима, возвращает его результат. Режим действует, только пока в списке есть strict_conditions: сервер без неё ничего не отклоняет, поэтому продолжайте проверять capabilities (правило 1 выше).

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

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

Что учитываетсяТемпВсплеск
Сообщения в один чат: sendMessage, editMessageText, sendPhoto, sendDocument1 в секунду3
Те же методы на бота10 в секунду20
Те же методы для всех ботов одного владельца вместе20 в секунду40
getUpdates на бота5 в секунду10
getChatContext на бота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. Текущая версия 1.2.0; то, что она добавляет, перечисляет capabilities, поэтому программа отличает сервер с ней от более старого.
  • Сам контракт опубликован как openapi.json (OpenAPI 3.1); по нему можно сгенерировать клиент или проверять им свои тесты.

Справочник собран из контракта SMeet Bot API 1.2.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можноЗаменить меню команд, которое пользователь видит после ввода «/».
setMyConditionsModePOSTможноВключить или выключить строгий режим условий.
UpdatesПолучение событий через long polling.
getUpdatesPOSTПолучить ожидающие события через long polling.
rejectUpdatePOSTобязателенПеревести одно выданное и неподтверждённое событие в FAILED.
MessagesОтправка и редактирование сообщений, ответы на нажатия кнопок.
sendMessagePOSTобязателенОтправить текстовое сообщение в разрешённый личный чат, группу, тему или канал.
editMessageTextPOSTобязателенИзменить текст и кнопки сообщения, которое отправил этот бот.
deleteMessagePOSTобязателенУдалить сообщение.
pinChatMessagePOSTобязателенЗакрепить сообщение в группе, теме или канале.
unpinChatMessagePOSTобязателенОткрепить сообщение в группе, теме или канале.
restrictChatMemberPOSTобязателенУдалить участника из группы или канала.
getChatContextGET POSTПрочитать текущее состояние одного чата этого бота вместе с его версиями.
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. Сохранено.

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

setMyConditionsMode#

POST /setMyConditionsModeBot

Включить или выключить строгий режим условий.

В режиме strict новый вызов изменяющего метода без нужных ему условий получает 400 INVALID_REQUEST с именем недостающего параметра: expected_access_revision в личном чате; expected_access_revision и expected_installation_generation в группе, теме или канале, а для deleteMessage ещё expected_message_revision. Повтор по ключу вызова, сделанного до включения, по-прежнему возвращает его результат. Режим виден в getMe.conditions_mode. Доступен, когда capabilities содержит strict_conditions; иначе 404 METHOD_NOT_FOUND.

Заголовки#

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

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

ПолеТипОбязательноОписание
modestringДаОдно из значений: optional, strict.

Результат#

В поле 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 нет. Личный чат должен оставаться запущенным. Для группы или темы нужно can_send_messages, для канала: can_post_messages. Принимает условия expected_access_revision, expected_installation_generation (группы, темы и каналы) и initiator. Кнопке mini_app нужен личный чат бота, у которого есть мини-приложение.

Заголовки#

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

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

ПолеТипОбязательноОписание
chat_idIdДа
textstringДаОбычный текст. Строки, которые начинаются с "__META__:" или "__SYSTEM__", отклоняются. От 1 до 4096 символов.
reply_to_message_idIdНетСообщение из этого же чата.
reply_markupInlineKeyboardMarkupНет
expected_access_revisionRevisionНетДействовать, только пока access_revision чата равна этой; иначе 409 ACCESS_REVISION_CONFLICT.
expected_installation_generationRevisionНетГруппы, темы и каналы: действовать, только пока установка та же; иначе 409 INSTALLATION_GENERATION_CONFLICT. В личном чате 400 INVALID_REQUEST.
initiatorInitiatorНет

Результат#

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

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

editMessageText#

POST /editMessageTextMessages

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

Изменять можно только сообщения этого бота. Если reply_markup не передан, после правки у сообщения не будет кнопок. Принимает условия sendMessage и expected_message_revision. Кнопке mini_app нужен личный чат бота, у которого есть мини-приложение, как в sendMessage.

Заголовки#

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

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

ПолеТипОбязательноОписание
chat_idIdДа
message_idIdДа
textstringДаОт 1 до 4096 символов.
reply_markupInlineKeyboardMarkupНет
expected_access_revisionRevisionНетДействовать, только пока access_revision чата равна этой; иначе 409 ACCESS_REVISION_CONFLICT.
expected_installation_generationRevisionНетГруппы, темы и каналы: действовать, только пока установка та же; иначе 409 INSTALLATION_GENERATION_CONFLICT. В личном чате 400 INVALID_REQUEST.
expected_message_revisionRevisionНетДействовать, только пока message_revision сообщения равна этой; иначе 409 MESSAGE_REVISION_CONFLICT.
initiatorInitiatorНет

Результат#

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

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

deleteMessage#

POST /deleteMessageMessages

Удалить сообщение.

В личном чате бот может удалить только своё сообщение. В группе, теме или канале установке требуется can_delete_messages. Принимает условия expected_access_revision, expected_installation_generation (группы, темы и каналы), expected_message_revision и initiator.

Заголовки#

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

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

ПолеТипОбязательноОписание
chat_idIdДа
message_idIdДа
expected_access_revisionRevisionНетДействовать, только пока access_revision чата равна этой; иначе 409 ACCESS_REVISION_CONFLICT.
expected_installation_generationRevisionНетГруппы, темы и каналы: действовать, только пока установка та же; иначе 409 INSTALLATION_GENERATION_CONFLICT. В личном чате 400 INVALID_REQUEST.
expected_message_revisionRevisionНетДействовать, только пока message_revision сообщения равна этой; иначе 409 MESSAGE_REVISION_CONFLICT.
initiatorInitiatorНет

Результат#

Идентификатор удалённого сообщения. В поле result приходит объект с такими полями.

ПолеТипОбязательноОписание
message_idIdДа
chatChatДа
deletedbooleanДаВсегда true.

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

pinChatMessage#

POST /pinChatMessageMessages

Закрепить сообщение в группе, теме или канале.

Установке требуется can_pin_messages. Принимает условия expected_access_revision, expected_installation_generation, expected_message_revision и initiator.

Заголовки#

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

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

ПолеТипОбязательноОписание
chat_idIdДа
message_idIdДа
expected_access_revisionRevisionНетДействовать, только пока access_revision чата равна этой; иначе 409 ACCESS_REVISION_CONFLICT.
expected_installation_generationRevisionНетГруппы, темы и каналы: действовать, только пока установка та же; иначе 409 INSTALLATION_GENERATION_CONFLICT. В личном чате 400 INVALID_REQUEST.
expected_message_revisionRevisionНетДействовать, только пока message_revision сообщения равна этой; иначе 409 MESSAGE_REVISION_CONFLICT.
initiatorInitiatorНет

Результат#

Сообщение закреплено. В поле result приходит объект с такими полями.

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

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

unpinChatMessage#

POST /unpinChatMessageMessages

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

Установке требуется can_pin_messages. Принимает условия expected_access_revision, expected_installation_generation, expected_message_revision и initiator.

Заголовки#

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

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

ПолеТипОбязательноОписание
chat_idIdДа
message_idIdДа
expected_access_revisionRevisionНетДействовать, только пока access_revision чата равна этой; иначе 409 ACCESS_REVISION_CONFLICT.
expected_installation_generationRevisionНетГруппы, темы и каналы: действовать, только пока установка та же; иначе 409 INSTALLATION_GENERATION_CONFLICT. В личном чате 400 INVALID_REQUEST.
expected_message_revisionRevisionНетДействовать, только пока message_revision сообщения равна этой; иначе 409 MESSAGE_REVISION_CONFLICT.
initiatorInitiatorНет

Результат#

Сообщение откреплено. В поле result приходит объект с такими полями.

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

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

restrictChatMember#

POST /restrictChatMemberMessages

Удалить участника из группы или канала.

Установке требуется can_restrict_members. Этим методом нельзя удалить владельца группы и самого бота. Принимает условия expected_access_revision, expected_installation_generation и initiator.

Заголовки#

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

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

ПолеТипОбязательноОписание
chat_idIdДа
user_idIdДа
expected_access_revisionRevisionНетДействовать, только пока access_revision установки равна этой; иначе 409 ACCESS_REVISION_CONFLICT.
expected_installation_generationRevisionНетДействовать, только пока установка та же; иначе 409 INSTALLATION_GENERATION_CONFLICT.
initiatorInitiatorНет

Результат#

Участник удалён. В поле result приходит объект с такими полями.

ПолеТипОбязательноОписание
chatChatДа
user_idIdДа
restrictedbooleanДаВсегда true.

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

getChatContext#

GET POST /getChatContextMessages

Прочитать текущее состояние одного чата этого бота вместе с его версиями.

Для личного чата: человек и состояние диалога, в том числе когда он остановлен, заблокирован или человек ушёл из пространства. Для группы, темы или канала: владелец группы, права установки и время установки. С user_id: является ли этот человек человеком этого чата, а в группе: известен ли он боту там, участник ли он и владелец ли. Ответ является снимком и устаревает сразу: действие делает безопасным условие изменяющего вызова, а не чтение перед ним. Доступен, когда capabilities содержит chat_context; иначе 404 METHOD_NOT_FOUND. Не больше 5 вызовов в секунду на бота, всплеск до 10.

Параметры#

ИмяТипОбязательноОписание
chat_idIdДаchat.id из события: личный чат, группа, тема или канал этого бота.
user_idIdНетЧеловек, которого нужно проверить; ответ в user_check.

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

Результат#

В поле result приходит ChatContext. Чат в его нынешнем состоянии.

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

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. Принимает условия sendMessage. Кнопке mini_app нужен личный чат бота, у которого есть мини-приложение, как в sendMessage.

Заголовки#

ЗаголовокТипОбязательноОписание
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Нет
expected_access_revisionRevisionНетДействовать, только пока access_revision чата равна этой; иначе 409 ACCESS_REVISION_CONFLICT.
expected_installation_generationRevisionНетГруппы, темы и каналы: действовать, только пока установка та же; иначе 409 INSTALLATION_GENERATION_CONFLICT. В личном чате 400 INVALID_REQUEST.
initiatorInitiatorНет

Результат#

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

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

sendDocument#

POST /sendDocumentFiles

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

Принимает условия sendMessage. Кнопке mini_app нужен личный чат бота, у которого есть мини-приложение, как в sendMessage.

Заголовки#

ЗаголовокТипОбязательноОписание
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Нет
expected_access_revisionRevisionНетДействовать, только пока access_revision чата равна этой; иначе 409 ACCESS_REVISION_CONFLICT.
expected_installation_generationRevisionНетГруппы, темы и каналы: действовать, только пока установка та же; иначе 409 INSTALLATION_GENERATION_CONFLICT. В личном чате 400 INVALID_REQUEST.
initiatorInitiatorНет

Результат#

В поле 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
my_chat_membermy_chat_memberMyChatMember
delivery_gapdelivery_gapDeliveryGap
file_status_changedfile_statusFileStatusChange
mini_app_submissionmini_app_submissionMiniAppSubmission

Типы#

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

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

ИмяТипОписание
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.
RevisionstringНепрозрачная версия доступа, установки или сообщения. Храните её строкой и сравнивайте только на равенство; никогда не вычисляйте с ней. От 1 до 64 символов. Пример: 184502.
UpdateTypestringНезнакомые значения нужно пропускать. Одно из значений: message, message_edited, message_deleted, callback_query, chat_access_changed, my_chat_member, delivery_gap, file_status_changed, mini_app_submission.
FileStatusstringОдно из значений: scanning, ready, rejected, scan_failed.

User#

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

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

Chat#

ПолеТипОбязательноОписание
idIdДа
typestringДаНезнакомые значения нужно пропускать. Одно из значений: private, group, channel.
group_idIdНетРодительская группа или канал для чатов group и channel.
titlestringНетТекущее название группы или канала.
topic_idIdНетЕсть, когда этот идентификатор чата обозначает тему внутри родительской группы или канала.
access_revisionRevisionНетВерсия доступа бота к этому чату на момент создания объекта: версия диалога для личного чата и версия установки в родительской группе для группы, темы или канала. Передавайте её обратно как expected_access_revision. Есть в каждом объекте сервера 1.2.0.
installation_generationRevisionНетТолько для групп и каналов: какая это установка бота. Новая установка даёт новое значение. Передавайте его обратно как expected_installation_generation.

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.
capabilitiesмассив stringНетЧто этот сервер обещает сверх 1.1.0. chat_context: getChatContext. access_revisions: в чатах есть access_revision и installation_generation, а изменяющие методы проверяют ожидаемые значения. message_revisions: в сообщениях есть message_revision, его проверяет expected_message_revision. initiator_conditions: изменяющие методы проверяют initiator. strict_conditions: setMyConditionsMode и conditions_mode. mini_apps: кнопка mini_app и событие mini_app_submission. Незнакомые значения пропускайте. Поля нет у сервера, который не обещает ничего из этого.
conditions_modestringНетЕсть вместе с возможностями условий; меняется методом setMyConditionsMode. Одно из значений: optional, strict.
mini_appBotMiniAppНетМини-приложение бота, пока capabilities содержит mini_apps и у бота оно есть.

BotMiniApp#

Мини-приложение бота в описании getMe.

ПолеТипОбязательноОписание
idIdДаТо же значение, что app_id в mini_app_submission.
statusstringДаНезнакомые значения нужно пропускать. Одно из значений: active, paused_by_owner, blocked_by_admin.
publishedbooleanДаРаботает ли проверенная конфигурация. Кнопка mini_app открывает только опубликованную; до этого человек видит, что мини-приложение пока недоступно.

MiniAppButton#

Открывает мини-приложение бота в приложении SMeet человека, только опубликованную версию.

ПолеТипОбязательноОписание
start_paramstringНетПередаётся мини-приложению при открытии. Пустой или отсутствующий: без параметра. Сервер мини-приложения видит его после входа, а отправка результата его не повторяет. До 64 символов. Шаблон: ^[A-Za-z0-9_-]*$.

InlineKeyboardButton#

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

InlineKeyboardMarkup#

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

Attachment#

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

Message#

ПолеТипОбязательноОписание
message_idIdДа
chatChatДа
fromUserДа
dateTimestampДа
edit_dateTimestampНет
textstringНетДо 4096 символов.
reply_to_message_idIdНет
attachmentsмассив AttachmentНет
reply_markupInlineKeyboardMarkupНет
has_unsupported_contentbooleanНетtrue, если в сообщении есть содержимое, которое эта версия API не передаёт (видеосообщение в кружке, геопозиция, опрос и подобное). Тогда text содержит читаемую замену, если она есть. Голосовое сообщение сюда не относится: оно приходит вложением voice.
message_revisionRevisionНетВерсия этого сообщения: меняется при каждой правке его текста или цитаты, но не расшифровки голосового. Есть в message, edited_message и в результатах отправки и правки; передавайте её обратно как expected_message_revision.

CallbackQuery#

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

ChatAccess#

ПолеТипОбязательноОписание
chatChatДаС access_revision после изменения.
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Да

GroupBotPermissions#

ПолеТипОбязательноОписание
read_all_messagesbooleanДаПолучать все новые сообщения людей. Без этого приходят только адресованные боту сообщения.
can_send_messagesbooleanДаОтправлять сообщения в группы и их темы.
can_edit_messagesbooleanДа
can_delete_messagesbooleanДа
can_pin_messagesbooleanДа
can_restrict_membersbooleanДа
can_post_messagesbooleanДаПубликовать в каналах; не зависит от can_send_messages.

MyChatMember#

ПолеТипОбязательноОписание
chatChatДаС access_revision и installation_generation после изменения.
fromUserДаЧеловек, который установил, изменил или удалил бота.
statusstringДаОдно из значений: member, left.
permissionsGroupBotPermissionsДа
dateTimestampДа

DeliveryGap#

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

ПолеТипОбязательноОписание
chatChatДа
dropped_countintegerДаНе меньше 1.
first_dropped_atTimestampДа
last_dropped_atTimestampДа

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НетЧат этого сообщения. Для вложения, полученного в группе или теме, это групповой чат с group_id, title и topic_id.
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
my_chat_membermy_chat_member
delivery_gapdelivery_gap
file_status_changedfile_status
mini_app_submissionmini_app_submission

Правка старого сообщения не новая команда: считайте message_edited исправлением. Версии в данных события относятся к моменту, когда оно произошло, и повтор из FAILED передаёт их без изменений; у событий, записанных до 1.2.0, версий нет.

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

MiniAppSubmission#

Человек отправил боту форму его мини-приложения и подтвердил это в SMeet. Приложение, среду, человека и чат определяет SMeet; страница передаёт только data. Одна отправка даёт одно событие, сколько бы раз страница ни повторяла запрос.

ПолеТипОбязательноОписание
idstring (uuid)ДаОтправка, как её знают приложение человека и мини-приложение.
app_idIdДа
environmentstringДаtest приходит только от владельца, принявших приглашение тестировщиков и проверяющих. Одно из значений: production, test.
client_request_idstringДаrequestId этой формы, который задала страница. Повтор страницы с тем же значением является той же отправкой и второго события не даёт. От 1 до 64 символов. Шаблон: ^[A-Za-z0-9._:-]+$.
fromUserДа
chatChatДаЛичный чат человека с ботом с той access_revision, с которой было открыто мини-приложение. Отвечайте туда, передав эту версию как expected_access_revision.
dataобъектДаФорма в том виде, в каком её отправила страница, JSON-объект до 16 КиБ.
dateTimestampДа

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.

Initiator#

Человек, по решению которого действует бот, и роль, которая у него должна быть сейчас. Проверяется в родительской группе chat_id вместе с действием; если проверка не прошла, ответ 403 INITIATOR_NOT_AUTHORIZED, одинаковый для «не участник», «не владелец» и «неизвестен боту». Незнакомое поле внутри объекта даёт 400 INVALID_REQUEST.

ПолеТипОбязательноОписание
user_idIdДаОбычно callback_query.from.id или message.from.id.
rolestringДаgroup_owner: текущий владелец группы. group_member: участник группы, известный там боту (бот получал от него событие после текущей установки). Оба должны быть активными участниками пространства группы. Одно из значений: group_member, group_owner.
chat_idIdДаГруппа, тема или канал этого бота. Если чат самого метода групповой, то та же группа; если личный, то любая группа бота.
expected_installation_generationRevisionНетУстановка бота в этой группе.
expected_access_revisionRevisionНетВерсия доступа установки бота в этой группе.

ChatContext#

Один чат в его нынешнем состоянии. Есть ровно одно из полей private и group.

ПолеТипОбязательноОписание
chatChatДаС access_revision и для групп installation_generation на этот момент.
spaceSpaceДа
privateChatContextPrivateНет
groupChatContextGroupНет
user_checkUserCheckНетЕсть, если передан user_id.

ChatContextPrivate#

ПолеТипОбязательноОписание
userUserДа
statusstringДаКак в chat_access_changed и как его сейчас увидела бы отправка: blocked и тогда, когда человек добавил аккаунт бота в общий список блокировок, removed и тогда, когда он больше не активный участник пространства бота. Одно из значений: started, stopped, blocked, removed.

ChatContextGroup#

ПолеТипОбязательноОписание
ownerUserДа
permissionsGroupBotPermissionsДа
installed_atTimestampДаКогда началась текущая установка; ничего более раннего бот не видит.

UserCheck#

ПолеТипОбязательноОписание
user_idIdДа
is_chat_userbooleanНетТолько для личных чатов. Это человек этого чата.
knownbooleanНетТолько для групп. Владелец группы или человек, от которого бот получал событие в этой группе после текущей установки (около 8 суток после последнего такого события).
is_memberbooleanНетТолько при known true. Состоит в группе и является активным участником её пространства.
is_ownerbooleanНетТолько при known true. Текущий владелец группы и активный участник её пространства.

File#

ПолеТипОбязательноОписание
file_idFileIdДа
kindstringДаОдно из значений: photo, document, voice.
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, если ошибка в одном параметре, при четырёх кодах конфликтов и инициатора (условие, которое не выполнилось, например expected_access_revision или initiator.user_id), и при CHAT_NOT_FOUND и GROUP_BOT_NOT_INSTALLED о чате инициатора (initiator.chat_id).
reasonstringНетЕсть при FILE_REJECTED: почему файл нельзя использовать (not_a_photo, type_not_allowed, malware_detected, too_large, empty, scanner_unavailable, ...). При INVALID_REQUEST о reply_markup значение mini_app_not_configured: кнопка mini_app в группе или у бота без мини-приложения. Незнакомые значения нужно принимать.
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Организация выключила работу ботов
GROUP_BOT_PERMISSION_DENIED403Боту не выдали это действие в группе или канале
INITIATOR_NOT_AUTHORIZED403У инициатора сейчас нет нужной роли; не повторяйте
CHAT_NOT_FOUND404У этого бота нет такого чата (чужие чаты не раскрываются)
GROUP_BOT_NOT_INSTALLED404Установка бота в группе закончилась
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Файл ещё проверяется
ACCESS_REVISION_CONFLICT409Доступ к этому чату изменился; expected_access_revision больше не совпадает. Не повторяйте
INSTALLATION_GENERATION_CONFLICT409Бота установили заново; expected_installation_generation больше не совпадает. Не повторяйте
MESSAGE_REVISION_CONFLICT409Сообщение изменилось после expected_message_revision. Не повторяйте
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