Документация APIВКонтакте

API для ВКонтакте

Текст, фото, файлы, голосовые, кнопки-ссылки, правка и удаление. Видео и аудио приходят человеку файлом. Адреса на странице уже подставлены, примеры можно копировать как есть.

С чего начать

  1. Выпустите ключ в кабинете: бот → «Настройки» → «Для разработчиков».
  2. Проверьте его запросом GET /vk/me — он ничего не меняет.
  3. Отправьте сообщение: POST /vk/messages. Ответы человека и всё остальное будут приходить на ваш вебхук.
Написать можно тем, кто писал сообществу или разрешил сообщения от него. Метка из ссылки vk.me/сообщество?ref=метка сохраняется в переменную start_param.
Адрес API
https://etochat.bot/api/v2/vk
Первый запрос
curl -X GET https://etochat.bot/api/v2/vk/me \
  -H "Authorization: Bearer etchtbt_bot_3f9a1c7d2e5b48a06c1f9d3e7b2a5c80d4f6e1b93a7c2d58"

Основы

Ключ

Ключ выпускается в кабинете: бот → «Настройки» → «Для разработчиков». Передавайте его в заголовке Authorization: Bearer <ключ>.

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

Канал в адресе должен совпадать с каналом бота, иначе придёт 400 channel_mismatch.

Заголовок
Authorization: Bearer etchtbt_bot_3f9a1c7d2e5b48a06c1f9d3e7b2a5c80d4f6e1b93a7c2d58

Повторная отправка

У «Отправить сообщение» и «Запустить сценарий» передавайте заголовок Idempotency-Key — любую уникальную строку, например UUID. Если связь оборвалась и вы отправили запрос ещё раз, второго сообщения не будет: придёт результат первого с idempotent_replay: true.

Тот же ключ с другим телом запроса — ошибка 422 idempotency_key_reused.

Повтор того же запроса
{
  "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 /vk/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 символов
Подпись к файлу
до 4096 символов
Фото
до 20 МБ
Другие файлы
до 50 МБ
Кнопок-ссылок под сообщением
до 40
Изменить и удалить сообщение
в течение 24 часов после отправки
Запросов к API
до 120 в минуту на бота

Что умеет ВКонтакте

Чего здесь нет, канал не умеет — на такой запрос API ответит ошибкой 422.

Отправить текстовое сообщениеработает
Отправить фотоработает
Отправить документработает
Отправить видеочастично
Придёт файлом, а не видео с плеером.
Отправить аудиочастично
Придёт файлом, а не аудиозаписью.
Отправить голосовоеработает
Голосовое сообщение. Если ВКонтакте не примет файл, уйдёт документом.
Кнопки-ссылки под сообщениемработает
Изменить текст отправленного сообщенияработает
Удалить отправленное сообщениеработает
Показать «печатает…»работает
Прочитать перепискуиз кабинета
Данные человека: теги и переменныеработает
Скачать файл из сообщенияработает
Открыть или закрыть диалог в кабинетеработает

Бот

Проверить ключ

GET /vk/me

Какому боту принадлежит ключ. Ничего не меняет — с него удобно начать. Если can_send = false, у бота не оплачен тариф и отправка вернёт 402.

Запрос
curl -X GET https://etochat.bot/api/v2/vk/me \
  -H "Authorization: Bearer etchtbt_bot_3f9a1c7d2e5b48a06c1f9d3e7b2a5c80d4f6e1b93a7c2d58"
Ответ
{
  "bot": {
    "id": 42,
    "name": "Поддержка",
    "channel": "vk",
    "username": "my_bot"
  },
  "can_send": true,
  "rate_limit": {
    "per_minute": 120
  }
}

Диалоги

Список диалогов

GET /vk/chats

Люди, которые писали сообществу, сверху — с последней активностью. Ответ приходит страницами: передавайте next_cursor в следующий запрос, пока has_more = true.

Параметры
limitчисло
Сколько вернуть, от 1 до 100. По умолчанию 50.
cursorстрока
next_cursor из прошлого ответа.
queryстрока
Поиск по имени и username.
Запрос
curl -X GET https://etochat.bot/api/v2/vk/chats?limit=1 \
  -H "Authorization: Bearer etchtbt_bot_3f9a1c7d2e5b48a06c1f9d3e7b2a5c80d4f6e1b93a7c2d58"
Ответ
{
  "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",
      "tags": [
        "заявка"
      ],
      "automation": {
        "mode": "bot",
        "until": null
      }
    }
  ],
  "has_more": true,
  "next_cursor": "MjAyNi0xMC0yMyAxODo0NDozMXwxNzc"
}

