## List call journal

`GET /v1/calls/log`

Returns the key owner's personal call journal — incoming, outgoing and missed calls — with a missed-call counter. The journal is read page by page using a cursor from the last record.

## Parameters

| Parameter | Type | Required | Default | Description |
|----------|-----|-------|-----------|----------|
| `status` (query) | string / string[] | no | — | Call status: `initiated`, `answered`, `declined`, `missed`, case-insensitive. Several statuses — `status[]=missed&status[]=answered` or a repeated parameter `status=missed&status=answered` |
| `excludeStatus` (query) | string | no | — | One status from the same list to exclude from the results |
| `type` (query) | string | no | — | Direction: `incoming` or `outgoing`. When `status` is also passed, `status` takes precedence |
| `search` (query) | string | no | — | Search string |
| `count` (query) | integer | no | `50` | Page size, a positive integer. A value above `100` is capped at `100` |
| `lastId` (query) | integer | no | `0` | `id` of the last record on the previous page. `0` — the first page |
| `lastStatusTime` (query) | string | no | — | `statusTime` of the last record on the previous page, passed together with `lastId` |

Other query parameters are rejected with `400 INVALID_PARAMS`.

Pagination: for the next page, pass `lastId` and `lastStatusTime` from the last record of the current one. Pass `statusTime` unchanged, exactly as it arrived: it is an ATOM date with a time zone offset.

## Examples

### curl — personal key

```bash
curl -g -X GET "https://vibecode.bitrix24.com/v1/calls/log?status[]=missed&status[]=answered&count=50" \
  -H "X-Api-Key: YOUR_API_KEY"
```

### curl — OAuth application

```bash
curl -g -X GET "https://vibecode.bitrix24.com/v1/calls/log?status[]=missed&status[]=answered&count=50" \
  -H "X-Api-Key: YOUR_APP_KEY" \
  -H "Authorization: Bearer USER_SESSION_TOKEN"
```

### JavaScript — personal key

```javascript
const response = await fetch('https://vibecode.bitrix24.com/v1/calls/log?status[]=missed&status[]=answered&count=50', {
  headers: { 'X-Api-Key': 'YOUR_API_KEY' },
})
const { success, data, error } = await response.json()
console.log(success ? data.calls : error.code)
```

### JavaScript — OAuth application

```javascript
const response = await fetch('https://vibecode.bitrix24.com/v1/calls/log?status[]=missed&status[]=answered&count=50', {
  headers: {
    'X-Api-Key': 'YOUR_APP_KEY',
    'Authorization': 'Bearer USER_SESSION_TOKEN',
  },
})
const { success, data, error } = await response.json()
console.log(success ? data.calls : error.code)
```

## Response fields

| Field | Type | Description |
|------|-----|----------|
| `success` | boolean | `true` on success |
| `data.calls` | array | Journal records; an empty array means there are none |
| `data.calls[].id` | integer | Journal record ID. Used to [delete a record](/docs/calls/log/delete) and as `lastId` |
| `data.calls[].sourceType` | string | Call source |
| `data.calls[].sourceCallId` | integer | Call ID in the source |
| `data.calls[].status` | string | Status: `initiated`, `answered`, `declined`, `missed` |
| `data.calls[].statusTime` | string | Time of the status change in ATOM format. Used as `lastStatusTime` |
| `data.calls[].type` | string | Direction: `incoming` or `outgoing` |
| `data.calls[].isUnseen` | boolean | Whether the record is unseen |
| `data.calls[].callData` | object | Call data. Only the fields present in the record are returned: `phoneNumber`, `displayName`, `title`, `avatar`, `color`, `dialogId`, `chatType` are strings, `duration`, `userId`, `chatId`, `userCount` are integers |
| `data.missedCounter` | integer | Missed-call counter |

## Response example

A journal with no records:

```json
{
  "success": true,
  "data": {
    "calls": [],
    "missedCounter": 0
  }
}
```

## Error response example

400 — unknown status:

```json
{
  "success": false,
  "error": {
    "code": "INVALID_FILTER_VALUE",
    "message": "Invalid filter value"
  }
}
```

## Errors

| HTTP | Code | Description |
|------|-----|----------|
| 400 | `INVALID_FILTER_VALUE` | `status` or `excludeStatus` is outside the status list, the `status` array is empty, or `type` or `search` is not a string |
| 400 | `INVALID_PARAMS` | Unknown query parameter: `Unknown query parameter` |
| 400 | `INVALID_PARAMS` | `count` is not a positive integer, `lastId` is not a non-negative integer, or `lastStatusTime` is not a string |
| 403 | `SCOPE_DENIED` | The key lacks the `call` scope |
| 403 | `BITRIX_ACCESS_DENIED` | Bitrix24 denied access |
| 401 | `TOKEN_MISSING` | The key has no Bitrix24 tokens configured |
| 422 | `BITRIX_ERROR` | Bitrix24 rejected the request; the text is in `error.message` |
| 429 | `RATE_LIMITED` | The Bitrix24 request limit is exceeded |
| 502 | `BITRIX_ERROR` | The journal response does not have the expected shape: `Invalid call log response` |
| 502 | `BITRIX_UNAVAILABLE` | Bitrix24 is unavailable |
| 503 | `BITRIX_TIMEOUT` | Bitrix24 did not respond in time |

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

## Known specifics

**The journal is personal.** The response contains only the calls of the user who owns the key. Call statistics for the Bitrix24 account are read separately — [Analytics and reference data](/docs/telephony/analytics).

**Strings with credentials are hidden.** A string field of a record that contains a URL with a Bitrix24 access token is returned as `[REDACTED]`.

**The `manage_call_log` MCP tool.** The same list is requested with `action: "list"` and parameters in `query`: `{ "status": ["missed", "answered"], "count": 50 }`. The `status` array must contain at least one string; other `query` values are strings or numbers.

## See also

- [Call journal](/docs/calls/log)
- [Delete journal entry](/docs/calls/log/delete)
- [Chat with a user](/docs/calls/log/chat)
- [Calls](/docs/calls)
