
## Call statistics

`GET /v1/calls/statistics`

Returns a list of records of past calls with support for filtering by fields, sorting and pagination.

## Parameters

| Parameter | Type | Default | Description |
|----------|-----|-----------|---------|
| `filter` (query) | object | — | Filtering by record fields (UPPER_SNAKE_CASE). The operators `>`, `>=`, `<`, `<=`, `!` are supported as a prefix to the field name. Example: `?filter[CALL_TYPE]=1`, `?filter[>CALL_START_DATE]=2026-01-01T00:00:00` |
| `sort` (query) | string | — | Field to sort by, in UPPER_SNAKE_CASE. Example: `CALL_START_DATE` |
| `order` (query) | string | — | Sort direction: `ASC` or `DESC` |
| `limit` (query) | number | `50` | Maximum number of records in the response (up to 500) |
| `offset` (query) | number | `0` | Offset for pagination |

A date range is set with two operators at once. Example: `filter[>CALL_START_DATE]=2026-04-01T00:00:00&filter[<CALL_START_DATE]=2026-04-30T23:59:59`.

## Examples

### curl — personal key

```bash
curl "https://vibecode.bitrix24.com/v1/calls/statistics?limit=10&sort=CALL_START_DATE&order=DESC&filter[CALL_TYPE]=1" \
  -H "X-Api-Key: YOUR_API_KEY"
```

### curl — OAuth application

```bash
curl "https://vibecode.bitrix24.com/v1/calls/statistics?limit=10&sort=CALL_START_DATE&order=DESC&filter[CALL_TYPE]=1" \
  -H "X-Api-Key: YOUR_APP_KEY" \
  -H "Authorization: Bearer USER_SESSION_TOKEN"
```

### JavaScript — personal key

```javascript
const params = new URLSearchParams({
  limit: '10',
  sort: 'CALL_START_DATE',
  order: 'DESC',
  'filter[CALL_TYPE]': '1',
})

const res = await fetch(`https://vibecode.bitrix24.com/v1/calls/statistics?${params}`, {
  headers: { 'X-Api-Key': 'YOUR_API_KEY' },
})

const { success, data, total } = await res.json()
console.log(`Calls found: ${total}`)
```

### JavaScript — OAuth application

```javascript
const params = new URLSearchParams({
  limit: '10',
  sort: 'CALL_START_DATE',
  order: 'DESC',
  'filter[CALL_TYPE]': '1',
})

const res = await fetch(`https://vibecode.bitrix24.com/v1/calls/statistics?${params}`, {
  headers: {
    'X-Api-Key': 'YOUR_APP_KEY',
    'Authorization': 'Bearer USER_SESSION_TOKEN',
  },
})

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

### JavaScript — iterating through all pages

```javascript
const pageSize = 100
let offset = 0
const allCalls = []

while (true) {
  const params = new URLSearchParams({
    limit: String(pageSize),
    offset: String(offset),
    sort: 'CALL_START_DATE',
    order: 'DESC',
  })

  const res = await fetch(`https://vibecode.bitrix24.com/v1/calls/statistics?${params}`, {
    headers: { 'X-Api-Key': 'YOUR_API_KEY' },
  })
  // total is at the root of the response — total records under the filter
  const { data, total } = await res.json()

  allCalls.push(...data)
  offset += data.length

  // fetch the next page until all records are collected
  if (offset >= total || data.length === 0) break
}