Один диалог

GET /vk/chats/{chat_id}

Всё про одного человека: имя, теги, ваши external_id, кто сейчас ведёт диалог (automation) и переменные, которые сохранили сценарии, — ответы на вопросы, телефон, номер бонуса. Если человек пришёл по ссылке vk.me/ваше_сообщество?ref=reels_42, метка будет в переменной start_param.

Запрос
curl -X GET https://etochat.bot/api/v2/vk/chats/9995735656 \
  -H "Authorization: Bearer etchtbt_bot_3f9a1c7d2e5b48a06c1f9d3e7b2a5c80d4f6e1b93a7c2d58"
Ответ
{
  "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",
    "tags": [
      "заявка"
    ],
    "automation": {
      "mode": "bot",
      "until": null
    },
    "variables": {
      "start_param": "reels_42",
      "phone": "+79000000000"
    }
  }
}

Переписка

GET /vk/chats/{chat_id}/messages

Переписка, новые сообщения сверху, страницами — то же, что в «Сообщениях» кабинета. Кто отправил — в sent_by, файлы — в attachments со ссылкой на скачивание. file_name и file_size — если они известны.

Параметры
limitчисло
Сколько вернуть, от 1 до 100. По умолчанию 50.
cursorстрока
next_cursor из прошлого ответа.
Запрос
curl -X GET https://etochat.bot/api/v2/vk/chats/9995735656/messages?limit=2 \
  -H "Authorization: Bearer etchtbt_bot_3f9a1c7d2e5b48a06c1f9d3e7b2a5c80d4f6e1b93a7c2d58"
Ответ
{
  "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/vk/files/16671"
        }
      ],
      "sent_by": "person",
      "sent_at": "2026-10-23 18:44:31",
      "edited": false
    }
  ],
  "has_more": true,
  "next_cursor": "MjAyNi0xMC0yMyAxODo0NDozMXwxNjY3MQ",
  "source": "cabinet"
}

Открыть или закрыть диалог

POST /vk/chats/{chat_id}/status

Закрывает или открывает диалог в кабинете — как кнопка «Закрыть» у оператора. Человек ничего не заметит, а если напишет — диалог откроется снова. Пригодится, если ваша система сама пишет человеку и ответ не нужен: диалог не будет висеть «без ответа».

Параметры
statusстрока
open или closed. Обязательно.
Запрос
curl -X POST https://etochat.bot/api/v2/vk/chats/9995735656/status \
  -H "Authorization: Bearer etchtbt_bot_3f9a1c7d2e5b48a06c1f9d3e7b2a5c80d4f6e1b93a7c2d58" \
  -H "Content-Type: application/json" \
  -d '{
    "status": "closed"
}'
Ответ
{
  "status": "ok",
  "chat_id": "9995735656",
  "chat_status": "closed",
  "closed_at": "2026-10-23 19:32:32",
  "changed": true
}

Кто отвечает: бот или человек

POST /vk/chats/{chat_id}/automation

Передаёт диалог вашему сотруднику или вашему ИИ и обратно. mode = human — наши сценарии и ИИ-агент в этом диалоге молчат, сообщения человека по-прежнему приходят вам, отвечаете вы через «Отправить сообщение». По умолчанию на сутки, срок можно задать в minutes. mode = bot — вернуть диалог боту. Если человек нажмёт кнопку под старым сообщением бота, бот ответит.

