
## 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](/docs/chats/events/subscribe). Without an active subscription events are not accumulated and the response will be empty.

## Parameters

| Parameter | Type | Required | Default | Description |
|----------|-----|:-----:|-----------|---------|
| `offset` (query) | number | yes | — | Starting position. First call: `0`. Subsequent calls: the `nextOffset` value from the previous response |
| `limit` (query) | number | no | `100` | Maximum number of events per call (1–1000) |

## Examples

### curl — personal key

```bash
curl "https://vibecode.bitrix24.com/v1/chats/events?offset=0&limit=50" \
  -H "X-Api-Key: YOUR_API_KEY"
```

### curl — OAuth application

```bash
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

```javascript
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

```javascript
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 |
|------|-----|---------|
| `success` | boolean | Always `true` on success |
| `data` | object | Result container |
| `data.events` | array | Array of events |
| `data.events[].eventId` | number | Event identifier. Used as `offset` in the next request |
| `data.events[].type` | string | Event type (`ONIMV2MESSAGEADD`, `ONIMV2JOINCHAT`, etc.) |
| `data.events[].date` | string | Event date and time (ISO 8601) |
| `data.events[].data` | object | Event data. Structure depends on the type, fields in camelCase |
| `data.nextOffset` | number | Value for the `offset` parameter in the next request |
| `data.hasMore` | boolean | `true` — there are more events beyond the current response |

## Response example

There are new events:

```json
{
  "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):

```json
{
  "success": true,
  "data": {
    "events": [],
    "nextOffset": 104,
    "hasMore": false
  }
}
```

## Error response example

400 — the required `offset` parameter was not provided:

```json
{
  "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 |
|------|-----|---------|
| 400 | `MISSING_PARAMS` | The `offset` parameter is missing or empty |
| 400 | `INVALID_OFFSET` | The `offset` value is not a non-negative integer (fractional, negative, leading zeros, extraneous characters) |
| 400 | `INVALID_LIMIT` | The `limit` value is outside the range 1–1000 or is not an integer |
| 403 | `SCOPE_DENIED` | The API key does not have the `im` scope |
| 401 | `TOKEN_MISSING` | The API key has no configured tokens |
| 422 | `BITRIX_ERROR` | Bitrix24 returned an error (error text in `message`) |
| 502 | `BITRIX_UNAVAILABLE` | Bitrix24 is unavailable or returned a server error |

Full list of common API errors — [Errors](/docs/errors).

## Event types

The `type` field of each event is one of the codes:

| Event | Description |
|---------|---------|
| `ONIMV2MESSAGEADD` | New message in a subscribed chat |
| `ONIMV2MESSAGEUPDATE` | Message edited |
| `ONIMV2MESSAGEDELETE` | Message deleted |
| `ONIMV2JOINCHAT` | A participant joined the chat |
| `ONIMV2REACTIONCHANGE` | A message reaction changed |

## Known specifics

**Token is required.** For a personal key `vibe_api_…` the `X-Api-Key` header is enough. For an OAuth key `vibe_app_…` you must also add `Authorization: Bearer <session_token>` — without it the request returns `401 TOKEN_MISSING`. Obtaining a token for an OAuth application — see [Keys and authorization](/docs/keys-auth).

**Cursor on the client side.** Unlike bot event polling, this endpoint does not store the `offset` position in the database — cursor management is on the calling code's side. On the first call pass `offset=0`, on subsequent calls — 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 frequency — events will be delivered on the next call. More about limits — [Limits and optimization](/docs/optimization).

**Polling loop (ready-to-use example):**

```javascript
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()
```

## See also

- [Subscribe to events](/docs/chats/events/subscribe)
- [Unsubscribe from events](/docs/chats/events/unsubscribe)
- [Messenger events](/docs/chats/events)
- [User events and polling together with the bot](/docs/bots/events/user-events)
- [Limits and optimization](/docs/optimization)