console.log(`Calls collected: ${allCalls.length}`)
```

## Response fields

| Field | Type | Description |
|------|-----|---------|
| `success` | boolean | Always `true` on success |
| `data` | array | Array of call records |
| `data[].id` | string | Internal record identifier |
| `data[].portalUserId` | string | Operator ID. List: [`GET /v1/users`](/docs/entities/users) |
| `data[].portalNumber` | string | Line identifier: `reg<id>` — a rented Voximplant line, `sip<id>` — a SIP line, `REST_APP:<id>` — a line via a REST application |
| `data[].phoneNumber` | string | Client's phone number |
| `data[].callId` | string | Call identifier. Prefixes: `externalCall.` — from [`register`](../crm/register.md), `callback.` — from a [callback](../outbound/callback.md), `infocall.` — from an [automatic call](../outbound/auto-call.md) |
| `data[].externalCallId` | string \| null | External identifier passed at registration |
| `data[].callCategory` | string | Category: `external` for calls via the REST API |
| `data[].callLog` | string \| null | URL of the Voximplant detailed log. `null` for calls via a REST application |
| `data[].callDuration` | string | Call duration in seconds (string) |
| `data[].callStartDate` | string | Call start date, ISO 8601 with the Bitrix24 account time zone |
| `data[].callRecordUrl` | string | URL of the call audio recording |
| `data[].callVote` | string \| null | Call rating from `1` to `5` |
| `data[].cost` | string | Call cost (decimal string) |
| `data[].costCurrency` | string | Currency code (`USD` and others) |
| `data[].callFailedCode` | string | Result code: `200` — success, `603-S` — cancelled, `304` — missed, `500` — scenario error, `402` — insufficient funds, `403` — forbidden |
| `data[].callFailedReason` | string | Text reason for the result |
| `data[].crmEntityType` | string | Type of the linked CRM entity: `LEAD`, `CONTACT` or an empty string. Source: [leads](/docs/entities/leads), [contacts](/docs/entities/contacts) |
| `data[].crmEntityId` | string | ID of the linked CRM entity |
| `data[].crmActivityId` | string | CRM activity ID. `"0"` — no activity created |
| `data[].restAppId` | string \| null | ID of the application that initiated the call |
| `data[].restAppName` | string \| null | Name of the application that initiated the call |
| `data[].transcriptId` | string \| null | ID of the attached transcription. Attach: [`POST /v1/calls/:callId/transcription`](../crm/transcription.md) |
| `data[].transcriptPending` | string | `Y` — transcription is being processed, `N` — transcription is ready or absent |
| `data[].sessionId` | string | Voximplant session identifier |
| `data[].redialAttempt` | string \| null | Redial attempt number |
| `data[].comment` | string \| null | Operator comment |
| `data[].recordDuration` | string \| null | Conversation recording duration in seconds |
| `data[].recordFileId` | string \| null | ID of the recording file on the Drive |
| `data[].callType` | string | Call type: `"1"` — outbound, `"2"` — inbound, `"3"` — inbound with redirect, `"4"` — [callback](../outbound/callback.md), `"5"` — informational ([automatic call](../outbound/auto-call.md)) |
| `total` | number | Total number of records matching the filter |

## Response example

```json
{
  "success": true,
  "data": [
    {
      "id": "1",
      "portalUserId": "1",
      "portalNumber": "reg133788",
      "phoneNumber": "+12025550123",
      "callId": "11018129443EB80D.1754478520.11438214",
      "externalCallId": null,
      "callCategory": "external",
      "callLog": "https://storage-gw-ru-02.voximplant.com/voximplant-logs/2025/08/06/...",
      "callDuration": "0",
      "callStartDate": "2025-08-06T14:08:40+00:00",
      "callRecordUrl": "",
      "callVote": null,
      "cost": "0.0000",
      "costCurrency": "USD",
      "callFailedCode": "603-S",
      "callFailedReason": "Decline self",
      "crmEntityType": "CONTACT",
      "crmEntityId": "275",
      "crmActivityId": "7739",
      "restAppId": null,
      "restAppName": null,
      "transcriptId": null,
      "transcriptPending": "N",
      "sessionId": "3841557776",
      "redialAttempt": null,
      "comment": null,
      "recordDuration": null,
      "recordFileId": null,
      "callType": "1"
    }
  ],
  "total": 30
}
```

## Error response example

403 — no `telephony` scope:

```json
{
  "success": false,
  "error": {
    "code": "SCOPE_DENIED",
    "message": "This endpoint requires 'telephony' scope"
  }
}
```

## Errors

| HTTP | Code | Description |
|------|-----|---------|
| 401 | `MISSING_API_KEY` | The `X-Api-Key` header was not provided |
| 401 | `INVALID_API_KEY` | Invalid API key |
| 401 | `TOKEN_MISSING` | The key has no configured Bitrix24 tokens |
| 401 | `KEY_INACTIVE` | The API key is inactive or revoked |
| 403 | `SCOPE_DENIED` | The key lacks the `telephony` scope |
| 422 | `BITRIX_ERROR` | Bitrix24 returned an error (text in `error.message`) |
| 429 | `RATE_LIMITED` | Request limit exceeded |
| 502 | `BITRIX_UNAVAILABLE` | Bitrix24 is unavailable |

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

## Known specifics

**Field names in filter and sort are UPPER_SNAKE_CASE, but in the response they are camelCase.** The `filter` and `sort` parameters accept the original Bitrix24 names (`CALL_TYPE`, `CALL_START_DATE`), while the response fields come back in camelCase (`callType`, `callStartDate`). A filter on a camelCase name is silently ignored — all records are returned.

**`total` is at the root of the response.** Unlike other Vibecode list endpoints, the `total` field is located directly at the root of the response, not in `meta.total`.

**A filter on a nonexistent field** does not return an error — an invalid field name is silently ignored and all records are returned.

## See also

- [Register a call](../crm/register.md) — returns `CALL_ID`, which appears in statistics
- [Callback](../outbound/callback.md)
- [Automatic call with speech synthesis](../outbound/auto-call.md)
- [Automatic call with an audio file](../outbound/auto-call-audio.md)
- [Voice reference](./voices.md) — available voices for an automatic call
- [Leads](/docs/entities/leads), [Contacts](/docs/entities/contacts) — entities from the `CRM_ENTITY_TYPE` / `CRM_ENTITY_ID` fields
- [Telephony — overview](/docs/telephony)