Параметры
modeстрока
human — отвечает человек, бот молчит; bot — вернуть боту. Обязательно.
minutesчисло
Для human: на сколько минут, от 1 до 43200 (30 дней). По умолчанию 1440 — сутки.
Запрос
curl -X POST https://etochat.bot/api/v2/vk/chats/9995735656/automation \
  -H "Authorization: Bearer etchtbt_bot_3f9a1c7d2e5b48a06c1f9d3e7b2a5c80d4f6e1b93a7c2d58" \
  -H "Content-Type: application/json" \
  -d '{
    "mode": "human",
    "minutes": 120
}'
Ответ
{
  "status": "ok",
  "chat_id": "9995735656",
  "mode": "human",
  "until": "2026-10-23 21:32:32"
}

Сообщения

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

POST /vk/messages Idempotency-Key

Отправляет человеку текст или файл. Файл — ссылкой в attachment или загрузкой (multipart/form-data). Ответ 200 — ВКонтакте принял сообщение, иначе придёт 502 с причиной.

Параметры
chat_idстрока
Кому. Нужен chat_id или external_id.
external_idстрока
Ваш идентификатор человека, если вы связали его через «Связать с вашим пользователем».
textстрока
Текст. Обязателен, если нет файла; с файлом — подпись. Разметка не поддерживается — HTML-теги снимем, уйдёт обычный текст. Перенос строки — \n.
attachmentобъект
Файл ссылкой: {"type":"photo","url":"https://…"}. type: photo, document, video, audio, voice. Ссылка прямая, без авторизации и редиректов. file_name — как файл назовётся у человека. Видео и аудио придут человеку файлом.
fileфайл
Файл загрузкой вместо ссылки: multipart/form-data с полями file, type, chat_id и, если нужно, text.
buttonsмассив
Кнопки-ссылки: [{"text":"…","url":"https://…"}], до 40 штук. Кнопки с ответами делаются в сценарии — запустите его методом «Запустить сценарий».
Запрос
curl -X POST https://etochat.bot/api/v2/vk/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/vk/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": "photo",
        "url": "https://example.com/orders/1043.jpg"
    }
}'

# Файл загрузкой
curl -X POST https://etochat.bot/api/v2/vk/messages \
  -H "Authorization: Bearer etchtbt_bot_3f9a1c7d2e5b48a06c1f9d3e7b2a5c80d4f6e1b93a7c2d58" \
  -F chat_id=9995735656 \
  -F type=document \
  -F "text=Прайс на октябрь" \
  -F file=@price.pdf
Ответ
{
  "status": "ok",
  "message_id": 16672,
  "chat_id": "9995735656",
  "channel": "vk",
  "type": "text"
}

Изменить сообщение

POST /vk/messages/{id}/edit

Меняет текст сообщения, которое отправил этот бот. id — message_id из ответа «Отправить сообщение» или id из переписки. Можно в течение 24 часов после отправки. Позже придёт 422 edit_window_expired.

Параметры
textстрока
Новый текст. Обязательно.
Запрос
curl -X POST https://etochat.bot/api/v2/vk/messages/16672/edit \
  -H "Authorization: Bearer etchtbt_bot_3f9a1c7d2e5b48a06c1f9d3e7b2a5c80d4f6e1b93a7c2d58" \
  -H "Content-Type: application/json" \
  -d '{
    "text": "Ваш заказ №1043 доставлен."
}'
Ответ
{
  "status": "ok",
  "message_id": 16672
}

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

POST /vk/messages/{id}/delete

Удаляет сообщение бота у человека и в кабинете. Сроки те же, что у изменения. Если ВКонтакте откажет, придёт 502 channel_delete_failed, а в кабинете сообщение останется.

