For AI agents: markdown of this page — /docs-content-en/telephony/analytics/statistics.md documentation index — /llms.txt
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
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
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
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
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
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 |
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, callback. — from a callback, infocall. — from an automatic call |
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, 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 |
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, "5" — informational (automatic call) |
total |
number | Total number of records matching the filter |
Response example
{
"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:
{
"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.
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 — returns
CALL_ID, which appears in statistics - Callback
- Automatic call with speech synthesis
- Automatic call with an audio file
- Voice reference — available voices for an automatic call
- Leads, Contacts — entities from the
CRM_ENTITY_TYPE/CRM_ENTITY_IDfields - Telephony — overview