#Bot-события (ONIMBOTV2*)

Приходят автоматически для всех ботов с eventMode: "fetch". Получаются через polling.

Каждое событие имеет структуру:

JSON
{
  "eventId": 29,
  "type": "ONIMBOTV2JOINCHAT",
  "date": "2026-04-13T17:12:00+03:00",
  "data": {
    "dialogId": "chat3553",
    "bot": { ... },
    "chat": { ... },
    "user": { ... },
    "language": "ru"
  }
}
Поле Тип Описание
eventId number ID события — передайте как offset в следующем запросе polling
type string Код типа события
date string Дата и время (ISO 8601)
data object Данные события (структура зависит от типа)

#Список событий

Тип Описание
ONIMBOTV2MESSAGEADD Новое сообщение боту
ONIMBOTV2MESSAGEUPDATE Сообщение отредактировано
ONIMBOTV2MESSAGEDELETE Сообщение удалено
ONIMBOTV2JOINCHAT Бот добавлен в чат
ONIMBOTV2COMMANDADD Вызвана slash-команда
ONIMBOTV2REACTIONCHANGE Реакция на сообщение бота
ONIMBOTV2DELETE Бот удалён с портала
ONIMBOTV2CONTEXTGET Запрошен контекст диалога

#Как обрабатывать события

Цикл опроса описан на странице Получить события. Получив массив events, маршрутизируйте каждое событие по полю type. Отвечать боту нужно в диалог event.data.dialogId (для групп — chatXXX, для личных — id пользователя, тот же, что event.data.chat.dialogId).

Перед обработкой сообщения отфильтруйте лишнее:

  • message.isSystem === true — служебное сообщение (вход в чат, смена настроек). Пропускайте.
  • message.authorId === event.data.bot.id — собственное сообщение бота. Боты типа personal и supervisor получают все сообщения чата, включая свои ответы, — без этой проверки обработчик зациклится. Бот типа bot получает только личные сообщения и @упоминания и свои сообщения обратно не получает, но проверка не повредит.

Диспетчер событий:

javascript
async function handleEvent(event) {
  const { type, data } = event
  const dialogId = data.dialogId

  switch (type) {
    case 'ONIMBOTV2MESSAGEADD': {
      const m = data.message
      if (m.isSystem || m.authorId === data.bot.id) return // пропустить служебные и свои
      // Ответить: POST /v1/bots/:botId/messages с { dialogId, fields: { message } }
      break
    }
    case 'ONIMBOTV2COMMANDADD':
      // Ответить на команду: POST /v1/bots/:botId/commands/:commandId/answer
      // commandId = data.command.id, messageId = data.message.id
      break
    case 'ONIMBOTV2JOINCHAT':
      // Бот добавлен в чат — можно отправить приветствие в dialogId
      break
    case 'ONIMBOTV2REACTIONCHANGE':
      // data.reaction (код реакции) + data.action ('add' | 'delete')
      break
    case 'ONIMBOTV2CONTEXTGET':
      // Диалог открыт по ссылке с контекстом — данные в data.context
      break
    case 'ONIMBOTV2MESSAGEUPDATE':
      // Сообщение отредактировано — data.message с новым текстом
      break
    case 'ONIMBOTV2MESSAGEDELETE':
      // Сообщение удалено — data.messageId (число)
      break
    case 'ONIMBOTV2DELETE':
      // Бот удалён с портала — освободите ресурсы, прекратите опрос
      break
  }
}

Ответ в диалог — всегда `POST /v1/bots/:botId/messages` с dialogId = event.data.dialogId.


#ONIMBOTV2MESSAGEADD

Новое сообщение боту (личное или @упоминание в групповом чате).