Запрос
curl -X POST https://etochat.bot/api/v2/vk/messages/16672/delete \
  -H "Authorization: Bearer etchtbt_bot_3f9a1c7d2e5b48a06c1f9d3e7b2a5c80d4f6e1b93a7c2d58" \
  -H "Content-Type: application/json"
Ответ
{
  "status": "ok",
  "message_id": 16672
}

Показать «печатает…»

POST /vk/typing

Показывает человеку «печатает…». Пропадёт сам через несколько секунд или когда придёт сообщение.

Параметры
chat_idстрока
Кому. Нужен chat_id или external_id.
external_idстрока
Ваш идентификатор человека.
Запрос
curl -X POST https://etochat.bot/api/v2/vk/typing \
  -H "Authorization: Bearer etchtbt_bot_3f9a1c7d2e5b48a06c1f9d3e7b2a5c80d4f6e1b93a7c2d58" \
  -H "Content-Type: application/json" \
  -d '{
    "chat_id": "9995735656"
}'
Ответ
{
  "status": "ok"
}

Скачать файл из сообщения

GET /vk/files/{message_id}

Отдаёт файл из сообщения: голосовое, фото, документ, видео — и присланные человеком, и отправленные ему. Ссылка на него приходит в attachments[].url — в вебхуке и в переписке. Скачивайте с вашим ключом в заголовке Authorization.

Запрос
curl -X GET https://etochat.bot/api/v2/vk/files/16671 \
  -H "Authorization: Bearer etchtbt_bot_3f9a1c7d2e5b48a06c1f9d3e7b2a5c80d4f6e1b93a7c2d58" \
  -o file
ОтветСам файл. Тип — в заголовке Content-Type.

Сценарии

Запустить сценарий

POST /vk/trigger Idempotency-Key

Запускает сценарий из конструктора для одного человека. Удобно для уведомлений: данные передаёте переменными, а текст, кнопки и логика живут в конструкторе — их можно менять без правки вашего кода. Нужен включённый сценарий с триггером «Из другой автоматизации», иначе придёт 404 no_flow. В сценарии переменные доступны как {{var.имя}}. В ответе outcome: handled — сценарий отработал, operator — сценарий передал диалог оператору.

Параметры
chat_idстрока
Для кого. Нужен chat_id или external_id.
external_idстрока
Ваш идентификатор человека.
varsобъект
Данные для сценария. Имена — латиница, цифры и подчёркивание; до 20 переменных, значение до 500 символов.
Запрос
curl -X POST https://etochat.bot/api/v2/vk/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"
    }
}'
Ответ
{
  "status": "ok",
  "outcome": "handled",
  "chat_id": "9995735656"
}

Контакты

Связать с вашим пользователем

POST /vk/contacts/link

Связывает человека с идентификатором в вашей системе, например с номером клиента в CRM. Дальше вместо chat_id можно передавать external_id. Повторный вызов с тем же external_id перевесит связку на другой диалог.

Параметры
chat_idстрока
Наш идентификатор диалога. Обязательно.
external_idстрока
Ваш идентификатор, до 190 символов. Большие и маленькие буквы различаются. Обязательно.
Запрос
curl -X POST https://etochat.bot/api/v2/vk/contacts/link \
  -H "Authorization: Bearer etchtbt_bot_3f9a1c7d2e5b48a06c1f9d3e7b2a5c80d4f6e1b93a7c2d58" \
  -H "Content-Type: application/json" \
  -d '{
    "chat_id": "9995735656",
    "external_id": "user-42"
}'
Ответ
{
  "linked": true,
  "external_id": "user-42",
  "chat_id": "9995735656"
}

События и вебхуки

Журнал событий

GET /vk/events

Те же события, что уходят вебхуком, списком от старых к новым. Храним 14 дней. Пригодится, если ваш сервер был недоступен, или если вебхук поднимать не хочется и проще забирать события раз в минуту. Запомните cursor последнего обработанного события и в следующий раз передайте его в after — придёт всё, что было после.

