# API ЭТОЧАТБОТ — Telegram Адрес API: `https://etochat.bot/api/v2/telegram`. Написать можно только тому, кто сам писал боту или нажал «Старт». Через Telegram Business — в переписках, где за последние 24 часа было сообщение: так Telegram ограничивает бизнес-ботов. Метка из ссылки t.me/ваш_бот?start=метка сохраняется в переменную `start_param`. ## Основы ### Ключ Ключ выпускается в кабинете: бот → «Настройки» → «Для разработчиков». Передавайте его в заголовке `Authorization: Bearer <ключ>`. Ключ показываем один раз, у нас хранится только отпечаток. Потеряли — отзовите и выпустите новый. Ключ работает только с одним ботом, у бота может быть до 5 ключей. Канал в адресе должен совпадать с каналом бота, иначе придёт 400 `channel_mismatch`. Заголовок: ``` Authorization: Bearer etchtbt_bot_3f9a1c7d2e5b48a06c1f9d3e7b2a5c80d4f6e1b93a7c2d58 ``` ### Повторная отправка У «Отправить сообщение» и «Запустить сценарий» передавайте заголовок `Idempotency-Key` — любую уникальную строку, например UUID. Если связь оборвалась и вы отправили запрос ещё раз, второго сообщения не будет: придёт результат первого с `idempotent_replay: true`. Тот же ключ с другим телом запроса — ошибка 422 `idempotency_key_reused`. Повтор того же запроса: ```json { "status": "ok", "message_id": 16672, "idempotent_replay": true } ``` ### Страницы, лимиты, тариф Списки отдаются страницами: передавайте `limit` и `cursor`, в ответе будут `next_cursor` и `has_more`. Разбирать `cursor` не нужно — просто передавайте дальше. Не больше 120 запросов в минуту на бота. В каждом ответе есть заголовки `X-RateLimit-Limit`, `X-RateLimit-Remaining` и `X-RateLimit-Reset`. Превысили — придёт 429 и заголовок `Retry-After`. Отправка и запуск сценариев работают, пока у бота оплачен тариф, иначе придёт 402. Чтение работает всегда. Проверить заранее можно в `GET /telegram/me` — поле `can_send`. Ответ 200 при отправке значит, что мессенджер принял сообщение. Не принял — придёт 502 с причиной. Время везде московское (UTC+3): `2026-10-23 18:44:31`. У событий `created_at` — с поясом. Заголовки лимита: ``` X-RateLimit-Limit: 120 X-RateLimit-Remaining: 117 X-RateLimit-Reset: 1761234567 ``` ## Ограничения канала - Текст сообщения: до 4096 символов. - Подпись к файлу: до 1024 символов. - Фото: до 10 МБ. - Другие файлы: до 50 МБ. - Кнопок-ссылок под сообщением: до 100. - Изменить и удалить сообщение: в течение примерно 48 часов после отправки. - Запросов к API: до 120 в минуту на бота. ## Что умеет Telegram Чего здесь нет, канал не умеет — на такой запрос API ответит ошибкой 422. - Отправить текстовое сообщение — работает - Отправить фото — работает - Отправить документ — работает - Отправить видео — работает - Отправить аудио — работает - Отправить голосовое — работает. Голосовое — в ogg/opus, другой формат уйдёт обычным аудио. Если человек запретил голосовые (настройка Telegram Premium), придёт 502 с причиной. - Отправить видео-кружок — работает. Подпись к кружку уйдёт отдельным сообщением. - Кнопки-ссылки под сообщением — работает - Изменить текст отправленного сообщения — работает - Удалить отправленное сообщение — работает - Показать «печатает…» — работает - Прочитать переписку — из кабинета. Telegram не отдаёт ботам историю — здесь переписка, которую видел бот. - Данные человека: теги и переменные — работает - Скачать файл из сообщения — работает - Открыть или закрыть диалог в кабинете — работает ## Методы ### Бот #### Проверить ключ `GET /telegram/me` Какому боту принадлежит ключ. Ничего не меняет — с него удобно начать. Если can_send = false, у бота не оплачен тариф и отправка вернёт 402. Запрос: ```bash curl -X GET https://etochat.bot/api/v2/telegram/me \ -H "Authorization: Bearer etchtbt_bot_3f9a1c7d2e5b48a06c1f9d3e7b2a5c80d4f6e1b93a7c2d58" ``` Ответ: ```json { "bot": { "id": 42, "name": "Поддержка", "channel": "telegram", "username": "my_bot" }, "can_send": true, "rate_limit": { "per_minute": 120 } } ``` ### Диалоги #### Список диалогов `GET /telegram/chats` Люди, которые писали боту, сверху — с последней активностью. Ответ приходит страницами: передавайте next_cursor в следующий запрос, пока has_more = true. | Параметр | Тип | Описание | | --- | --- | --- | | `limit` | число | Сколько вернуть, от 1 до 100. По умолчанию 50. | | `cursor` | строка | next_cursor из прошлого ответа. | | `query` | строка | Поиск по имени и username. | Запрос: ```bash curl -X GET https://etochat.bot/api/v2/telegram/chats?limit=1 \ -H "Authorization: Bearer etchtbt_bot_3f9a1c7d2e5b48a06c1f9d3e7b2a5c80d4f6e1b93a7c2d58" ``` Ответ: ```json { "data": [ { "chat_id": "9995735656", "name": "Иван", "username": "ivan", "status": "open", "unread": 0, "blocked_bot": false, "external_ids": [ "user-42" ], "created_at": "2026-10-14 10:02:11", "updated_at": "2026-10-23 18:44:31", "business": false, "tags": [ "заявка" ], "automation": { "mode": "bot", "until": null } } ], "has_more": true, "next_cursor": "MjAyNi0xMC0yMyAxODo0NDozMXwxNzc" } ``` #### Один диалог `GET /telegram/chats/{chat_id}` Всё про одного человека: имя, теги, ваши external_id, кто сейчас ведёт диалог (automation) и переменные, которые сохранили сценарии, — ответы на вопросы, телефон, номер бонуса. Если человек пришёл по ссылке t.me/ваш_бот?start=reels_42, метка будет в переменной start_param. business = true — переписка идёт через Telegram Business от имени вашего аккаунта. Запрос: ```bash curl -X GET https://etochat.bot/api/v2/telegram/chats/9995735656 \ -H "Authorization: Bearer etchtbt_bot_3f9a1c7d2e5b48a06c1f9d3e7b2a5c80d4f6e1b93a7c2d58" ``` Ответ: ```json { "chat": { "chat_id": "9995735656", "name": "Иван", "username": "ivan", "status": "open", "unread": 0, "blocked_bot": false, "external_ids": [ "user-42" ], "created_at": "2026-10-14 10:02:11", "updated_at": "2026-10-23 18:44:31", "business": false, "tags": [ "заявка" ], "automation": { "mode": "bot", "until": null }, "variables": { "start_param": "reels_42", "phone": "+79000000000" } } } ``` #### Переписка `GET /telegram/chats/{chat_id}/messages` Переписка, новые сообщения сверху, страницами — то же, что в «Сообщениях» кабинета. Кто отправил — в sent_by, файлы — в attachments со ссылкой на скачивание. file_name и file_size — если они известны. > Особенность канала: Telegram не отдаёт ботам историю — здесь переписка, которую видел бот. | Параметр | Тип | Описание | | --- | --- | --- | | `limit` | число | Сколько вернуть, от 1 до 100. По умолчанию 50. | | `cursor` | строка | next_cursor из прошлого ответа. | Запрос: ```bash curl -X GET https://etochat.bot/api/v2/telegram/chats/9995735656/messages?limit=2 \ -H "Authorization: Bearer etchtbt_bot_3f9a1c7d2e5b48a06c1f9d3e7b2a5c80d4f6e1b93a7c2d58" ``` Ответ: ```json { "data": [ { "id": 16672, "direction": "out", "type": "text", "text": "Добрый день! Сейчас подберу.", "attachments": [], "sent_by": "automation", "sent_at": "2026-10-23 18:44:40", "edited": false }, { "id": 16671, "direction": "in", "type": "voice", "text": "", "attachments": [ { "type": "voice", "mime_type": "audio/ogg", "duration": 7, "url": "https://etochat.bot/api/v2/telegram/files/16671" } ], "sent_by": "person", "sent_at": "2026-10-23 18:44:31", "edited": false } ], "has_more": true, "next_cursor": "MjAyNi0xMC0yMyAxODo0NDozMXwxNjY3MQ", "source": "cabinet" } ``` #### Открыть или закрыть диалог `POST /telegram/chats/{chat_id}/status` Закрывает или открывает диалог в кабинете — как кнопка «Закрыть» у оператора. Человек ничего не заметит, а если напишет — диалог откроется снова. Пригодится, если ваша система сама пишет человеку и ответ не нужен: диалог не будет висеть «без ответа». | Параметр | Тип | Описание | | --- | --- | --- | | `status` | строка | open или closed. Обязательно. | Запрос: ```bash curl -X POST https://etochat.bot/api/v2/telegram/chats/9995735656/status \ -H "Authorization: Bearer etchtbt_bot_3f9a1c7d2e5b48a06c1f9d3e7b2a5c80d4f6e1b93a7c2d58" \ -H "Content-Type: application/json" \ -d '{ "status": "closed" }' ``` Ответ: ```json { "status": "ok", "chat_id": "9995735656", "chat_status": "closed", "closed_at": "2026-10-23 19:32:32", "changed": true } ``` #### Кто отвечает: бот или человек `POST /telegram/chats/{chat_id}/automation` Передаёт диалог вашему сотруднику или вашему ИИ и обратно. mode = human — наши сценарии и ИИ-агент в этом диалоге молчат, сообщения человека по-прежнему приходят вам, отвечаете вы через «Отправить сообщение». По умолчанию на сутки, срок можно задать в minutes. mode = bot — вернуть диалог боту. Если человек нажмёт кнопку под старым сообщением бота, бот ответит. | Параметр | Тип | Описание | | --- | --- | --- | | `mode` | строка | human — отвечает человек, бот молчит; bot — вернуть боту. Обязательно. | | `minutes` | число | Для human: на сколько минут, от 1 до 43200 (30 дней). По умолчанию 1440 — сутки. | Запрос: ```bash curl -X POST https://etochat.bot/api/v2/telegram/chats/9995735656/automation \ -H "Authorization: Bearer etchtbt_bot_3f9a1c7d2e5b48a06c1f9d3e7b2a5c80d4f6e1b93a7c2d58" \ -H "Content-Type: application/json" \ -d '{ "mode": "human", "minutes": 120 }' ``` Ответ: ```json { "status": "ok", "chat_id": "9995735656", "mode": "human", "until": "2026-10-23 21:32:32" } ``` ### Сообщения #### Отправить сообщение `POST /telegram/messages` Отправляет человеку текст или файл. Файл — ссылкой в attachment или загрузкой (multipart/form-data). Ответ 200 — Telegram принял сообщение, иначе придёт 502 с причиной. | Параметр | Тип | Описание | | --- | --- | --- | | `chat_id` | строка | Кому. Нужен chat_id или external_id. | | `external_id` | строка | Ваш идентификатор человека, если вы связали его через «Связать с вашим пользователем». | | `text` | строка | Текст. Обязателен, если нет файла; с файлом — подпись. Можно размечать HTML: , , . Перенос строки — \n. | | `attachment` | объект | Файл ссылкой: {"type":"photo","url":"https://…"}. type: photo, document, video, audio, voice, video_note. Ссылка прямая, без авторизации и редиректов. file_name — как файл назовётся у человека. Голосовое (voice) — только ogg/opus, другой формат уйдёт обычным аудио. | | `file` | файл | Файл загрузкой вместо ссылки: multipart/form-data с полями file, type, chat_id и, если нужно, text. | | `buttons` | массив | Кнопки-ссылки: [{"text":"…","url":"https://…"}], до 100 штук. Кнопки с ответами делаются в сценарии — запустите его методом «Запустить сценарий». | Запрос: ```bash curl -X POST https://etochat.bot/api/v2/telegram/messages \ -H "Authorization: Bearer etchtbt_bot_3f9a1c7d2e5b48a06c1f9d3e7b2a5c80d4f6e1b93a7c2d58" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: 8f14e45f-ea6c-4a1e-9b2d-7c3a1f0b5d21" \ -d '{ "chat_id": "9995735656", "text": "Ваш заказ №1043 оплачен.", "buttons": [ { "text": "Статус доставки", "url": "https://example.com/orders/1043" } ] }' # Голосовое по ссылке curl -X POST https://etochat.bot/api/v2/telegram/messages \ -H "Authorization: Bearer etchtbt_bot_3f9a1c7d2e5b48a06c1f9d3e7b2a5c80d4f6e1b93a7c2d58" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: 3c2b7d90-5e1a-4f0b-8a6d-1e9f4b2c7a53" \ -d '{ "chat_id": "9995735656", "attachment": { "type": "voice", "url": "https://example.com/answers/1043.ogg" } }' # Файл загрузкой curl -X POST https://etochat.bot/api/v2/telegram/messages \ -H "Authorization: Bearer etchtbt_bot_3f9a1c7d2e5b48a06c1f9d3e7b2a5c80d4f6e1b93a7c2d58" \ -F chat_id=9995735656 \ -F type=document \ -F "text=Прайс на октябрь" \ -F file=@price.pdf ``` Ответ: ```json { "status": "ok", "message_id": 16672, "chat_id": "9995735656", "channel": "telegram", "type": "text" } ``` #### Изменить сообщение `POST /telegram/messages/{id}/edit` Меняет текст сообщения, которое отправил этот бот. id — message_id из ответа «Отправить сообщение» или id из переписки. Можно в течение примерно 48 часов после отправки. Позже придёт 422 edit_window_expired. | Параметр | Тип | Описание | | --- | --- | --- | | `text` | строка | Новый текст. Обязательно. | Запрос: ```bash curl -X POST https://etochat.bot/api/v2/telegram/messages/16672/edit \ -H "Authorization: Bearer etchtbt_bot_3f9a1c7d2e5b48a06c1f9d3e7b2a5c80d4f6e1b93a7c2d58" \ -H "Content-Type: application/json" \ -d '{ "text": "Ваш заказ №1043 доставлен." }' ``` Ответ: ```json { "status": "ok", "message_id": 16672 } ``` #### Удалить сообщение `POST /telegram/messages/{id}/delete` Удаляет сообщение бота у человека и в кабинете. Сроки те же, что у изменения. Если Telegram откажет, придёт 502 channel_delete_failed, а в кабинете сообщение останется. Запрос: ```bash curl -X POST https://etochat.bot/api/v2/telegram/messages/16672/delete \ -H "Authorization: Bearer etchtbt_bot_3f9a1c7d2e5b48a06c1f9d3e7b2a5c80d4f6e1b93a7c2d58" \ -H "Content-Type: application/json" ``` Ответ: ```json { "status": "ok", "message_id": 16672 } ``` #### Показать «печатает…» `POST /telegram/typing` Показывает человеку «печатает…». Пропадёт сам через несколько секунд или когда придёт сообщение. | Параметр | Тип | Описание | | --- | --- | --- | | `chat_id` | строка | Кому. Нужен chat_id или external_id. | | `external_id` | строка | Ваш идентификатор человека. | Запрос: ```bash curl -X POST https://etochat.bot/api/v2/telegram/typing \ -H "Authorization: Bearer etchtbt_bot_3f9a1c7d2e5b48a06c1f9d3e7b2a5c80d4f6e1b93a7c2d58" \ -H "Content-Type: application/json" \ -d '{ "chat_id": "9995735656" }' ``` Ответ: ```json { "status": "ok" } ``` #### Скачать файл из сообщения `GET /telegram/files/{message_id}` Отдаёт файл из сообщения: голосовое, фото, документ, видео — и присланные человеком, и отправленные ему. Ссылка на него приходит в attachments[].url — в вебхуке и в переписке. Скачивайте с вашим ключом в заголовке Authorization. Запрос: ```bash curl -X GET https://etochat.bot/api/v2/telegram/files/16671 \ -H "Authorization: Bearer etchtbt_bot_3f9a1c7d2e5b48a06c1f9d3e7b2a5c80d4f6e1b93a7c2d58" \ -o file ``` Ответ: Сам файл. Тип — в заголовке Content-Type. ### Сценарии #### Запустить сценарий `POST /telegram/trigger` Запускает сценарий из конструктора для одного человека. Удобно для уведомлений: данные передаёте переменными, а текст, кнопки и логика живут в конструкторе — их можно менять без правки вашего кода. Нужен включённый сценарий с триггером «Из другой автоматизации», иначе придёт 404 no_flow. В сценарии переменные доступны как {{var.имя}}. В ответе outcome: handled — сценарий отработал, operator — сценарий передал диалог оператору. | Параметр | Тип | Описание | | --- | --- | --- | | `chat_id` | строка | Для кого. Нужен chat_id или external_id. | | `external_id` | строка | Ваш идентификатор человека. | | `vars` | объект | Данные для сценария. Имена — латиница, цифры и подчёркивание; до 20 переменных, значение до 500 символов. | Запрос: ```bash curl -X POST https://etochat.bot/api/v2/telegram/trigger \ -H "Authorization: Bearer etchtbt_bot_3f9a1c7d2e5b48a06c1f9d3e7b2a5c80d4f6e1b93a7c2d58" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: b71e4a2c-9d3f-4c88-a0e5-6f2d8c1b9e47" \ -d '{ "external_id": "user-42", "vars": { "plan": "Премиум", "days_left": "3" } }' ``` Ответ: ```json { "status": "ok", "outcome": "handled", "chat_id": "9995735656" } ``` ### Контакты #### Связать с вашим пользователем `POST /telegram/contacts/link` Связывает человека с идентификатором в вашей системе, например с номером клиента в CRM. Дальше вместо chat_id можно передавать external_id. Повторный вызов с тем же external_id перевесит связку на другой диалог. | Параметр | Тип | Описание | | --- | --- | --- | | `chat_id` | строка | Наш идентификатор диалога. Обязательно. | | `external_id` | строка | Ваш идентификатор, до 190 символов. Большие и маленькие буквы различаются. Обязательно. | Запрос: ```bash curl -X POST https://etochat.bot/api/v2/telegram/contacts/link \ -H "Authorization: Bearer etchtbt_bot_3f9a1c7d2e5b48a06c1f9d3e7b2a5c80d4f6e1b93a7c2d58" \ -H "Content-Type: application/json" \ -d '{ "chat_id": "9995735656", "external_id": "user-42" }' ``` Ответ: ```json { "linked": true, "external_id": "user-42", "chat_id": "9995735656" } ``` ### События и вебхуки #### Журнал событий `GET /telegram/events` Те же события, что уходят вебхуком, списком от старых к новым. Храним 14 дней. Пригодится, если ваш сервер был недоступен, или если вебхук поднимать не хочется и проще забирать события раз в минуту. Запомните cursor последнего обработанного события и в следующий раз передайте его в after — придёт всё, что было после. | Параметр | Тип | Описание | | --- | --- | --- | | `after` | строка | cursor последнего события, которое вы уже обработали. Без него — с самого старого. | | `limit` | число | Сколько вернуть, от 1 до 100. По умолчанию 100. | | `types` | строка | Только эти события, через запятую, например message.received,message.sent. | Запрос: ```bash curl -X GET https://etochat.bot/api/v2/telegram/events?after=88213 \ -H "Authorization: Bearer etchtbt_bot_3f9a1c7d2e5b48a06c1f9d3e7b2a5c80d4f6e1b93a7c2d58" ``` Ответ: ```json { "data": [ { "cursor": "88214", "id": "0b6f3c1e-8d2a-4f57-9a41-2c7e5d9b8f10", "type": "message.received", "created_at": "2026-10-23T18:44:32+03:00", "bot_id": 42, "channel": "telegram", "data": { "chat": { "chat_id": "9995735656", "name": "Иван", "username": "ivan", "business": false }, "message": { "id": 16671, "direction": "in", "type": "voice", "text": "", "attachments": [ { "type": "voice", "mime_type": "audio/ogg", "duration": 7, "url": "https://etochat.bot/api/v2/telegram/files/16671" } ], "sent_by": "person", "sent_at": "2026-10-23 18:44:31" } } } ], "next_after": "88214", "has_more": false } ``` #### Ваши вебхуки `GET /telegram/webhooks` Адреса, на которые мы отправляем события этого бота, и когда туда последний раз удалось доставить. Секрет не показываем. Запрос: ```bash curl -X GET https://etochat.bot/api/v2/telegram/webhooks \ -H "Authorization: Bearer etchtbt_bot_3f9a1c7d2e5b48a06c1f9d3e7b2a5c80d4f6e1b93a7c2d58" ``` Ответ: ```json { "data": [ { "id": 7, "url": "https://crm.example.com/hooks/etochat", "events": [], "active": true, "last_ok_at": "2026-10-23 18:40:12", "last_error": null, "created_at": "2026-10-20 12:00:00" } ] } ``` #### Подключить вебхук `POST /telegram/webhooks` Добавляет адрес, на который мы будем отправлять события. Адрес должен быть доступен из интернета, лучше https. До 5 адресов на бота. Без events придут все события. В ответе secret для проверки подписи — показываем его один раз, сохраните. | Параметр | Тип | Описание | | --- | --- | --- | | `url` | строка | Куда отправлять события. Обязательно. | | `events` | массив | Только эти события, например ["message.received","message.sent"]. Пусто — все. | Запрос: ```bash curl -X POST https://etochat.bot/api/v2/telegram/webhooks \ -H "Authorization: Bearer etchtbt_bot_3f9a1c7d2e5b48a06c1f9d3e7b2a5c80d4f6e1b93a7c2d58" \ -H "Content-Type: application/json" \ -d '{ "url": "https://crm.example.com/hooks/etochat" }' ``` Ответ: ```json { "status": "ok", "id": 7, "secret": "whsec_MfKQ9r8GKYqrTwjUPD8ILPZIo2LaLaSw", "events": [ "*" ] } ``` #### Отключить вебхук `POST /telegram/webhooks/{id}/delete` Удаляет адрес. Что не успели доставить, туда уже не уйдёт — заберите из журнала событий. Запрос: ```bash curl -X POST https://etochat.bot/api/v2/telegram/webhooks/7/delete \ -H "Authorization: Bearer etchtbt_bot_3f9a1c7d2e5b48a06c1f9d3e7b2a5c80d4f6e1b93a7c2d58" \ -H "Content-Type: application/json" ``` Ответ: ```json { "status": "ok" } ``` ## Ошибки Формат — RFC 9457 (`application/problem+json`), машинный код в поле `code`. | HTTP | code | Когда | | --- | --- | --- | | 401 | `unauthorized` | Заголовок Authorization не передан. | | 401 | `invalid_key` | Ключ неверен, отозван или бот отключён. | | 400 | `channel_mismatch` | Ключ выдан боту другого канала, чем указан в адресе. | | 404 | `unknown_channel` | В адресе указан несуществующий канал. | | 422 | `channel_unsupported_operation` | Канал не умеет то, о чём вы попросили. В ответе есть поля channel и operation. | | 422 | `edit_window_expired` | Время, которое канал отводит на изменение или удаление, уже вышло. | | 422 | `idempotency_key_reused` | Этот Idempotency-Key уже использован с другим телом запроса. | | 409 | `idempotency_in_flight` | Запрос с этим ключом ещё выполняется. Повторите через секунду. | | 400 | `text_required` | Не передан обязательный текст. | | 400 | `text_too_long` | Текст длиннее предела канала. | | 400 | `recipient_required` | Не указан ни chat_id, ни external_id. | | 400 | `chat_id_invalid` | chat_id должен быть числом. | | 400 | `bad_status` | В status передано не open и не closed. | | 404 | `chat_not_found` | Диалога нет: человек ещё не писал боту. | | 404 | `external_id_not_linked` | Такой external_id ни с кем не связан — сначала вызовите /contacts/link. | | 400 | `message_id_invalid` | Идентификатор сообщения в адресе должен быть числом. | | 404 | `message_not_found` | Сообщение не найдено или отправлено не этим ботом. | | 400 | `external_id_required` | Не передан external_id. | | 400 | `external_id_too_long` | external_id длиннее 190 символов. | | 404 | `no_flow` | Нет включённого сценария с триггером «Из другой автоматизации». | | 402 | `subscription_inactive` | Подписка неактивна — отправка невозможна. | | 409 | `recipient_blocked_bot` | Человек заблокировал бота — написать нельзя, пока он не напишет сам. | | 429 | `rate_limited` | Превышен лимит запросов в минуту. См. заголовок Retry-After. | | 502 | `channel_send_failed` | Мессенджер не принял сообщение. Повторите с тем же Idempotency-Key. | | 502 | `channel_edit_failed` | Мессенджер не принял правку сообщения. | | 502 | `channel_delete_failed` | Мессенджер не принял удаление сообщения. | | 500 | `trigger_failed` | Сбой на нашей стороне при запуске сценария. Повторите с тем же Idempotency-Key. | | 400 | `attachment_type_invalid` | Неизвестный type файла. Можно: photo, document, video, audio, voice, video_note. | | 400 | `attachment_url_required` | Не передана ссылка на файл attachment.url. | | 400 | `attachment_url_invalid` | Ссылка на файл не подходит: нужна http- или https-ссылка на адрес в интернете. | | 400 | `attachment_download_failed` | Не смогли скачать файл по ссылке. Нужна прямая ссылка без авторизации и редиректов. | | 400 | `attachment_type_mismatch` | Для photo прислали не картинку. Другие файлы отправляйте как document. | | 400 | `file_upload_failed` | Файл не загрузился. Проверьте, что запрос — multipart/form-data с полем file. | | 413 | `file_too_large` | Файл больше предела канала для этого типа. | | 400 | `file_type_forbidden` | Такие файлы не пересылаем: исполняемые, HTML и SVG. | | 404 | `file_not_found` | В сообщении нет файла или сообщение не этого бота. | | 400 | `bad_mode` | В mode передано не human и не bot. | | 400 | `bad_minutes` | minutes — от 1 до 43200. | | 400 | `webhook_url_invalid` | Адрес вебхука не подходит: нужна http- или https-ссылка на адрес в интернете. | | 400 | `webhook_event_unknown` | В events есть событие, которого у нас нет. | | 409 | `webhook_limit` | У бота уже 5 вебхуков — удалите лишний. | ## Вебхуки Вебхук — это когда мы сами отправляем вам событие, как только оно случилось: человек написал, бот или оператор ответил, нажали кнопку, поставили тег. Так ваша CRM видит всю переписку, в том числе ответы сценариев и ИИ, и опрашивать нас не нужно. Приходят события из личной переписки с ботом. Сообщения в группах и каналах сюда не попадают. - Подключить в кабинете: бот → «Настройки» → «Для разработчиков» → «Вебхуки». Укажите адрес — будут приходить все события. - Или через API: POST /telegram/webhooks. Там можно выбрать только нужные события. - Адрес должен быть доступен из интернета, лучше https. До 5 адресов на бота. - После подключения покажем секрет `whsec_…` — им проверяется подпись. Показываем один раз, сохраните. Так выглядит событие: ```json { "id": "0b6f3c1e-8d2a-4f57-9a41-2c7e5d9b8f10", "type": "message.received", "created_at": "2026-10-23T18:44:32+03:00", "bot_id": 42, "channel": "telegram", "data": { "chat": { "chat_id": "9995735656", "name": "Иван", "username": "ivan", "business": false }, "message": { "id": 16671, "direction": "in", "type": "voice", "text": "", "attachments": [ { "type": "voice", "mime_type": "audio/ogg", "duration": 7, "url": "https://etochat.bot/api/v2/telegram/files/16671" } ], "sent_by": "person", "sent_at": "2026-10-23 18:44:31" } } } ``` ### Что приходит POST с JSON. Тип события — в `type`, человек — в `data.chat`, сообщение — в `data.message`. `id` события одинаковый на всех повторах. Файлы в само событие не кладём: в `attachments[].url` ссылка, скачайте её с вашим ключом — Скачать файл из сообщения. Ответьте любым кодом 2xx в течение 10 секунд. Если обработка долгая, сначала ответьте, потом обрабатывайте. - `message.received` — Человек написал (текст, голосовое, фото, файл…) data: `{"chat":{"chat_id":"9995735656","name":"Иван","username":"ivan","business":false},"message":{"id":16671,"direction":"in","type":"voice","text":"","attachments":[{"type":"voice","mime_type":"audio/ogg","duration":7,"url":"https://etochat.bot/api/v2/telegram/files/16671"}],"sent_by":"person","sent_at":"2026-10-23 18:44:31"}}` - `message.sent` — Человеку ушло сообщение: от сценария, ИИ, оператора, рассылки или через API data: `{"chat":{"chat_id":"9995735656","name":"Иван","username":"ivan","business":false},"message":{"id":16672,"direction":"out","type":"text","text":"Записала вас на пятницу, 15:00.","attachments":[],"sent_by":"ai","sent_at":"2026-10-23 18:44:40"}}` - `button.clicked` — Человек нажал кнопку под сообщением data: `{"chat":{"chat_id":"9995735656","name":"Иван","username":"ivan","business":false},"button":{"data":"b_price"}}` - `chat.started` — Новый человек написал первый раз. В start_param — метка ссылки t.me/ваш_бот?start=…, по которой он пришёл data: `{"chat":{"chat_id":"9995735656","name":"Иван","username":"ivan","business":false},"start_param":"reels_42","message":{"id":16601,"direction":"in","type":"text","text":"/start reels_42","attachments":[],"sent_by":"person","sent_at":"2026-10-23 18:40:02"}}` - `link.clicked` — Человек перешёл по отслеживаемой ссылке из сообщения data: `{"chat":{"chat_id":"9995735656","name":"Иван","username":"ivan","business":false},"link":{"url":"https://example.com/price","flow_id":12,"block_id":"n_k2x9p1"}}` - `tag.added` — Человеку поставили тег (сценарий, ИИ или оператор) data: `{"chat":{"chat_id":"9995735656","name":"Иван","username":"ivan","business":false},"tag":"заявка"}` - `tag.removed` — С человека сняли тег data: `{"chat":{"chat_id":"9995735656","name":"Иван","username":"ivan","business":false},"tag":"новый"}` - `variable.set` — Сохранилась переменная: ответ на вопрос, телефон, номер бонуса, метка ссылки data: `{"chat":{"chat_id":"9995735656","name":"Иван","username":"ivan","business":false},"variable":{"name":"phone","value":"+79000000000"}}` ### Кто отправил сообщение У исходящих сообщений в `sent_by` написано, кто ответил человеку. По нему CRM понимает, что бот уже ответил, и не отвечает второй. - `person` — человек — входящее сообщение - `automation` — сценарий - `ai` — ИИ-агент - `operator` — сотрудник из кабинета - `api` — вы через API - `app` — вы ответили сами из Telegram — через Telegram Business - `broadcast` — рассылка - `bot` — другие автоматические ответы бота ### Проверка подписи В каждом запросе три заголовка: `webhook-id` — номер события, `webhook-timestamp` — время отправки, `webhook-signature` — подпись. Подпись — HMAC-SHA256 от строки `id.timestamp.тело` в base64. Ключ — часть секрета после `whsec_`, раскодированная из base64. Это формат Standard Webhooks, для него есть готовые библиотеки `standardwebhooks` для PHP, Node.js, Python, Go и других языков — можно взять их. PHP: ```php // секрет — из кабинета или из ответа POST /webhooks $secret = 'whsec_…'; $key = base64_decode(substr($secret, 6)); $id = $_SERVER['HTTP_WEBHOOK_ID']; $ts = $_SERVER['HTTP_WEBHOOK_TIMESTAMP']; $body = file_get_contents('php://input'); $hmac = hash_hmac('sha256', "$id.$ts.$body", $key, true); $expected = 'v1,' . base64_encode($hmac); $ok = false; $sigs = explode(' ', $_SERVER['HTTP_WEBHOOK_SIGNATURE']); foreach ($sigs as $sig) { if (hash_equals($expected, $sig)) $ok = true; } if (!$ok || abs(time() - (int)$ts) > 300) { http_response_code(401); exit; } $event = json_decode($body, true); // уже обработали событие с этим $id — пропустите http_response_code(200); ``` Node.js: ```js const crypto = require('crypto'); // body — тело запроса строкой, как пришло (до JSON.parse) function verify(secret, headers, body) { const key = Buffer.from(secret.slice(6), 'base64'); const id = headers['webhook-id']; const ts = headers['webhook-timestamp']; const expected = 'v1,' + crypto.createHmac('sha256', key) .update(`${id}.${ts}.${body}`).digest('base64'); const ok = headers['webhook-signature'].split(' ').includes(expected); return ok && Math.abs(Date.now() / 1000 - Number(ts)) < 300; } ``` Python: ```py import base64, hashlib, hmac, time # body — тело запроса байтами, как пришло def verify(secret: str, headers, body: bytes) -> bool: key = base64.b64decode(secret[6:]) msg = f"{headers['webhook-id']}.{headers['webhook-timestamp']}.".encode() + body expected = "v1," + base64.b64encode(hmac.new(key, msg, hashlib.sha256).digest()).decode() ok = any(hmac.compare_digest(expected, s) for s in headers["webhook-signature"].split(" ")) return ok and abs(time.time() - int(headers["webhook-timestamp"])) < 300 ``` ### Повторы и дубли Не получили 2xx — повторим через 10 секунд, минуту, 5 минут, 30 минут, 2, 6, 12 и 24 часа. Всего 9 попыток, почти двое суток. У повтора тот же `webhook-id`, по нему отсекайте дубли. Порядок доставки не гарантирован: если важна последовательность, сортируйте по `created_at`. Последние доставки и ошибки видно в кабинете, в разделе «Для разработчиков». ### Если ваш сервер был недоступен Все события лежат в журнале 14 дней. Запоминайте `cursor` последнего обработанного события и дочитывайте пропущенное: GET /telegram/events?after=…. Можно обойтись и без вебхука — забирать события из журнала раз в минуту. Дочитать пропущенное: ```bash curl -X GET https://etochat.bot/api/v2/telegram/events?after=88213 \ -H "Authorization: Bearer etchtbt_bot_3f9a1c7d2e5b48a06c1f9d3e7b2a5c80d4f6e1b93a7c2d58" ``` ### Бот или ваш сотрудник Если в диалог вступает ваш сотрудник или ваш ИИ, переключите диалог: POST /telegram/chats/{chat_id}/automation с `mode = human`. Наши сценарии и ИИ в этом диалоге замолчат, а сообщения человека продолжат приходить вам. Вернуть боту — `mode = bot`. Или диалог вернётся сам, когда выйдет срок. ### Дублирование входящих апдейтов В том же разделе кабинета есть «Дублирование входящих апдейтов». Оно пересылает на ваш адрес то, что прислал сам Telegram, — как есть, в формате Telegram. Подходит, если у вас уже есть свой обработчик под этот формат и нужно получать те же данные параллельно с нами. - До 3 адресов. В заголовках `X-Etochat-Bot-Id` и `X-Etochat-Bot-Type`. - Отправляем один раз, без подписи и повторов. Ответов бота и операторов там нет — только то, что пришло от Telegram. - «Сервисные вебхуки» там же присылают ответы операторов из кабинета. В вебхуках API это `message.sent` с `sent_by = operator`. - Для CRM удобнее вебхуки API: один формат для всех каналов, исходящие сообщения, подпись, повторы и журнал. ### Блок «Вебхук» в сценарии — это другое В конструкторе есть блок [«Вебхук»](https://help.etochat.bot/auto/webhook). Он отправляет данные, когда человек дошёл до этого шага сценария, и что отправить, вы выбираете сами — например, заявку с ответами на вопросы. Вебхуки API — это все события бота сразу. Можно пользоваться и тем, и другим.