
## List events

`GET /v1/calendar-events`

Returns calendar events for a fixed period around the current date: from one month back to three months ahead.

## Parameters

| Parameter | Type | Required | Default | Description |
|----------|-----|:-----:|-----------|---------|
| `type` (query) | string | yes | — | Calendar type: `user`, `group`, `company_calendar` |
| `ownerId` (query) | number | yes | — | Calendar owner ID: employee (`GET /v1/users`) or workgroup |
| `limit` (query) | number | no | `50` | Result window size — up to 5000 occurrences |
| `offset` (query) | number | no | `0` | Window start: skip the first N occurrences of the sorted set |
| `include` (query) | string | no | — | Related employees: `owner`, `host`, `attendee`. See the "Related data" section below |

## Examples

### curl — personal key

```bash
curl "https://vibecode.bitrix24.com/v1/calendar-events?type=user&ownerId=1&limit=10" \
  -H "X-Api-Key: YOUR_API_KEY"
```

### curl — OAuth application

```bash
curl "https://vibecode.bitrix24.com/v1/calendar-events?type=user&ownerId=1&limit=10" \
  -H "X-Api-Key: YOUR_APP_KEY" \
  -H "Authorization: Bearer USER_SESSION_TOKEN"
```

### JavaScript — personal key

```javascript
const params = new URLSearchParams({ type: 'user', ownerId: '1', limit: '10' })
const res = await fetch(`https://vibecode.bitrix24.com/v1/calendar-events?${params}`, {
  headers: {
    'X-Api-Key': 'YOUR_API_KEY',
  },
})

const { success, data, meta } = await res.json()
console.log(`Found ${meta.total} events`)
```

### JavaScript — OAuth application

```javascript
const params = new URLSearchParams({ type: 'user', ownerId: '1', limit: '10' })
const res = await fetch(`https://vibecode.bitrix24.com/v1/calendar-events?${params}`, {
  headers: {
    'X-Api-Key': 'YOUR_APP_KEY',
    'Authorization': 'Bearer USER_SESSION_TOKEN',
  },
})

const { success, data, meta } = await res.json()
```

## Response fields

| Field | Type | Description |
|------|-----|---------|
| `success` | boolean | Always `true` on success |
| `data` | array | Array of events (all fields — see [Event fields](./fields.md)) |
| `meta.total` | number | Total number of occurrences in the set. Recurring events are expanded — one row per occurrence |
| `meta.hasMore` | boolean | `true` while records remain beyond the `offset + limit` window |
| `meta.includeSkipped` | boolean | `true` if the response holds more than 200 occurrences and the relations from `include` are not loaded. Reduce `limit`. With 200 occurrences or fewer, the field is absent |

The card URL for any event in the `data` array depends on the calendar type:

| `type` | URL |
|---|---|
| `user` | `https://<portal>.bitrix24.com/company/personal/user/<ownerId>/calendar/?EVENT_ID=<id>&EVENT_DATE=<dd.mm.yyyy>` |
| `group` | `https://<portal>.bitrix24.com/workgroups/group/<ownerId>/calendar/?EVENT_ID=<id>&EVENT_DATE=<dd.mm.yyyy>` |
| `company_calendar` | `https://<portal>.bitrix24.com/calendar/?EVENT_ID=<id>&EVENT_DATE=<dd.mm.yyyy>` |

`<dd.mm.yyyy>` — the event start date (the `from` field) in dot-separated "day.month.year" format. `<portal>` — your Bitrix24 portal domain. Access is limited by the employee's permissions in Bitrix24.

## Related data

To retrieve related employees with the event list, pass the `include` parameter in the GET request:

```
GET /v1/calendar-events?type=user&ownerId=1&include=owner,host
```

Available `include` values — up to three names per request:

| Name | Source field | Key in `_included` | Result |
|------|--------------|--------------------|--------|
| `owner` | `ownerId` — the calendar owner | `owner` | Employee card or `null` |
| `host` | `meetingHost` — the meeting organizer | `host` | Employee card or `null`. For `type: group`, a card is returned only when `isMeeting: true`, otherwise `_included.host` is `null`. For a non-meeting event in that calendar, `meetingHost` holds the workgroup ID. The event creator is identified by `createdBy` |
| `attendee` | `attendeeList` — the invitees | `users` | Array of cards in `attendeeList` order. Without invitees — an empty array |

The employee card is the same as in the [`GET /v1/users/:id`](/docs/entities/users/get) response. Since these relations load employee data, the API key needs the `user` scope in addition to `calendar`. Each employee is loaded once per response. The result is in the `_included` field of every `data` element:

```json
{
  "success": true,
  "data": [
    {
      "id": 7773,
      "name": "Team call",
      "ownerId": 1,
      "meetingHost": 1,
      "_included": {
        "owner": { "id": 1, "name": "John", "lastName": "Brown", "email": "john.brown@example.com", "active": true },
        "host": { "id": 1, "name": "John", "lastName": "Brown", "email": "john.brown@example.com", "active": true }
      }
    }
  ],
  "meta": { "total": 1, "hasMore": false }
}
```

The main fields of the event and the employee are shown. For all card fields, see [Employee fields](/docs/entities/users/fields). If the response holds more than 200 occurrences, relations are not loaded and `meta` carries `includeSkipped: true` — reduce `limit`.

## Response example

```json
{
  "success": true,
  "data": [
    {
      "id": 7773,
      "parentId": 7773,
      "active": true,
      "deleted": false,
      "type": "user",
      "ownerId": 1,
      "name": "Team call",
      "from": "2026-06-10T10:00:00+00:00",
      "to": "2026-06-10T11:00:00+00:00",
      "skipTime": false,
      "durationSeconds": 3600,
      "createdBy": 1,
      "dateCreate": "06/05/2026 09:12:00 am",
      "updatedAt": "06/05/2026 09:12:00 am",
      "description": "Weekly sync",
      "accessibility": "busy",
      "importance": "normal",
      "isMeeting": false,
      "meetingStatus": "H",
      "meetingHost": 1,
      "sectionId": 3,
      "attendeeList": [
        { "id": 1, "entryId": "7773", "status": "H" }
      ]
    }
  ],
  "meta": {
    "total": 1,
    "hasMore": false
  }
}
```

## Error response example

400 — the required `type` and `ownerId` parameters are missing:

```json
{
  "success": false,
  "error": {
    "code": "MISSING_REQUIRED_PARAMS",
    "message": "GET /v1/calendar-events requires query parameters: type, ownerId. Example: GET /v1/calendar-events?type=...&ownerId=..."
  }
}
```

## Errors

| HTTP | Code | Description |
|------|-----|---------|
| 400 | `MISSING_REQUIRED_PARAMS` | One or both of the `type` and `ownerId` parameters are missing |
| 400 | `INVALID_INCLUDE` | `include` contains a name outside the set `owner`, `host`, `attendee`. The response lists the available names |
| 400 | `INCLUDE_LIMIT_EXCEEDED` | `include` contains more than three names |
| 403 | `SCOPE_DENIED` | The API key does not have the `calendar` scope |
| 403 | `SCOPE_DENIED` | `include` requests a relation to an employee, and the key lacks the `user` scope. The response names the relation and the missing scope |
| 401 | `TOKEN_MISSING` | The API key has no configured tokens |
| 429 | `RATE_LIMITED` | Rate limit exceeded: 300 requests per minute per portal. All API keys for the portal share this limit. The current limit is returned in the `x-ratelimit-limit` header. Retry after the delay in the `Retry-After` header |

For the full list of common API errors, see [Errors](/docs/errors).

## Known specifics

**The whole range comes as one set.** Bitrix24 delivers the requested period as a single unpaginated array. The platform sorts it deterministically — by the event start `from`, ties broken by `id`, then by `occurrenceIndex` — and returns the window from `offset` to `offset + limit`. The element order is reproducible across requests. Windows with different `offset` values do not overlap and together cover the whole set.

**Recurring events.** An event with the `rrule` field is returned in the list as a separate item for each occurrence of the series that falls within the selection window. All items share the same `id` and `parentId`, differing in `from` / `to` and `occurrenceIndex` — the `id` + `occurrenceIndex` pair uniquely identifies a row of the set.

**Timezone offset across the set.** The instant in `from` and `to` is exact in every row, but the offset suffix is not the same across all rows of the response — occurrences of a recurring series carry the offset in effect at the start of the series. Parse the values into instants and convert them to the timezone you need. Hours and minutes read straight from the string can differ from the time the employee sees in the calendar.

**Change tracking.** The `version` field is a monotonic counter of changes to the event, independent of the account's regional settings. Request the list with `select=id,version`, compare the pairs against your snapshot and re-read the changed events. Both fields are described in [Event fields](./fields.md).

## See also

- [Create event](./create.md)
- [Get event](./get.md)
- [Event fields](./fields.md)
- [Related data](/docs/includes)