Параметры
afterстрока
cursor последнего события, которое вы уже обработали. Без него — с самого старого.
limitчисло
Сколько вернуть, от 1 до 100. По умолчанию 100.
typesстрока
Только эти события, через запятую, например message.received,message.sent.
Запрос
curl -X GET https://etochat.bot/api/v2/vk/events?after=88213 \
  -H "Authorization: Bearer etchtbt_bot_3f9a1c7d2e5b48a06c1f9d3e7b2a5c80d4f6e1b93a7c2d58"
Ответ
{
  "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": "vk",
      "data": {
        "chat": {
          "chat_id": "9995735656",
          "name": "Иван",
          "username": "ivan"
        },
        "message": {
          "id": 16671,
          "direction": "in",
          "type": "photo",
          "text": "Вот такой нужен",
          "attachments": [
            {
              "type": "photo",
              "mime_type": "image/jpeg",
              "url": "https://etochat.bot/api/v2/vk/files/16671"
            }
          ],
          "sent_by": "person",
          "sent_at": "2026-10-23 18:44:31"
        }
      }
    }
  ],
  "next_after": "88214",
  "has_more": false
}

Ваши вебхуки

GET /vk/webhooks

Адреса, на которые мы отправляем события этого бота, и когда туда последний раз удалось доставить. Секрет не показываем.

Запрос
curl -X GET https://etochat.bot/api/v2/vk/webhooks \
  -H "Authorization: Bearer etchtbt_bot_3f9a1c7d2e5b48a06c1f9d3e7b2a5c80d4f6e1b93a7c2d58"
Ответ
{
  "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 /vk/webhooks

Добавляет адрес, на который мы будем отправлять события. Адрес должен быть доступен из интернета, лучше https. До 5 адресов на бота. Без events придут все события. В ответе secret для проверки подписи — показываем его один раз, сохраните.

Параметры
urlстрока
Куда отправлять события. Обязательно.
eventsмассив
Только эти события, например ["message.received","message.sent"]. Пусто — все.
Запрос
curl -X POST https://etochat.bot/api/v2/vk/webhooks \
  -H "Authorization: Bearer etchtbt_bot_3f9a1c7d2e5b48a06c1f9d3e7b2a5c80d4f6e1b93a7c2d58" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://crm.example.com/hooks/etochat"
}'
Ответ
{
  "status": "ok",
  "id": 7,
  "secret": "whsec_MfKQ9r8GKYqrTwjUPD8ILPZIo2LaLaSw",
  "events": [
    "*"
  ]
}

Отключить вебхук

POST /vk/webhooks/{id}/delete

Удаляет адрес. Что не успели доставить, туда уже не уйдёт — заберите из журнала событий.

Запрос
curl -X POST https://etochat.bot/api/v2/vk/webhooks/7/delete \
  -H "Authorization: Bearer etchtbt_bot_3f9a1c7d2e5b48a06c1f9d3e7b2a5c80d4f6e1b93a7c2d58" \
  -H "Content-Type: application/json"
Ответ
{
  "status": "ok"
}

Как работают вебхуки

Вебхук — это когда мы сами отправляем вам событие, как только оно случилось: человек написал, бот или оператор ответил, нажали кнопку, поставили тег. Так ваша CRM видит всю переписку, в том числе ответы сценариев и ИИ, и опрашивать нас не нужно.

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

  • Подключить в кабинете: бот → «Настройки» → «Для разработчиков» → «Вебхуки». Укажите адрес — будут приходить все события.
  • Или через API: POST /vk/webhooks. Там можно выбрать только нужные события.
  • Адрес должен быть доступен из интернета, лучше https. До 5 адресов на бота.
  • После подключения покажем секрет whsec_… — им проверяется подпись. Показываем один раз, сохраните.
