
## Событие, доставленное вебхуком

Как выглядит тело события, которое Битрикс24 отправляет боту напрямую, какие в нём поля и чем обработчик проверяет подлинность запроса.

## Когда приходит такое событие

Режим доставки задаётся при регистрации бота — [POST /v1/bots](/docs/bots/management/create) с `eventMode: "webhook"` и адресом `webhookUrl`. Битрикс24 отправляет каждое событие POST-запросом прямо на этот адрес, платформа Вайбкод в доставке не участвует.

Опрос у такого бота остаётся пустым: [GET /v1/bots/:botId/events](/docs/bots/events/polling) отдаёт пустой массив `events`. Пока массив пуст, к ответу добавляется подсказка `hint` о том, что события ушли на `webhookUrl`. Рядом приходит `nextPollAfterMs` — его нет в ответе на запрос с `withUserEvents=true`.

Требования к адресу, время ожидания ответа, отсутствие повторной доставки и сравнение двух режимов — [Диагностика проблем](/docs/bots/troubleshooting).

## Тело запроса

Формат тела — `application/x-www-form-urlencoded`, вложенность передаётся квадратными скобками. Ниже событие «новое сообщение боту» разнесено по строкам для чтения, в запросе это одна строка без переносов.

```
event=ONIMBOTV2MESSAGEADD
&data[bot][id]=42
&data[message][id]=1501
&data[message][text]=%D0%9F%D1%80%D0%B8%D0%B2%D0%B5%D1%82%2C+%D0%B1%D0%BE%D1%82
&data[message][params][FILE_ID][0]=15423
&data[chat][id]=123
&data[chat][dialogId]=chat123
&data[chat][type]=chat
&data[user][id]=1
&auth[application_token]=customFAKE-bot-token-0123456789abcdef01
&auth[domain]=your-portal.bitrix24.ru
&auth[member_id]=a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6
```

Разобранное тело — объект с тремя ключами верхнего уровня:

```json
{
  "event": "ONIMBOTV2MESSAGEADD",
  "data": {
    "bot": { "id": "42" },
    "message": {
      "id": "1501",
      "text": "Привет, бот",
      "params": { "FILE_ID": ["15423"] }
    },
    "chat": { "id": "123", "dialogId": "chat123", "type": "chat" },
    "user": { "id": "1" }
  },
  "auth": {
    "application_token": "customFAKE-bot-token-0123456789abcdef01",
    "domain": "your-portal.bitrix24.ru",
    "member_id": "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6"
  }
}
```

## Поля тела

| Поле | Описание |
|------|---------|
| `event` | Код типа события. Тот же набор кодов, что на опросе — [Bot-события](/docs/bots/events/bot-events) |
| `data` | Данные события. Состав повторяет объект `data` одноимённого события на опросе, значения приходят строками |
| `data.bot.id` | ID бота. Он же подставляется в путь при ответе — `POST /v1/bots/:botId/messages` |
| `data.chat.dialogId` | ID диалога для ответа: `chatXXX` для группового чата, ID сотрудника для личного |
| `data.message.id` | ID сообщения — для цитирования через `replyId` |
| `data.message.params.FILE_ID` | Массив ID вложенных файлов. Скачать — [GET /files/:fileId](/docs/bots/files/download) |
| `auth.application_token` | Значение для проверки подлинности, см. раздел ниже |
| `auth.domain` | Адрес портала, с которого пришло событие |
| `auth.member_id` | Идентификатор портала в Битрикс24 |

Значения всех полей приходят строками — числа и логические значения тоже, это следствие формата тела. Поэтому `data.message.id` сравнивайте после приведения к числу, а не напрямую.

## Проверка подлинности

Адрес `webhookUrl` доступен из интернета, поэтому обработчик обязан отличать событие портала от постороннего запроса. Признак — поле `auth[application_token]`, и ожидаемое значение зависит от того, каким ключом бот зарегистрирован через [POST /v1/bots](/docs/bots/management/create).

| Ключ регистрации | Ожидаемое значение `auth[application_token]` |
|------------------|---------------------------------------------|
| Личный ключ `vibe_api_…` | Строка `custom` и токен бота подряд |
| Ключ, привязанный к OAuth-приложению | `application_token` этого приложения — тот же, по которому приложение сверяет [события подписок](/docs/infra/event-subscriptions/handler) |

Дальше в разделе разобрана первая ветка. Для второй сравнивайте с `application_token` приложения, а токен бота в сверке не участвует.

Токен бота платформа выдаёт при регистрации сама и в ответах не показывает. Чтобы получить значение для сравнения, задайте свой токен — [PATCH /v1/bots/:botId](/docs/bots/management/update) с полем `fields.botToken`: от 32 до 40 символов из алфавита `[A-Za-z0-9_-]`. Значение вне этих границ отклоняется с `400 BOT_TOKEN_INVALID`, и токен бота остаётся прежним.

Одно и то же значение приходит либо на верхнем уровне в `auth`, либо вложенным в `data[bot][auth]` — проверяйте оба места. Не совпало — отвечайте `403` и событие не обрабатывайте.

Токен бота — секрет: он же авторизует входящие события. Храните его рядом с API-ключом и не публикуйте в клиентском коде.

## Обработчик

Ниже код самого приложения, а не вызов к API Вайбкод, поэтому примера на cURL здесь нет. Пример собран под бота, зарегистрированного личным ключом: ожидаемое значение строится из токена бота. Бот зарегистрирован ключом OAuth-приложения — подставьте в `EXPECTED_TOKEN` `application_token` приложения.

```javascript
import express from 'express'

const BOT_TOKEN = process.env.BOT_TOKEN // токен, заданный через PATCH fields.botToken
const EXPECTED_TOKEN = 'custom' + BOT_TOKEN
const VIBE = 'https://vibecode.bitrix24.tech/v1'

const app = express()
app.use(express.urlencoded({ extended: true }))

app.post('/bot-events', (req, res) => {
  const incoming =
    req.body.auth?.application_token ?? req.body.data?.bot?.auth?.application_token
  if (incoming !== EXPECTED_TOKEN) return res.sendStatus(403)

  // Ответ уходит до обработки: Битрикс24 ждёт ответа ограниченное время,
  // а повторной доставки у события нет.
  res.sendStatus(200)
  handleEvent(req.body).catch((err) => console.error('Handler error:', err.message))
})

async function handleEvent({ event, data }) {
  if (event !== 'ONIMBOTV2MESSAGEADD') return
  // Собственный ответ бота приходит обратно — без этой проверки обработчик зациклится.
  if (String(data.message?.authorId) === String(data.bot?.id)) return

  await fetch(`${VIBE}/bots/${data.bot.id}/messages`, {
    method: 'POST',
    headers: { 'X-Api-Key': process.env.VIBE_API_KEY, 'Content-Type': 'application/json' },
    body: JSON.stringify({
      dialogId: data.chat.dialogId,
      fields: { message: `Получил: ${data.message.text}` },
    }),
  })
}

app.listen(3000)
```

Маршрутизация идёт по полю `event`, набор типов тот же, что на опросе. Полный отбор входящих сообщений, включая пропуск служебных, и структура `data` каждого типа — [Bot-события](/docs/bots/events/bot-events).

## Смотрите также

- [Получить события (polling)](/docs/bots/events/polling)
- [Bot-события](/docs/bots/events/bot-events)
- [Зарегистрировать бота](/docs/bots/management/create)
- [Обновить бота](/docs/bots/management/update)
- [Отправить сообщение](/docs/bots/messages/send)
- [Диагностика проблем](/docs/bots/troubleshooting)
- [Бот-платформа](/docs/bots)
