For AI agents: markdown of this page — /docs-content-en/chats/events/poll.md documentation index — /llms.txt
Get events
GET /v1/chats/events
Returns accumulated messenger user events starting from the specified position. The offset parameter is required — pass 0 on the first call, then nextOffset from each response.
Requires a prior subscription. Without an active subscription, events are not accumulated and the response will be empty.
The generic filter parameter is not applied by this endpoint. By default, the API preserves the previous HTTP 200 response and data, adds Warning: 299 - "filter is not supported on this endpoint and was ignored", and records unsupported-filter use in the server log.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
Parameteroffset (query) |
Typenumber | Requiredyes | Default— | DescriptionStarting position. First call: 0. Subsequent calls: the nextOffset value from the previous response |
Parameterlimit (query) |
Typenumber | Requiredno | Default100 |
DescriptionMaximum number of events per call (1–1000) |
Examples
curl — personal key
curl "https://vibecode.bitrix24.com/v1/chats/events?offset=0&limit=50" \
-H "X-Api-Key: YOUR_API_KEY"
curl — OAuth application
curl "https://vibecode.bitrix24.com/v1/chats/events?offset=0&limit=50" \
-H "X-Api-Key: YOUR_APP_KEY" \
-H "Authorization: Bearer USER_SESSION_TOKEN"
JavaScript — personal key
const res = await fetch('https://vibecode.bitrix24.com/v1/chats/events?offset=0&limit=50', {
headers: {
'X-Api-Key': 'YOUR_API_KEY',
},
})
const { success, data } = await res.json()
console.log('Events:', data.events.length, 'Next offset:', data.nextOffset)
JavaScript — OAuth application
const res = await fetch('https://vibecode.bitrix24.com/v1/chats/events?offset=0&limit=50', {
headers: {
'X-Api-Key': 'YOUR_APP_KEY',
'Authorization': 'Bearer USER_SESSION_TOKEN',
},
})
const { success, data } = await res.json()
Response fields
| Field | Type | Description |
|---|---|---|
Fieldsuccess |
Typeboolean | DescriptionAlways true on success |
Fielddata |
Typeobject | DescriptionResult container |
Fielddata.events |
Typearray | DescriptionArray of events |
Fielddata.events[].eventId |
Typenumber | DescriptionEvent identifier. Used as offset in the next request |
Fielddata.events[].type |
Typestring | DescriptionEvent type (ONIMV2MESSAGEADD, ONIMV2JOINCHAT, etc.) |
Fielddata.events[].date |
Typestring | DescriptionEvent date and time (ISO 8601) |
Fielddata.events[].data |
Typeobject | DescriptionEvent data; fields are in camelCase. The structure depends on type. The fields of the ONIMV2MESSAGEADD event are described in User events |
Fielddata.nextOffset |
Typenumber | DescriptionValue for the offset parameter in the next request |
Fielddata.hasMore |
Typeboolean | Descriptiontrue — there are more events beyond the current response |
Response example
There are new events:
{
"success": true,
"data": {
"events": [
{
"eventId": 101,
"type": "ONIMV2JOINCHAT",
"date": "2026-06-05T10:00:00+00:00",
"data": {
"dialogId": "chat100",
"chat": {
"id": 100,
"name": "Project chat",
"type": "open"
},
"user": {
"id": 42,
"name": "John Smith"
}
}
},
{
"eventId": 103,
"type": "ONIMV2MESSAGEADD",
"date": "2026-06-05T10:01:30+00:00",
"data": {
"message": {
"id": 500,
"chatId": 100,
"text": "Hi everyone!"
},
"chat": {
"id": 100,
"name": "Project chat"
},
"user": {
"id": 42,
"name": "John Smith"
}
}
}
],
"nextOffset": 104,
"hasMore": false
}
}
Empty response (no new events):
{
"success": true,
"data": {
"events": [],
"nextOffset": 104,
"hasMore": false
}
}
Error response example
400 — the required offset parameter was not provided:
{
"success": false,
"error": {
"code": "MISSING_PARAMS",
"message": "Query parameter `offset` is required. Pass `0` for the first call, then `nextOffset` from each response to advance the cursor."
}
}
Errors
| HTTP | Code | Description |
|---|---|---|
| HTTP200 | CodeWarning: 299 |
DescriptionThe filter is ignored by default; previous data is preserved and the warning is sent in the Warning header. |
| HTTP400 (strict mode only) | CodeUNSUPPORTED_FILTER |
DescriptionThe server mode is disabled by default; until it is enabled, the endpoint preserves HTTP 200 and the previous data. |
| HTTP400 | CodeMISSING_PARAMS |
DescriptionThe offset parameter is missing or empty |
| HTTP400 | CodeINVALID_OFFSET |
DescriptionThe offset value is not a non-negative integer (fractional, negative, leading zeros, extraneous characters) |
| HTTP400 | CodeINVALID_LIMIT |
DescriptionThe limit value is outside the range 1–1000 or is not an integer |
| HTTP403 | CodeSCOPE_DENIED |
DescriptionThe API key does not have the im scope |
| HTTP401 | CodeTOKEN_MISSING |
DescriptionThe API key has no configured tokens |
| HTTP422 | CodeBITRIX_ERROR |
DescriptionBitrix24 returned an error (error text in message) |
| HTTP502 | CodeBITRIX_UNAVAILABLE |
DescriptionBitrix24 is unavailable or returned a server error |
For the full list of common API errors, see Errors.
Event types
The type field of each event is one of the following codes:
| Event | Description |
|---|---|
EventONIMV2MESSAGEADD |
DescriptionNew message in a subscribed chat |
EventONIMV2MESSAGEUPDATE |
DescriptionMessage edited |
EventONIMV2MESSAGEDELETE |
DescriptionMessage deleted |
EventONIMV2JOINCHAT |
DescriptionA participant joined the chat |
EventONIMV2REACTIONCHANGE |
DescriptionA message reaction changed |
Known specifics
Token is required. For a personal vibe_api_… key, the X-Api-Key header is enough. For an OAuth vibe_app_… key, you must also add Authorization: Bearer <session_token> — without it the request returns 401 TOKEN_MISSING. To obtain a token for an OAuth application, see Keys and authorization.
Client-side cursor. Unlike bot event polling, this endpoint does not store the offset position in the database — the calling code manages the cursor. On the first call, pass offset=0; on subsequent calls, pass the nextOffset value from the previous response.
Request chain:
GET /v1/chats/events?offset=0 → { nextOffset: 104, hasMore: true }
GET /v1/chats/events?offset=104 → { nextOffset: 112, hasMore: false }
GET /v1/chats/events?offset=112 → { events: [], hasMore: false }
Recommended polling interval: 2–5 seconds between requests. On an empty response, do not increase the polling frequency — events will be delivered on the next call. For more on limits, see Limits and optimization.
Polling does not keep the machine online. If the loop below runs on a Black Hole server, disable auto-sleep on it. The idle timer counts inbound requests to the server, while polling only makes outbound calls — from the timer's point of view the server is idle, and after an hour it goes to sleep along with the polling process. To disable it, call PATCH /v1/infra/servers/:id/sleep with sleepAfterMinutes: null.
Polling loop (ready-to-use example):
const API_KEY = 'YOUR_API_KEY'
const BASE = 'https://vibecode.bitrix24.com/v1'
async function pollChatEvents() {
// Subscribe first — without a subscription events are not accumulated
await fetch(`${BASE}/chats/events/subscribe`, {
method: 'POST',
headers: { 'X-Api-Key': API_KEY },
})
let offset = 0
while (true) {
try {
const res = await fetch(`${BASE}/chats/events?offset=${offset}`, {
headers: { 'X-Api-Key': API_KEY },
})
const { data } = await res.json()
for (const event of data.events ?? []) {
await handleEvent(event)
}
if (data.nextOffset !== undefined) {
offset = data.nextOffset
}
if (!data.hasMore) {
await new Promise(r => setTimeout(r, 3000))
}
} catch (err) {
console.error('Polling error:', err.message)
await new Promise(r => setTimeout(r, 5000))
}
}
}
async function handleEvent(event) {
switch (event.type) {
case 'ONIMV2MESSAGEADD':
console.log('New message:', event.data.message?.text)
break
case 'ONIMV2JOINCHAT':
console.log('Participant joined the chat:', event.data.chat?.name)
break
case 'ONIMV2MESSAGEDELETE':
console.log('Message deleted:', event.data.message?.id)
break
default:
console.log('Event:', event.type)
}
}
pollChatEvents()