Так выглядит событие
{
  "id": "0b6f3c1e-8d2a-4f57-9a41-2c7e5d9b8f10",
  "type": "message.received",
  "created_at": "2026-10-23T18:44:32+03:00",
  "bot_id": 42,
  "channel": "vk",
  "data": {
    "chat": {
      "chat_id": "9995735656",
      "name": "Иван",
      "username": "ivan"
    },
    "message": {
      "id": 16671,
      "direction": "in",
      "type": "photo",
      "text": "Вот такой нужен",
      "attachments": [
        {
          "type": "photo",
          "mime_type": "image/jpeg",
          "url": "https://etochat.bot/api/v2/vk/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"
  },
  "message": {
    "id": 16671,
    "direction": "in",
    "type": "photo",
    "text": "Вот такой нужен",
    "attachments": [
      {
        "type": "photo",
        "mime_type": "image/jpeg",
        "url": "https://etochat.bot/api/v2/vk/files/16671"
      }
    ],
    "sent_by": "person",
    "sent_at": "2026-10-23 18:44:31"
  }
}
message.sentЧеловеку ушло сообщение: от сценария, ИИ, оператора, рассылки или через APIПример
data
{
  "chat": {
    "chat_id": "9995735656",
    "name": "Иван",
    "username": "ivan"
  },
  "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"
  },
  "button": {
    "data": "b_price"
  }
}
chat.startedНовый человек написал первый раз. Метка ссылки, по которой он пришёл, приходит отдельно — событием variable.set с именем start_paramПример
data
{
  "chat": {
    "chat_id": "9995735656",
    "name": "Иван",
    "username": "ivan"
  },
  "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": "9995735656",
    "name": "Иван",
    "username": "ivan"
  },
  "link": {
    "url": "https://example.com/price",
    "flow_id": 12,
    "block_id": "n_k2x9p1"
  }
}
tag.addedЧеловеку поставили тег (сценарий, ИИ или оператор)Пример
data
{
  "chat": {
    "chat_id": "9995735656",
    "name": "Иван",
    "username": "ivan"
  },
  "tag": "заявка"
}
tag.removedС человека сняли тегПример
data
{
  "chat": {
    "chat_id": "9995735656",
    "name": "Иван",
    "username": "ivan"
  },
  "tag": "новый"
}
variable.setСохранилась переменная: ответ на вопрос, телефон, номер бонуса, метка ссылкиПример
data
{
  "chat": {
    "chat_id": "9995735656",
    "name": "Иван",
    "username": "ivan"
  },
  "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 и других языков — можно взять их.

// секрет — из кабинета или из ответа 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);

Повторы и дубли

Не получили 2xx — повторим через 10 секунд, минуту, 5 минут, 30 минут, 2, 6, 12 и 24 часа. Всего 9 попыток, почти двое суток.

У повтора тот же webhook-id, по нему отсекайте дубли. Порядок доставки не гарантирован: если важна последовательность, сортируйте по created_at.

Последние доставки и ошибки видно в кабинете, в разделе «Для разработчиков».

Если ваш сервер был недоступен

Все события лежат в журнале 14 дней. Запоминайте cursor последнего обработанного события и дочитывайте пропущенное: GET /vk/events?after=….

Можно обойтись и без вебхука — забирать события из журнала раз в минуту.

Дочитать пропущенное
curl -X GET https://etochat.bot/api/v2/vk/events?after=88213 \
  -H "Authorization: Bearer etchtbt_bot_3f9a1c7d2e5b48a06c1f9d3e7b2a5c80d4f6e1b93a7c2d58"

Бот или ваш сотрудник

Если в диалог вступает ваш сотрудник или ваш ИИ, переключите диалог: POST /vk/chats/{chat_id}/automation с mode = human. Наши сценарии и ИИ в этом диалоге замолчат, а сообщения человека продолжат приходить вам.

Вернуть боту — mode = bot. Или диалог вернётся сам, когда выйдет срок.

Блок «Вебхук» в сценарии — это другое

В конструкторе есть блок «Вебхук». Он отправляет данные, когда человек дошёл до этого шага сценария, и что отправить, вы выбираете сами — например, заявку с ответами на вопросы.

Вебхуки API — это все события бота сразу. Можно пользоваться и тем, и другим.

Ошибки

Ошибки приходят в формате application/problem+json. Разбирайте поле code — оно не меняется, а текст в title мы можем переформулировать.

unauthorized
401
Заголовок Authorization не передан.
invalid_key
401
Ключ неверен, отозван или бот отключён.
channel_mismatch
400
Ключ выдан боту другого канала, чем указан в адресе.
unknown_channel
404
В адресе указан несуществующий канал.
channel_unsupported_operation
422
Канал не умеет то, о чём вы попросили. В ответе есть поля channel и operation.
edit_window_expired
422
Время, которое канал отводит на изменение или удаление, уже вышло.
idempotency_key_reused
422
Этот Idempotency-Key уже использован с другим телом запроса.
idempotency_in_flight
409
Запрос с этим ключом ещё выполняется. Повторите через секунду.
text_required
400
Не передан обязательный текст.
text_too_long
400
Текст длиннее предела канала.
recipient_required
400
Не указан ни chat_id, ни external_id.
chat_id_invalid
400
chat_id должен быть числом.
bad_status
400
В status передано не open и не closed.
chat_not_found
404
Диалога нет: человек ещё не писал боту.
external_id_not_linked
404
Такой external_id ни с кем не связан — сначала вызовите /contacts/link.
message_id_invalid
400
Идентификатор сообщения в адресе должен быть числом.
message_not_found
404
Сообщение не найдено или отправлено не этим ботом.
external_id_required
400
Не передан external_id.
external_id_too_long
400
external_id длиннее 190 символов.
no_flow
404
Нет включённого сценария с триггером «Из другой автоматизации».
subscription_inactive
402
Подписка неактивна — отправка невозможна.
recipient_blocked_bot
409
Человек заблокировал бота — написать нельзя, пока он не напишет сам.
rate_limited
429
Превышен лимит запросов в минуту. См. заголовок Retry-After.
channel_send_failed
502
Мессенджер не принял сообщение. Повторите с тем же Idempotency-Key.
channel_edit_failed
502
Мессенджер не принял правку сообщения.
channel_delete_failed
502
Мессенджер не принял удаление сообщения.
trigger_failed
500
Сбой на нашей стороне при запуске сценария. Повторите с тем же Idempotency-Key.
attachment_type_invalid
400
Неизвестный type файла. Можно: photo, document, video, audio, voice.
attachment_url_required
400
Не передана ссылка на файл attachment.url.
attachment_url_invalid
400
Ссылка на файл не подходит: нужна http- или https-ссылка на адрес в интернете.
attachment_download_failed
400
Не смогли скачать файл по ссылке. Нужна прямая ссылка без авторизации и редиректов.
attachment_type_mismatch
400
Для photo прислали не картинку. Другие файлы отправляйте как document.
file_upload_failed
400
Файл не загрузился. Проверьте, что запрос — multipart/form-data с полем file.
file_too_large
413
Файл больше предела канала для этого типа.
file_type_forbidden
400
Такие файлы не пересылаем: исполняемые, HTML и SVG.
file_not_found
404
В сообщении нет файла или сообщение не этого бота.
bad_mode
400
В mode передано не human и не bot.
bad_minutes
400
minutes — от 1 до 43200.
webhook_url_invalid
400
Адрес вебхука не подходит: нужна http- или https-ссылка на адрес в интернете.
webhook_event_unknown
400
В events есть событие, которого у нас нет.
webhook_limit
409
У бота уже 5 вебхуков — удалите лишний.

Подключите ВКонтакте
к своей CRM

Получить ключ Поговорить с нами

Ключ — в кабинете: бот → «Настройки» → «Для разработчиков»