# API ЭТОЧАТБОТ — Чат на сайте Адрес API: `https://etochat.bot/api/v2/webchat`. Написать можно посетителю, у которого есть диалог, или сразу по вашему `auth_id` — тогда диалог создастся сам. ## Основы ### Ключ Ключ выпускается в кабинете: бот → «Настройки» → «Для разработчиков». Передавайте его в заголовке `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 /webchat/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 ``` ## Ограничения канала - Текст сообщения: до 4000 символов. - Подпись к файлу: до 4000 символов. - Фото: до 20 МБ. - Другие файлы: до 50 МБ. - Кнопок-ссылок под сообщением: до 8. - Изменить и удалить сообщение: без ограничения по времени. - Запросов к API: до 120 в минуту на бота. ## Что умеет Чат на сайте Чего здесь нет, канал не умеет — на такой запрос API ответит ошибкой 422. - Отправить текстовое сообщение — работает - Отправить фото — работает - Отправить документ — работает - Отправить видео — работает - Отправить аудио — работает - Отправить голосовое — работает - Кнопки-ссылки под сообщением — работает - Изменить текст отправленного сообщения — работает - Удалить отправленное сообщение — работает - Прочитать переписку — из кабинета - Данные человека: теги и переменные — работает - Скачать файл из сообщения — работает - Видеть посетителей сайта и кто сейчас онлайн — работает - Открыть или закрыть диалог в кабинете — работает ## Методы ### Бот #### Проверить ключ `GET /webchat/me` Какому боту принадлежит ключ. Ничего не меняет — с него удобно начать. Если can_send = false, у бота не оплачен тариф и отправка вернёт 402. Запрос: ```bash curl -X GET https://etochat.bot/api/v2/webchat/me \ -H "Authorization: Bearer etchtbt_bot_3f9a1c7d2e5b48a06c1f9d3e7b2a5c80d4f6e1b93a7c2d58" ``` Ответ: ```json { "bot": { "id": 42, "name": "Поддержка", "channel": "webchat", "username": "" }, "can_send": true, "rate_limit": { "per_minute": 120 } } ``` ### Диалоги #### Список диалогов `GET /webchat/chats` Посетители, у которых есть диалог, сверху — с последней активностью. Ответ приходит страницами: передавайте next_cursor в следующий запрос, пока has_more = true. | Параметр | Тип | Описание | | --- | --- | --- | | `limit` | число | Сколько вернуть, от 1 до 100. По умолчанию 50. | | `cursor` | строка | next_cursor из прошлого ответа. | | `query` | строка | Поиск по имени. | Запрос: ```bash curl -X GET https://etochat.bot/api/v2/webchat/chats?limit=1 \ -H "Authorization: Bearer etchtbt_bot_3f9a1c7d2e5b48a06c1f9d3e7b2a5c80d4f6e1b93a7c2d58" ``` Ответ: ```json { "data": [ { "chat_id": "5827431900215634771", "name": "Иван", "username": "", "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", "tags": [ "заявка" ], "automation": { "mode": "bot", "until": null } } ], "has_more": true, "next_cursor": "MjAyNi0xMC0yMyAxODo0NDozMXwxNzc" } ``` #### Один диалог `GET /webchat/chats/{chat_id}` Всё про одного посетителя: имя, теги, ваши external_id, кто сейчас ведёт диалог (automation) и переменные, которые сохранили сценарии, — ответы на вопросы, телефон, номер бонуса. Запрос: ```bash curl -X GET https://etochat.bot/api/v2/webchat/chats/5827431900215634771 \ -H "Authorization: Bearer etchtbt_bot_3f9a1c7d2e5b48a06c1f9d3e7b2a5c80d4f6e1b93a7c2d58" ``` Ответ: ```json { "chat": { "chat_id": "5827431900215634771", "name": "Иван", "username": "", "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", "tags": [ "заявка" ], "automation": { "mode": "bot", "until": null }, "variables": { "phone": "+79000000000" } } } ``` #### Переписка `GET /webchat/chats/{chat_id}/messages` Переписка, новые сообщения сверху, страницами — то же, что в «Сообщениях» кабинета. Кто отправил — в sent_by, файлы — в attachments со ссылкой на скачивание. file_name и file_size — если они известны. | Параметр | Тип | Описание | | --- | --- | --- | | `limit` | число | Сколько вернуть, от 1 до 100. По умолчанию 50. | | `cursor` | строка | next_cursor из прошлого ответа. | Запрос: ```bash curl -X GET https://etochat.bot/api/v2/webchat/chats/5827431900215634771/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": "photo", "text": "Вот такой нужен", "attachments": [ { "type": "photo", "mime_type": "image/jpeg", "url": "https://etochat.bot/api/v2/webchat/files/16671" } ], "sent_by": "person", "sent_at": "2026-10-23 18:44:31", "edited": false } ], "has_more": true, "next_cursor": "MjAyNi0xMC0yMyAxODo0NDozMXwxNjY3MQ", "source": "cabinet" } ``` #### Открыть или закрыть диалог `POST /webchat/chats/{chat_id}/status` Закрывает или открывает диалог в кабинете — как кнопка «Закрыть» у оператора. Посетитель ничего не заметит, а если напишет — диалог откроется снова. Пригодится, если ваша система сама пишет человеку и ответ не нужен: диалог не будет висеть «без ответа». | Параметр | Тип | Описание | | --- | --- | --- | | `status` | строка | open или closed. Обязательно. | Запрос: ```bash curl -X POST https://etochat.bot/api/v2/webchat/chats/5827431900215634771/status \ -H "Authorization: Bearer etchtbt_bot_3f9a1c7d2e5b48a06c1f9d3e7b2a5c80d4f6e1b93a7c2d58" \ -H "Content-Type: application/json" \ -d '{ "status": "closed" }' ``` Ответ: ```json { "status": "ok", "chat_id": "5827431900215634771", "chat_status": "closed", "closed_at": "2026-10-23 19:32:32", "changed": true } ``` #### Кто отвечает: бот или человек `POST /webchat/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/webchat/chats/5827431900215634771/automation \ -H "Authorization: Bearer etchtbt_bot_3f9a1c7d2e5b48a06c1f9d3e7b2a5c80d4f6e1b93a7c2d58" \ -H "Content-Type: application/json" \ -d '{ "mode": "human", "minutes": 120 }' ``` Ответ: ```json { "status": "ok", "chat_id": "5827431900215634771", "mode": "human", "until": "2026-10-23 21:32:32" } ``` ### Сообщения #### Отправить сообщение `POST /webchat/messages` Отправляет посетителю текст или файл. Кому — chat_id, ваш external_id или auth_id. Если посетителя нет на сайте, он увидит сообщение, когда вернётся; можно продублировать письмом (email_copy). | Параметр | Тип | Описание | | --- | --- | --- | | `chat_id` | строка | Кому. Нужен один из трёх: chat_id, external_id или auth_id. | | `external_id` | строка | Ваш идентификатор человека, если вы связали его через «Связать с вашим пользователем». | | `auth_id` | строка | Тот же идентификатор, что вы передаёте виджету в etochatbot('auth', {id, hash}). Подпись не нужна — запрос и так с ключом. Если диалога ещё нет, он создастся сам. | | `text` | строка | Текст. Обязателен, если нет файла; с файлом — подпись. Разметка не поддерживается — HTML-теги снимем, уйдёт обычный текст. Перенос строки — \n. | | `attachment` | объект | Файл ссылкой: {"type":"photo","url":"https://…"}. type: photo, document, video, audio, voice. Ссылка прямая, без авторизации и редиректов. file_name — как файл назовётся у человека. | | `file` | файл | Файл загрузкой вместо ссылки: multipart/form-data с полями file, type, chat_id или auth_id и, если нужно, text. | | `buttons` | массив | Кнопки-ссылки: [{"text":"…","url":"https://…"}], до 8 штук. Кнопки с ответами делаются в сценарии — запустите его методом «Запустить сценарий». | | `email` | строка | Почта посетителя. Запомним её в карточке: диалог подпишется именем, а оператор сможет ответить письмом. | | `name` | строка | Имя посетителя для карточки и подписи диалога. | | `email_copy` | да/нет | Продублировать сообщение письмом на почту посетителя. Почту берём из email или из карточки. Как прошло — в поле email_copy ответа; на саму отправку не влияет. | | `email_delay_minutes` | число | С email_copy: отправить письмо через N минут и только если посетитель за это время не вернулся в чат. | Запрос: ```bash curl -X POST https://etochat.bot/api/v2/webchat/messages \ -H "Authorization: Bearer etchtbt_bot_3f9a1c7d2e5b48a06c1f9d3e7b2a5c80d4f6e1b93a7c2d58" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: 8f14e45f-ea6c-4a1e-9b2d-7c3a1f0b5d21" \ -d '{ "auth_id": "c4ca4238a0b923820dcc509a6f75849b", "text": "Ваш заказ №1043 оплачен.", "buttons": [ { "text": "Статус доставки", "url": "https://example.com/orders/1043" } ] }' # Фото по ссылке curl -X POST https://etochat.bot/api/v2/webchat/messages \ -H "Authorization: Bearer etchtbt_bot_3f9a1c7d2e5b48a06c1f9d3e7b2a5c80d4f6e1b93a7c2d58" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: 3c2b7d90-5e1a-4f0b-8a6d-1e9f4b2c7a53" \ -d '{ "auth_id": "c4ca4238a0b923820dcc509a6f75849b", "attachment": { "type": "photo", "url": "https://example.com/orders/1043.jpg" } }' # Файл загрузкой curl -X POST https://etochat.bot/api/v2/webchat/messages \ -H "Authorization: Bearer etchtbt_bot_3f9a1c7d2e5b48a06c1f9d3e7b2a5c80d4f6e1b93a7c2d58" \ -F auth_id=c4ca4238a0b923820dcc509a6f75849b \ -F type=document \ -F "text=Прайс на октябрь" \ -F file=@price.pdf ``` Ответ: ```json { "status": "ok", "message_id": 16672, "chat_id": "5827431900215634771", "channel": "webchat", "type": "text" } ``` #### Изменить сообщение `POST /webchat/messages/{id}/edit` Меняет текст сообщения, которое отправил этот бот. id — message_id из ответа «Отправить сообщение» или id из переписки. Ограничения по времени нет. | Параметр | Тип | Описание | | --- | --- | --- | | `text` | строка | Новый текст. Обязательно. | Запрос: ```bash curl -X POST https://etochat.bot/api/v2/webchat/messages/16672/edit \ -H "Authorization: Bearer etchtbt_bot_3f9a1c7d2e5b48a06c1f9d3e7b2a5c80d4f6e1b93a7c2d58" \ -H "Content-Type: application/json" \ -d '{ "text": "Ваш заказ №1043 доставлен." }' ``` Ответ: ```json { "status": "ok", "message_id": 16672 } ``` #### Удалить сообщение `POST /webchat/messages/{id}/delete` Удаляет сообщение бота в чате и в кабинете. Запрос: ```bash curl -X POST https://etochat.bot/api/v2/webchat/messages/16672/delete \ -H "Authorization: Bearer etchtbt_bot_3f9a1c7d2e5b48a06c1f9d3e7b2a5c80d4f6e1b93a7c2d58" \ -H "Content-Type: application/json" ``` Ответ: ```json { "status": "ok", "message_id": 16672 } ``` #### Скачать файл из сообщения `GET /webchat/files/{message_id}` Отдаёт файл из сообщения: голосовое, фото, документ, видео — и присланные посетителем, и отправленные ему. Ссылка на него приходит в attachments[].url — в вебхуке и в переписке. Скачивайте с вашим ключом в заголовке Authorization. Запрос: ```bash curl -X GET https://etochat.bot/api/v2/webchat/files/16671 \ -H "Authorization: Bearer etchtbt_bot_3f9a1c7d2e5b48a06c1f9d3e7b2a5c80d4f6e1b93a7c2d58" \ -o file ``` Ответ: Сам файл. Тип — в заголовке Content-Type. ### Сценарии #### Запустить сценарий `POST /webchat/trigger` Запускает сценарий из конструктора для одного человека. Удобно для уведомлений: данные передаёте переменными, а текст, кнопки и логика живут в конструкторе — их можно менять без правки вашего кода. Нужен включённый сценарий с триггером «Из другой автоматизации», иначе придёт 404 no_flow. В сценарии переменные доступны как {{var.имя}}. В ответе outcome: handled — сценарий отработал, operator — сценарий передал диалог оператору. | Параметр | Тип | Описание | | --- | --- | --- | | `chat_id` | строка | Для кого. Нужен один из трёх: chat_id, external_id или auth_id. | | `external_id` | строка | Ваш идентификатор человека. | | `auth_id` | строка | Идентификатор посетителя из etochatbot('auth'). | | `vars` | объект | Данные для сценария. Имена — латиница, цифры и подчёркивание; до 20 переменных, значение до 500 символов. | Запрос: ```bash curl -X POST https://etochat.bot/api/v2/webchat/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": "5827431900215634771" } ``` ### Контакты #### Связать с вашим пользователем `POST /webchat/contacts/link` Связывает человека с идентификатором в вашей системе, например с номером клиента в CRM. Дальше вместо chat_id можно передавать external_id. Повторный вызов с тем же external_id перевесит связку на другой диалог. Связать можно и посетителя, который ещё не писал, — по auth_id. | Параметр | Тип | Описание | | --- | --- | --- | | `chat_id` | строка | Наш идентификатор диалога. Или вместо него auth_id. | | `auth_id` | строка | Идентификатор посетителя из etochatbot('auth'). Переписка не нужна. | | `external_id` | строка | Ваш идентификатор, до 190 символов. Большие и маленькие буквы различаются. Обязательно. | Запрос: ```bash curl -X POST https://etochat.bot/api/v2/webchat/contacts/link \ -H "Authorization: Bearer etchtbt_bot_3f9a1c7d2e5b48a06c1f9d3e7b2a5c80d4f6e1b93a7c2d58" \ -H "Content-Type: application/json" \ -d '{ "chat_id": "5827431900215634771", "external_id": "user-42" }' ``` Ответ: ```json { "linked": true, "external_id": "user-42", "chat_id": "5827431900215634771" } ``` ### События и вебхуки #### Журнал событий `GET /webchat/events` Те же события, что уходят вебхуком, списком от старых к новым. Храним 14 дней. Пригодится, если ваш сервер был недоступен, или если вебхук поднимать не хочется и проще забирать события раз в минуту. Запомните cursor последнего обработанного события и в следующий раз передайте его в after — придёт всё, что было после. | Параметр | Тип | Описание | | --- | --- | --- | | `after` | строка | cursor последнего события, которое вы уже обработали. Без него — с самого старого. | | `limit` | число | Сколько вернуть, от 1 до 100. По умолчанию 100. | | `types` | строка | Только эти события, через запятую, например message.received,message.sent. | Запрос: ```bash curl -X GET https://etochat.bot/api/v2/webchat/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": "webchat", "data": { "chat": { "chat_id": "5827431900215634771", "name": "Иван", "username": "" }, "message": { "id": 16671, "direction": "in", "type": "photo", "text": "Вот такой нужен", "attachments": [ { "type": "photo", "mime_type": "image/jpeg", "url": "https://etochat.bot/api/v2/webchat/files/16671" } ], "sent_by": "person", "sent_at": "2026-10-23 18:44:31" } } } ], "next_after": "88214", "has_more": false } ``` #### Ваши вебхуки `GET /webchat/webhooks` Адреса, на которые мы отправляем события этого бота, и когда туда последний раз удалось доставить. Секрет не показываем. Запрос: ```bash curl -X GET https://etochat.bot/api/v2/webchat/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 /webchat/webhooks` Добавляет адрес, на который мы будем отправлять события. Адрес должен быть доступен из интернета, лучше https. До 5 адресов на бота. Без events придут все события. В ответе secret для проверки подписи — показываем его один раз, сохраните. | Параметр | Тип | Описание | | --- | --- | --- | | `url` | строка | Куда отправлять события. Обязательно. | | `events` | массив | Только эти события, например ["message.received","message.sent"]. Пусто — все. | Запрос: ```bash curl -X POST https://etochat.bot/api/v2/webchat/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 /webchat/webhooks/{id}/delete` Удаляет адрес. Что не успели доставить, туда уже не уйдёт — заберите из журнала событий. Запрос: ```bash curl -X POST https://etochat.bot/api/v2/webchat/webhooks/7/delete \ -H "Authorization: Bearer etchtbt_bot_3f9a1c7d2e5b48a06c1f9d3e7b2a5c80d4f6e1b93a7c2d58" \ -H "Content-Type: application/json" ``` Ответ: ```json { "status": "ok" } ``` ### Посетители сайта #### Посетители сайта `GET /webchat/visitors` Все, кто открывал сайт, в том числе те, кто ничего не написал. Сверху — с последней активностью. Написать можно тем, у кого has_dialog = true. | Параметр | Тип | Описание | | --- | --- | --- | | `online` | да/нет | Только те, кто на сайте сейчас. | | `query` | строка | Поиск по имени, почте, адресу и заголовку страницы. | | `sort` | строка | active — по последней активности (по умолчанию), views — по числу просмотров, email — сначала с почтой. | | `limit` | число | Сколько вернуть, от 1 до 100. По умолчанию 50. | | `offset` | число | Сколько пропустить. Список меняется каждые несколько секунд, поэтому страницы могут немного сдвигаться. | Запрос: ```bash curl -X GET https://etochat.bot/api/v2/webchat/visitors?online=1&limit=1 \ -H "Authorization: Bearer etchtbt_bot_3f9a1c7d2e5b48a06c1f9d3e7b2a5c80d4f6e1b93a7c2d58" ``` Ответ: ```json { "data": [ { "visitor_id": "v_8f31c0a2", "chat_id": "5827431900215634771", "name": "Ирина", "email": "irina@example.com", "verified": true, "online": true, "idle_seconds": 4, "page_views": 12, "current_url": "https://example.com/pricing", "current_title": "Тарифы", "has_dialog": true, "first_seen": "2026-10-02 11:20:41", "last_seen": "2026-10-23 16:58:03" } ], "total": 37, "with_email": 12, "with_dialog": 9, "has_more": true, "next_offset": 1 } ``` #### Кто на сайте сейчас `GET /webchat/visitors/online` Сколько людей на сайте прямо сейчас и кто они. Считаем как «Сейчас на сайте» в кабинете: посетитель онлайн, если его вкладка отмечалась в последние window_seconds секунд. | Параметр | Тип | Описание | | --- | --- | --- | | `limit` | число | Сколько записей вернуть, от 1 до 100. По умолчанию 50. Число online всегда полное. | Запрос: ```bash curl -X GET https://etochat.bot/api/v2/webchat/visitors/online \ -H "Authorization: Bearer etchtbt_bot_3f9a1c7d2e5b48a06c1f9d3e7b2a5c80d4f6e1b93a7c2d58" ``` Ответ: ```json { "online": 3, "window_seconds": 45, "data": [ { "visitor_id": "v_8f31c0a2", "chat_id": "5827431900215634771", "name": "Ирина", "email": "irina@example.com", "verified": true, "online": true, "idle_seconds": 4, "page_views": 12, "current_url": "https://example.com/pricing", "current_title": "Тарифы", "has_dialog": true, "first_seen": "2026-10-02 11:20:41", "last_seen": "2026-10-23 16:58:03" } ] } ``` #### Карточка посетителя `GET /webchat/visitors/{visitor_id}` Всё про одного посетителя и последние 50 страниц, которые он открывал, — свежая первой. IP-адрес не отдаём. Чтобы написать посетителю, возьмите chat_id и вызовите «Отправить сообщение». Запрос: ```bash curl -X GET https://etochat.bot/api/v2/webchat/visitors/v_8f31c0a2 \ -H "Authorization: Bearer etchtbt_bot_3f9a1c7d2e5b48a06c1f9d3e7b2a5c80d4f6e1b93a7c2d58" ``` Ответ: ```json { "visitor": { "visitor_id": "v_8f31c0a2", "chat_id": "5827431900215634771", "name": "Ирина", "email": "irina@example.com", "verified": true, "online": true, "idle_seconds": 4, "page_views": 12, "current_url": "https://example.com/pricing", "current_title": "Тарифы", "has_dialog": true, "first_seen": "2026-10-02 11:20:41", "last_seen": "2026-10-23 16:58:03", "referrer": "https://yandex.ru/", "geo": "Москва, RU", "user_agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_5 like Mac OS X)…", "pages": [ { "url": "https://example.com/pricing", "title": "Тарифы", "seen_at": "2026-10-23 16:57:12" }, { "url": "https://example.com/", "title": "Главная", "seen_at": "2026-10-23 16:55:48" } ] } } ``` ## Ошибки Формат — 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, ни auth_id. | | 400 | `chat_id_invalid` | chat_id должен быть числом. | | 400 | `bad_status` | В status передано не open и не closed. | | 404 | `chat_not_found` | Диалога с таким chat_id нет. | | 404 | `external_id_not_linked` | Такой external_id ни с кем не связан — сначала вызовите /contacts/link. | | 400 | `auth_id_too_long` | auth_id длиннее 64 символов. | | 400 | `visitor_id_required` | Не передан visitor_id. | | 404 | `visitor_not_found` | Посетителя с таким visitor_id у этого бота нет. | | 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` | Подписка неактивна — отправка невозможна. | | 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. | | 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 /webchat/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": "webchat", "data": { "chat": { "chat_id": "5827431900215634771", "name": "Иван", "username": "" }, "message": { "id": 16671, "direction": "in", "type": "photo", "text": "Вот такой нужен", "attachments": [ { "type": "photo", "mime_type": "image/jpeg", "url": "https://etochat.bot/api/v2/webchat/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":"5827431900215634771","name":"Иван","username":""},"message":{"id":16671,"direction":"in","type":"photo","text":"Вот такой нужен","attachments":[{"type":"photo","mime_type":"image/jpeg","url":"https://etochat.bot/api/v2/webchat/files/16671"}],"sent_by":"person","sent_at":"2026-10-23 18:44:31"}}` - `message.sent` — Человеку ушло сообщение: от сценария, ИИ, оператора, рассылки или через API data: `{"chat":{"chat_id":"5827431900215634771","name":"Иван","username":""},"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":"5827431900215634771","name":"Иван","username":""},"button":{"data":"b_price"}}` - `chat.started` — Новый человек написал первый раз data: `{"chat":{"chat_id":"5827431900215634771","name":"Иван","username":""},"start_param":null,"message":{"id":16601,"direction":"in","type":"text","text":"Здравствуйте!","attachments":[],"sent_by":"person","sent_at":"2026-10-23 18:40:02"}}` - `link.clicked` — Человек перешёл по отслеживаемой ссылке из сообщения data: `{"chat":{"chat_id":"5827431900215634771","name":"Иван","username":""},"link":{"url":"https://example.com/price","flow_id":12,"block_id":"n_k2x9p1"}}` - `tag.added` — Человеку поставили тег (сценарий, ИИ или оператор) data: `{"chat":{"chat_id":"5827431900215634771","name":"Иван","username":""},"tag":"заявка"}` - `tag.removed` — С человека сняли тег data: `{"chat":{"chat_id":"5827431900215634771","name":"Иван","username":""},"tag":"новый"}` - `variable.set` — Сохранилась переменная: ответ на вопрос, телефон, номер бонуса, метка ссылки data: `{"chat":{"chat_id":"5827431900215634771","name":"Иван","username":""},"variable":{"name":"phone","value":"+79000000000"}}` ### Кто отправил сообщение У исходящих сообщений в `sent_by` написано, кто ответил человеку. По нему CRM понимает, что бот уже ответил, и не отвечает второй. - `person` — человек — входящее сообщение - `automation` — сценарий - `ai` — ИИ-агент - `operator` — сотрудник из кабинета - `api` — вы через API - `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 /webchat/events?after=…. Можно обойтись и без вебхука — забирать события из журнала раз в минуту. Дочитать пропущенное: ```bash curl -X GET https://etochat.bot/api/v2/webchat/events?after=88213 \ -H "Authorization: Bearer etchtbt_bot_3f9a1c7d2e5b48a06c1f9d3e7b2a5c80d4f6e1b93a7c2d58" ``` ### Бот или ваш сотрудник Если в диалог вступает ваш сотрудник или ваш ИИ, переключите диалог: POST /webchat/chats/{chat_id}/automation с `mode = human`. Наши сценарии и ИИ в этом диалоге замолчат, а сообщения человека продолжат приходить вам. Вернуть боту — `mode = bot`. Или диалог вернётся сам, когда выйдет срок. ### Блок «Вебхук» в сценарии — это другое В конструкторе есть блок [«Вебхук»](https://help.etochat.bot/auto/webhook). Он отправляет данные, когда человек дошёл до этого шага сценария, и что отправить, вы выбираете сами — например, заявку с ответами на вопросы. Вебхуки API — это все события бота сразу. Можно пользоваться и тем, и другим.