#Bot-события (ONIMBOTV2*)
Приходят автоматически для всех ботов с eventMode: "fetch". Получаются через polling.
Каждое событие имеет структуру:
{
"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получает только личные сообщения и@упоминанияи свои сообщения обратно не получает, но проверка не повредит.
Диспетчер событий:
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
Новое сообщение боту (личное или @упоминание в групповом чате).
{
"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.
Корректный паттерн:
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 → скачать содержимое → расшифровать.
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 (число).
{
"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 — кто добавил бота.
{
"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.
{
"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
Реакция добавлена или удалена на сообщение бота.
{
"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.
{
"eventId": 50,
"type": "ONIMBOTV2DELETE",
"date": "2026-04-13T17:25:00+03:00",
"data": {
"bot": { "id": 42, "code": "support_bot", "type": "bot" }
}
}
#ONIMBOTV2CONTEXTGET
Пользователь открыл диалог с ботом по ссылке, в которую вложен контекст. В поле context приходят произвольные данные из этой ссылки — бот может сразу ответить с их учётом.
{
"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 выше. В сокращённых примерах показаны только ключевые поля.