[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"docs-bots\u002Fevents\u002Fpolling":3,"docs-tabs-bots\u002Fevents\u002Fpolling":6},{"content":4,"lastmod":5},"\n## Получить события (polling)\n\n`GET \u002Fv1\u002Fbots\u002F:botId\u002Fevents`\n\nОсновной механизм получения входящих сообщений и команд. Бот периодически запрашивает новые события.\n\nВайбкод хранит `lastOffset` в базе данных — при первом запросе без `offset` используется сохранённое значение. Это позволяет боту продолжить с места остановки после перезапуска.\n\n## Параметры\n\n| Параметр | Тип | Обяз. | По умолч. | Описание |\n|----------|-----|:-----:|-----------|---------|\n| `botId` (path) | number | да | — | ID бота |\n| `offset` (query) | number | нет | из БД | Начальная позиция. Без параметра — сохранённое в БД значение. `offset=0` — начать с начала |\n| `limit` (query) | number | нет | `100` | Максимальное количество событий (1-1000) |\n| `withUserEvents` (query) | boolean | нет | `false` | Включить [user-события](\u002Fdocs\u002Fbots\u002Fevents\u002Fuser-events) (ONIMV2*). Требует предварительной подписки — порядок настройки описан на странице User-события. Без подписки запрос вернёт `502 BITRIX_ERROR: User is not subscribed` |\n\n## Примеры\n\n### curl — личный ключ\n\n```bash\ncurl \"https:\u002F\u002Fvibecode.bitrix24.tech\u002Fv1\u002Fbots\u002F42\u002Fevents?limit=50\" \\\n  -H \"X-Api-Key: YOUR_API_KEY\"\n```\n\n### curl — OAuth-приложение\n\n```bash\ncurl \"https:\u002F\u002Fvibecode.bitrix24.tech\u002Fv1\u002Fbots\u002F42\u002Fevents?limit=50\" \\\n  -H \"X-Api-Key: YOUR_APP_KEY\" \\\n  -H \"Authorization: Bearer USER_SESSION_TOKEN\"\n```\n\n### JavaScript — личный ключ\n\n```javascript\nconst res = await fetch('https:\u002F\u002Fvibecode.bitrix24.tech\u002Fv1\u002Fbots\u002F42\u002Fevents?limit=50', {\n  headers: {\n    'X-Api-Key': 'YOUR_API_KEY',\n  },\n})\n\nconst { success, data } = await res.json()\nconsole.log('События:', data.events.length, 'Ещё:', data.hasMore)\n```\n\n### JavaScript — OAuth-приложение\n\n```javascript\nconst res = await fetch('https:\u002F\u002Fvibecode.bitrix24.tech\u002Fv1\u002Fbots\u002F42\u002Fevents?limit=50', {\n  headers: {\n    'X-Api-Key': 'YOUR_APP_KEY',\n    'Authorization': 'Bearer USER_SESSION_TOKEN',\n  },\n})\n\nconst { success, data } = await res.json()\n```\n\n## Поля ответа\n\n| Поле | Тип | Описание |\n|------|-----|---------|\n| `events` | array | Массив событий (см. [типы событий](\u002Fdocs\u002Fbots\u002Fevents\u002Fbot-events)) |\n| `events[].eventId` | number | ID события — передайте как `offset` в следующем запросе |\n| `events[].type` | string | Код события (`ONIMBOTV2MESSAGEADD`, `ONIMBOTV2COMMANDADD` и т.д.) |\n| `events[].date` | string | Дата и время события (ISO 8601) |\n| `events[].data` | object | Данные события (структура зависит от типа, ключи в camelCase) |\n| `nextOffset` | number | Смещение для следующего запроса |\n| `hasMore` | boolean | Есть ещё необработанные события |\n| `storedOffset` | number | Текущее сохранённое смещение в БД |\n| `persisted` | boolean | `true` если `lastOffset` в БД продвинулся (доставлено хотя бы одно событие). `false` — ответ пустой, курсор не изменился |\n| `hint` | string | Диагностическое сообщение при устойчиво пустой очереди — указывает на состояние установки на стороне Битрикс24. Счётчик пустых ответов обновляется периодически, а не на каждый запрос, поэтому подсказка появляется после нескольких периодов пустоты, а не строго на пятом запросе (при рекомендованном интервале опроса 2–5 секунд — спустя примерно пару минут непрерывно пустого опроса). Число N в тексте — количество зафиксированных периодов пустоты, а не точное число сделанных запросов; для веток кода-логики опирайтесь на `persisted` и наличие событий, а `hint` трактуйте как подсказку |\n\n## Пример ответа\n\nЕсть новые события (`persisted: true`):\n\n```json\n{\n  \"success\": true,\n  \"data\": {\n    \"events\": [\n      {\n        \"eventId\": 35,\n        \"type\": \"ONIMBOTV2MESSAGEADD\",\n        \"date\": \"2026-04-13T17:15:00+03:00\",\n        \"data\": {\n          \"dialogId\": \"chat123\",\n          \"message\": { \"id\": 1501, \"text\": \"Привет, бот!\" },\n          \"user\": { \"id\": 1, \"name\": \"Иван Петров\" }\n        }\n      }\n    ],\n    \"nextOffset\": 36,\n    \"hasMore\": false,\n    \"storedOffset\": 35,\n    \"persisted\": true\n  }\n}\n```\n\nУстойчиво пустая очередь событий (появляется поле `hint`; число в тексте — количество зафиксированных периодов пустоты, а не сделанных запросов):\n\n```json\n{\n  \"success\": true,\n  \"data\": {\n    \"events\": [],\n    \"nextOffset\": 36,\n    \"hasMore\": false,\n    \"storedOffset\": 36,\n    \"persisted\": false,\n    \"hint\": \"Events queue has stayed empty across 7 consecutive checks (the empty-poll counter is sampled periodically, not once per request, so this reflects sustained emptiness rather than the exact number of polls). Bot config: eventMode='fetch', code='support_bot'. If the bot is alive on B24 (chats receive messages, im.bot.list lists it) the most common cause is B24-side event-subscription decay — run POST \u002Fv1\u002Fbots\u002F42\u002Fresubscribe first (lightweight, preserves openline \u002F WELCOME_BOT bindings). If that does not help, verify: (1) eventMode is 'fetch' (current: 'fetch'); (2) your OAuth app's INSTALL event handler responded 200 to Bitrix24; (3) the bot was added to a chat where messages are being sent (it must be a participant for chat-message events); (4) only if all of the above are confirmed — try POST \u002Fv1\u002Fbots to re-register (destroys openline bindings; last resort).\"\n  }\n}\n```\n\n## Пример ответа при ошибке\n\n404 — бот не найден:\n\n```json\n{\n  \"success\": false,\n  \"error\": {\n    \"code\": \"BOT_NOT_FOUND\",\n    \"message\": \"Bot 999 not found. Register it first via POST \u002Fv1\u002Fbots.\"\n  }\n}\n```\n\n## Ошибки\n\n| HTTP | Код | Описание |\n|------|-----|---------|\n| 400 | `INVALID_BOT_ID` | `botId` не является числом |\n| 404 | `BOT_NOT_FOUND` | Бот с таким ID не найден |\n| 403 | `BOT_ACCESS_DENIED` | Бот принадлежит другому API-ключу |\n| 502 | `BITRIX_ERROR` | Ошибка Битрикс24 (текст ошибки в `message`) |\n| 403 | `SCOPE_DENIED` | API-ключ не имеет скоупа `imbot` |\n| 401 | `TOKEN_MISSING` | API-ключ не имеет настроенных токенов |\n\nПолный список общих ошибок API — [Ошибки](\u002Fdocs\u002Ferrors).\n\n## Известные особенности\n\n**Какой токен использовать.** Для личного ключа `vibe_api_…` достаточно заголовка `X-Api-Key`. Для ключа авторизации `vibe_app_…` обязательно добавлять `Authorization: Bearer \u003Csession_token>` — без Bearer запрос вернёт `401 TOKEN_MISSING`. Получение `session_token` для OAuth-приложения — см. [Ключи и авторизация](\u002Fdocs\u002Fkeys-auth).\n\n**Серверное хранение offset:** Вайбкод хранит `lastOffset` в базе. При первом запросе без `offset` — используется сохранённое значение. После получения событий `lastOffset` обновляется автоматически, не дожидаясь результата записи.\n\n**offset=0:** явная передача `offset=0` начинает с начала истории — для отладки или первичной загрузки.\n\n**offset=N (конкретное значение):** само событие с указанным ID попадает в ответ. Чтобы не получить дубли, всегда передавайте `nextOffset` из предыдущего ответа, а не `eventId` последнего обработанного события.\n\n**Цепочка запросов:**\n\n```\nGET \u002Fevents           → { nextOffset: 42, hasMore: true }\nGET \u002Fevents?offset=42 → { nextOffset: 55, hasMore: false }\nGET \u002Fevents?offset=55 → { events: [], hasMore: false }\n```\n\n**Рекомендуемый интервал polling:** 2-5 секунд между запросами.\n\n**Polling-цикл (готовый пример):**\n\n```javascript\nconst BOT_ID = 42\nconst API_KEY = 'YOUR_API_KEY'\nconst BASE = 'https:\u002F\u002Fvibecode.bitrix24.tech\u002Fv1'\n\nasync function pollEvents() {\n  let offset = undefined\n\n  while (true) {\n    try {\n      const url = new URL(`${BASE}\u002Fbots\u002F${BOT_ID}\u002Fevents`)\n      if (offset !== undefined) url.searchParams.set('offset', String(offset))\n\n      const res = await fetch(url, {\n        headers: { 'X-Api-Key': API_KEY },\n      })\n      const { data } = await res.json()\n\n      for (const event of data.events ?? []) {\n        await handleEvent(event)\n      }\n\n      if (data.nextOffset !== undefined) {\n        offset = data.nextOffset\n      }\n    } catch (err) {\n      console.error('Poll error:', err.message)\n    }\n\n    await new Promise(r => setTimeout(r, 3000))\n  }\n}\n\nasync function handleEvent(event) {\n  const { data } = event\n\n  switch (event.type) {\n    case 'ONIMBOTV2MESSAGEADD':\n      \u002F\u002F Ответить на сообщение\n      await fetch(`${BASE}\u002Fbots\u002F${BOT_ID}\u002Fmessages`, {\n        method: 'POST',\n        headers: { 'X-Api-Key': API_KEY, 'Content-Type': 'application\u002Fjson' },\n        body: JSON.stringify({\n          dialogId: data.chat.dialogId,\n          fields: { message: `Получил: ${data.message.text}` },\n        }),\n      })\n      break\n\n    case 'ONIMBOTV2COMMANDADD':\n      \u002F\u002F Ответить на команду\n      await fetch(`${BASE}\u002Fbots\u002F${BOT_ID}\u002Fcommands\u002F${data.command.id}\u002Fanswer`, {\n        method: 'POST',\n        headers: { 'X-Api-Key': API_KEY, 'Content-Type': 'application\u002Fjson' },\n        body: JSON.stringify({\n          dialogId: data.chat.dialogId,\n          messageId: data.message.id,\n          fields: { message: `Команда \u002F${data.command.command}: ${data.command.params}` },\n        }),\n      })\n      break\n  }\n}\n\npollEvents()\n```\n\n## Смотрите также\n\n- [Диагностика проблем](\u002Fdocs\u002Fbots\u002Ftroubleshooting)\n- [Bot-события](\u002Fdocs\u002Fbots\u002Fevents\u002Fbot-events)\n- [User-события](\u002Fdocs\u002Fbots\u002Fevents\u002Fuser-events)\n- [Отправить сообщение](\u002Fdocs\u002Fbots\u002Fmessages\u002Fsend)\n- [Ответить на команду](\u002Fdocs\u002Fbots\u002Fcommands\u002Fanswer)\n- [Бот-платформа](\u002Fdocs\u002Fbots)\n- [Лимиты и оптимизация](\u002Fdocs\u002Foptimization)\n","2026-07-09",{}]