JSON
{
  "eventId": 35,
  "type": "ONIMBOTV2MESSAGEADD",
  "date": "2026-04-13T17:15:00+03:00",
  "data": {
    "dialogId": "chat123",
    "bot": {
      "id": 42,
      "code": "support_bot",
      "type": "bot",
      "isHidden": false,
      "isReactionsEnabled": true,
      "eventMode": "fetch"
    },
    "message": {
      "id": 1501,
      "chatId": 123,
      "authorId": 1,
      "date": "2026-04-13T17:15:00+03:00",
      "text": "Привет, бот! Подскажи по задаче #42",
      "isSystem": false,
      "uuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
      "forward": null,
      "params": {
        "FILE_ID": ["15423"]
      },
      "viewedByOthers": false
    },
    "chat": {
      "id": 123,
      "dialogId": "chat123",
      "type": "chat",
      "name": "Рабочий чат",
      "owner": 1,
      "color": "#64a513",
      "entityType": "",
      "entityId": "",
      "permissions": {
        "manageUsersAdd": "member",
        "manageUsersDelete": "manager",
        "manageSettings": "owner",
        "manageMessages": "member",
        "canPost": "member"
      }
    },
    "user": {
      "id": 1,
      "active": true,
      "name": "Иван Петров",
      "firstName": "Иван",
      "lastName": "Петров",
      "workPosition": "Менеджер",
      "color": "#1eb4aa",
      "gender": "M",
      "extranet": false,
      "bot": false,
      "status": "online",
      "departments": [1, 5],
      "type": "employee"
    },
    "language": "ru"
  }
}

Поля data:

Поле Тип Описание
dialogId string ID диалога для ответа (chatXXX или id пользователя)
message.id number ID сообщения — для цитирования через replyId
message.text string Текст сообщения
message.authorId number ID автора. Сравните с bot.id, чтобы пропустить собственные сообщения
message.isSystem boolean true для служебных сообщений — пропускайте их
message.params.FILE_ID string[] ID прикреплённых файлов. Скачать: GET /files/:fileId
message.forward object | null Объект пересланного сообщения или null
bot object Объект бота (id, code, type, eventMode)
chat.entityType string Тип привязки: LINES (открытая линия), CRM, пустая строка (обычный чат)
chat.permissions object Права в чате (manageUsersAdd, manageSettings и др.)
user object Автор сообщения (id, name, departments, status и др.)
language string Язык интерфейса пользователя

#Обработка голосовых и файловых сообщений

Голосовые сообщения и файлы-вложения попадают в ONIMBOTV2MESSAGEADD со следующими особенностями:

  • У голосового сообщения message.text пуст или содержит только пояснение ("Голосовое сообщение").
  • Массив message.params.FILE_IDавторитативный источник информации о вложениях, появляется сразу в теле события.
  • GET /v1/chats/:dialogId/messages не гарантирует, что то же сообщение вернётся сразу — disk-индекс Битрикс24 иногда догоняет событие с задержкой в 1–2 секунды. Не ждите его через polling.

Корректный паттерн:

javascript
const FILE_FETCH_TIMEOUT_MS = 10_000

async function handleMessageAdd(event) {
  const { message } = event.data
  const fileIds = message.params?.FILE_ID ?? []

  // Обычное текстовое сообщение
  if (fileIds.length === 0) {
    return handleText(message)
  }

  // Есть вложение — читаем метаданные через disk API
  for (const fileId of fileIds) {
    let meta = null
    for (let attempt = 0; attempt < 2; attempt++) {
      // encodeURIComponent защищает от future-regressions если fileId когда-то
      // окажется не чисто-числовым (например, B24 поменяет формат ID или
      // этот паттерн расширят на user-provided ID из другого источника).
      const url = `https://vibecode.bitrix24.tech/v1/files/${encodeURIComponent(fileId)}`
      // AbortController защищает handler от зависания, если B24 или наш proxy
      // отвечают медленно. Без timeout одно «залипшее» событие может заблокировать poll-loop.
      const controller = new AbortController()
      const timer = setTimeout(() => controller.abort(), FILE_FETCH_TIMEOUT_MS)
      let res
      try {
        res = await fetch(url, {
          headers: { 'X-Api-Key': process.env.VIBE_KEY },
          signal: controller.signal,
        })
      } catch (err) {
        clearTimeout(timer)
        if (err.name === 'AbortError') {
          console.warn(`File ${fileId} fetch timed out after ${FILE_FETCH_TIMEOUT_MS}ms, retrying`)
          continue
        }
        throw err
      }
      clearTimeout(timer)
      if (res.ok) {
        meta = await res.json()
        break
      }
      // 403 BITRIX_ACCESS_DENIED или 404 сразу после события — типично:
      // disk ещё не проиндексировал файл. Ждём 1.5 секунды и ретраим.
      if (res.status === 403 || res.status === 404) {
        await sleep(1500)
        continue
      }
      throw new Error(`Failed to read file ${fileId}: HTTP ${res.status}`)
    }
    if (!meta) {
      // Файл всё ещё недоступен — логируем и продолжаем без attachment
      console.warn(`File ${fileId} not available after retry, skipping`)
      continue
    }
    await processAttachment(meta.data)
  }
}

const sleep = (ms) => new Promise((r) => setTimeout(r, ms))

Требуемые скоупы у API-ключа: imbot (получение событий) + disk (вызов /v1/files/:id). Без скоупа disk запрос GET /v1/files/:id возвращает SCOPE_DENIED 403. Скоуп im нужен дополнительно, если вы вызываете методы im.* напрямую (для этого паттерна не требуется).

Модель прав в Битрикс24:

  • Бот-пользователь (от чьего имени работает API-ключ) должен быть участником чата, в котором пришло сообщение. Для групповых ботов участие добавляется автоматически при установке. Для ботов типа personal и supervisor требуется настройка в интерфейсе портала (см. Зарегистрировать бота).
  • Файл-вложение доступно только в пределах срока хранения файлов в Битрикс24, который зависит от тарифа портала. После удаления файла запрос возвращает BITRIX_ACCESS_DENIED даже с корректным скоупом.
  • Скоуп disk у ключа необходим, но недостаточен — он открывает эндпоинт /v1/files/:id, но права на конкретный файл Битрикс24 проверяет отдельно по участию бота в чате.

Расшифровка голосового сообщения. Получив fileId из message.params.FILE_ID, скачайте байты файла и отправьте их на распознавание. Полная цепочка: событие → fileId → скачать содержимое → расшифровать.

javascript
async function transcribeVoice(fileId) {
  // 1. Скачать байты файла (бинарный ответ)
  const fileRes = await fetch(
    `https://vibecode.bitrix24.tech/v1/files/${encodeURIComponent(fileId)}/download`,
    { headers: { 'X-Api-Key': process.env.VIBE_KEY } },
  )
  const audio = await fileRes.blob()

  // 2. Отправить аудио на расшифровку (Whisper, бесплатно; скоуп `vibe:ai`)
  const form = new FormData()
  form.append('file', audio, 'voice.ogg')
  const trRes = await fetch('https://vibecode.bitrix24.tech/v1/audio/transcriptions', {
    method: 'POST',
    headers: { 'X-Api-Key': process.env.VIBE_KEY },
    body: form,
  })
  const { text } = await trRes.json()
  return text
}

Контракт распознавания (форматы, лимиты, коды ошибок empty_file / AI_PROVIDER_TIMEOUT) — Расшифровка аудио. Скоуп vibe:ai добавляется к ключу автоматически.


#ONIMBOTV2MESSAGEUPDATE

Сообщение отредактировано. Структура data совпадает с ONIMBOTV2MESSAGEADD: объект message содержит обновлённый text и те же поля id, authorId, params.FILE_ID. Находите исходное сообщение по message.id.


#ONIMBOTV2MESSAGEDELETE

Сообщение удалено. Вместо объекта message содержит messageId (число).

JSON
{
  "eventId": 37,
  "type": "ONIMBOTV2MESSAGEDELETE",
  "date": "2026-04-13T17:16:00+03:00",
  "data": {
    "dialogId": "chat123",
    "bot": { "id": 42, "code": "support_bot", "type": "bot" },
    "messageId": 1501,
    "chat": { "id": 123, "dialogId": "chat123", "type": "chat", "name": "Рабочий чат" },
    "user": { "id": 1, "name": "Иван Петров" }
  }
}

#ONIMBOTV2JOINCHAT

Бот добавлен в чат. user — кто добавил бота.

JSON
{
  "eventId": 29,
  "type": "ONIMBOTV2JOINCHAT",
  "date": "2026-04-13T17:12:00+03:00",
  "data": {
    "dialogId": "chat3553",
    "bot": { "id": 42, "code": "support_bot", "type": "bot", "eventMode": "fetch" },
    "chat": {
      "id": 3553,
      "dialogId": "chat3553",
      "type": "chat",
      "name": "Отдел продаж",
      "owner": 42,
      "color": "#64a513",
      "permissions": { "manageUsersAdd": "member", "manageSettings": "owner" }
    },
    "user": { "id": 3, "name": "Мария Сидорова", "workPosition": "Руководитель" },
    "language": "ru"
  }
}

#ONIMBOTV2COMMANDADD

Вызвана slash-команда бота. Содержит дополнительный объект command.

JSON
{
  "eventId": 40,
  "type": "ONIMBOTV2COMMANDADD",
  "date": "2026-04-13T17:18:00+03:00",
  "data": {
    "dialogId": "chat123",
    "bot": { "id": 42, "code": "support_bot", "type": "bot" },
    "message": { "id": 1510, "text": "/help задачи" },
    "chat": { "id": 123, "dialogId": "chat123" },
    "user": { "id": 1, "name": "Иван Петров" },
    "command": {
      "id": 7,
      "command": "help",
      "params": "задачи",
      "context": "textarea"
    }
  }
}

Поля command:

Поле Описание
id ID команды
command Текст команды без /
params Параметры, введённые после команды
context Откуда вызвана: textarea (поле ввода), keyboard (кнопка), menu (контекстное меню)

Для ответа используйте POST /commands/:commandId/answer с messageId из message.id.


#ONIMBOTV2REACTIONCHANGE

Реакция добавлена или удалена на сообщение бота.

JSON
{
  "eventId": 42,
  "type": "ONIMBOTV2REACTIONCHANGE",
  "date": "2026-04-13T17:19:00+03:00",
  "data": {
    "dialogId": "chat123",
    "bot": { "id": 42, "code": "support_bot", "type": "bot" },
    "reaction": "like",
    "action": "add",
    "message": { "id": 1502, "text": "Привет! Чем могу помочь?" },
    "chat": { "id": 123, "dialogId": "chat123" },
    "user": { "id": 1, "name": "Иван Петров" }
  }
}
Поле Описание
reaction Код реакции (см. коды реакций)
action "add" — добавлена, "delete" — удалена

#ONIMBOTV2DELETE

Бот удалён с портала. Содержит только объект bot — без chat и user.

JSON
{
  "eventId": 50,
  "type": "ONIMBOTV2DELETE",
  "date": "2026-04-13T17:25:00+03:00",
  "data": {
    "bot": { "id": 42, "code": "support_bot", "type": "bot" }
  }
}

#ONIMBOTV2CONTEXTGET

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

JSON
{
  "eventId": 45,
  "type": "ONIMBOTV2CONTEXTGET",
  "date": "2026-04-13T17:20:00+03:00",
  "data": {
    "dialogId": "5",
    "bot": { "id": 42, "code": "support_bot", "type": "bot" },
    "context": { "action": "openTask", "taskId": "456" },
    "chat": { "id": 789, "dialogId": "5", "type": "private" },
    "user": { "id": 5, "name": "Алексей Козлов" }
  }
}
Поле Описание
context Произвольные данные из ссылки, по которой открыли диалог. Приходит строкой или объектом. Битрикс24 передаёт значения строками — число 456 придёт как "456"

Событие приходит для личного диалога с ботом, поэтому dialogId — это id пользователя, открывшего ссылку.

Как боту передают контекст. Диалог с ботом открывают по ссылке с параметром BOT_CONTEXT — в него кладут произвольный JSON в URL-кодировке:

https://<portal>/online/?IM_DIALOG=<dialogId>&BOT_CONTEXT=<JSON в URL-кодировке>

<portal> — домен портала, <dialogId> — идентификатор диалога с ботом. Значение BOT_CONTEXT приходит боту в поле context этого события без изменений.


#Общие объекты в событиях

Все события (кроме ONIMBOTV2DELETE) содержат объекты bot, chat, user с одинаковой структурой. Полные объекты показаны в примере ONIMBOTV2MESSAGEADD выше. В сокращённых примерах показаны только ключевые поля.

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