Studio API
What is Studio API?
Think of Studio API as a data pipeline that sends raw call data out of Telavox so customers can plug it into their own systems - like their own dashboards, BI tools (Power BI, Tableau), CRMs, or custom reports.
It's not a tool with a user interface. It's a way for customers who want their own statistics setup to get the raw numbers and build whatever they want with them.
Studio API – Live
Sends call data to your system the moment something happens — a call starts, enters a queue, is answered, or ends. This makes it a good fit for live wallboards, real-time dashboards, and anything where you want to see what's happening right now. How it works: Studio API – Live uses a technology called WebSockets, which keeps an open connection between Telavox and your system so new data can be pushed through instantly.
Studio API – Historic
Lets you pull call data for a specific time period whenever you need it — for example, "all calls from October 1 to 10." This option is best for reports, trend analysis, and looking back at what's already happened. How it works: Studio API – Historic uses a technology called GraphQL, which lets you ask for exactly the information you need — nothing more, nothing less.
Requirements
To track/receive data on users
- The user must have the feature "Stats Tracked User".
Access and visibility
Stats Tracked User (User level)
- User will be tracked in the report.
- No access to Stats UI via the Web and Desktop app.
Introducing the Telavox Studio API. Discover more by clicking the tabs below: Studio API - Live and Studio API - Historic.
Part 1: Concepts
1. Concepts
1.1 Datasources
Stats Studio exposes two complementary data layers, with five data sources between them. The live layer streams events over WebSockets the moment they happen. The historic layer lets you query a date range over GraphQL whenever you want.
Datasource |
Live (WebSocket) |
Historic (GraphQL) |
Call Overview |
/calls |
calls |
Queue Overview |
/queues |
queues |
Agent Overview |
/agents |
agents |
Profile Live |
/profile_live |
none |
Profile History |
/profile_history |
profile_history |
Both layers authenticate with the same Statistics Token — only the way you attach it to a request differs (see § 1.3). |
1.2 Perspectives
The same call can be told from several angles. Stats Studio organises five data sources around three perspectives of a call, plus two views of agent state. Pick the perspective that matches the question you are trying to answer.
Example. One incoming call enters the IVR, lands in the first queue, is offered to Agent 1 (no answer), then to Agent 2 (no answer), jumps to a second queue, and is finally offered to Agent 3, who answers. How many records does that single call produce?
Perspective |
Records |
Detail |
Call Overview |
1 |
1 answered |
Queue Overview |
2 |
1 not answered (first queue), 1 answered (second queue) |
Agent Overview |
3 |
2 not answered (Agent 1, Agent 2), 1 answered (Agent 3) |
Two additional data sources don’t follow this per-call grouping:
Profile Live: fires one event per agent every time their presence changes (Available, Lunch, In a call…).
Profile History: one record per agent profile slot, summarised once an hour.
1.3 Authentication
Both layers use the same Statistics Token.
Sign in to the Telavox webagent.
Open Settings → My Account → Username and password.
Under Integration, click Manage Token.
Click Create new Statistics Token and copy the value.
The token is shown once. Treat it like a password. Rotate it from the same page if it leaks. |
How the token is attached differs by layer:
Live: pass an Authorization: Bearer <token> header during the WebSocket handshake. A successful upgrade returns HTTP 101 Switching Protocols. See Part 2.
Historic: pass an Authorization: Bearer <token> header on every HTTP POST. See Part 3.
1.4 Field-naming convention
The same concept appears in both layers under different casings:
Layer |
Style |
Example |
Live (WebSocket) |
snake_case |
id_call`, `call_direction`, `customer_target.number |
Historic (GraphQL) |
camelCase |
idCall`, `callDirection`, `customerTarget { number } |
Each datasource page in Parts 2 and 3 lists its full field reference in the casing of that layer. |
1.5 Identifiers — id vs accountId
Each agent, queue and IVR has two numeric IDs. Use the right one for the job:
id. Internal to Stats Studio. Use it when you are working only inside Stats Studio.
accountId. The same ID Telavox exposes in CAPI (Customer API) and PAPI (Partner API). Use it whenever you need to match a Stats Studio record to the same agent, queue or IVR in another Telavox system or in your own database.
The live WebSocket stream always includes account_id in the payload. The historic GraphQL filters accept agentAccountId, queueAccountId and ivrAccountId as inputs.
1.6 When to use which layer
Need it now? Use the live WebSocket of the relevant datasource.
Need it for a date range (yesterday, last week, last quarter)? Use the GraphQL query of the same datasource.
Building a wallboard? Open the WebSocket for delta updates and seed the initial state with a GraphQL query.
Building a daily report? GraphQL only.
Watching agent presence in real time? /profile_live.
Reporting on per-hour agent productivity? profile_history (historic) or /profile_history (live, hourly).
Delete
Part 2: Studio API - Live (WebSocket)
2. Live (WebSocket)
The live layer pushes events as they happen, one WebSocket per datasource. Every endpoint accepts the same Statistics Token (see § 1.3) as a handshake header.
2.0 Connecting
Node.js
|
// Node.js — npm i ws
import WebSocket from "ws";
const ws = new WebSocket("wss://statistics-webhook.telavox.se/calls", {
headers: { Authorization: `Bearer ${process.env.TELAVOX_TOKEN}` },
});
ws.on("message", (buf) => {
const evt = JSON.parse(buf.toString("utf8"));
console.log(evt);
});
ws.on("close", (code, reason) =>
console.log("closed", code, reason.toString())
);
|
Python
|
# Python — pip install websocket-client
import json, os, websocket
def on_message(ws, msg):
print(json.loads(msg))
ws = websocket.WebSocketApp(
"wss://statistics-webhook.telavox.se/calls",
header={"Authorization": f"Bearer {os.environ['TELAVOX_TOKEN']}"},
on_message=on_message,
)
ws.run_forever()
|
A successful handshake returns HTTP 101 Switching Protocols. A WSS path that returns HTTP 101 followed immediately by a text frame “Invalid path.” is an unknown datasource. HTTP 401 means the token is missing, expired, or revoked. |
2.1 Call Overview
One document per call journey: the full path of a call across transfers, queues, agents and IVRs.
Field |
Value |
Endpoint |
wss://statistics-webhook.telavox.se/calls |
Cadence |
Event-driven. Pushes as the call progresses (call started, entered queue, agent answered, call ended). |
Casing |
snake_case |
Sample payload
|
{
"id_call": "4d9c10dc-3f9f-41c7-a091-91d5fc746692",
"answered": 0,
"active": 1,
"call_direction": "incoming",
"time": {
"start": 1779099997234
},
"offered_and_answered": 0,
"offered_and_not_answered": 0,
"customer_telavox": {
"id": 43,
"name": "Telavox AB"
},
"customer_target": {
"number": "0032470624609"
},
"agent_list": ["Jennifer Symoens"]
}
|
Field reference
|
{
"active": "Int",
"agent_first": {
"account_id": "Int",
"answered": "DateTime",
"avatar": "String",
"email": "String",
"from_transfer": "Int",
"id": "Int",
"is_admin": "Int",
"licence": {
"ccpro": "Int",
"type": "String"
},
"name": "String",
"phone": "String",
"to_transfer": "Int",
"username": "String"
},
"agent_last": {
"account_id": "Int",
"answered": "DateTime",
"avatar": "String",
"email": "String",
"from_transfer": "Int",
"id": "Int",
"is_admin": "Int",
"licence": {
"ccpro": "Int",
"type": "String"
},
"name": "String",
"phone": "String",
"to_transfer": "Int",
"username": "String"
},
"agent_list": "String",
"agent_list_answer": "String",
"agent_list_no_answer": "String",
"answered": "Int",
"call_direction": "String",
"customer_target": {
"number": "String"
},
"customer_telavox": {
"id": "Int",
"name": "String"
},
"duration": {
"before_queue": "Int",
"on_hold": "Int",
"queue": {
"first": "Int",
"last": "Int",
"total": "Int"
},
"ring": {
"first": "Int",
"last": "Int",
"total": "Int"
},
"talk": {
"first": "Int",
"last": "Int",
"total": "Int"
},
"total": "Int",
"wait": "Int"
},
"id_call": "String",
"inactive_number": "Int",
"internal": "Int",
"ivr_first": {
"account_id": "Int",
"id": "Int",
"name": "String",
"phone": "String",
"timestamp": "DateTime"
},
"ivr_last": {
"account_id": "Int",
"id": "Int",
"name": "String",
"phone": "String",
"timestamp": "DateTime"
},
"ivr_list": "String",
"menu_digit": {
"destination": "String",
"dialed_number": "String",
"type": "String"
},
"offered_and_answered": "Int",
"offered_and_not_answered": "Int",
"ohw_ordered": "Boolean",
"on_hooked_calls": {
"answer_wait_request": "Int",
"customer_no_answer": "Int",
"no_wait_request": "Int"
},
"queue_first": {
"account_id": "Int",
"fail_reason": "String",
"id": "Int",
"name": "String",
"phone": "String",
"strategy": "String",
"timestart": "DateTime"
},
"queue_last": {
"account_id": "Int",
"fail_reason": "String",
"id": "Int",
"name": "String",
"phone": "String",
"strategy": "String",
"timestart": "DateTime"
},
"queue_list": "String",
"queue_pickup": "Int",
"recorded": "Int",
"redirected": {
"no_answer": "Int",
"no_reason": "Int",
"not_reachable": "Int",
"total": "Int",
"unconditional": "Int",
"user_busy": "Int"
},
"save_key_input": {
"input": "String",
"variable_name": "String"
},
"simultaneous_device_calls": "Int",
"terminated_call_reason": "String",
"time": {
"end": "DateTime",
"start": "DateTime"
},
"transfer_first": {
"answered": "Int",
"type": "String"
},
"transfer_last": {
"answered": "Int",
"type": "String"
},
"transfers": "Int",
"voicemail": "Int"
}
|
2.2 Queue Overview
One document per (call, queue) pair. A call that traverses three queues emits three records; calls that never enter a queue emit none.
Field |
Value |
Endpoint |
wss://statistics-webhook.telavox.se/queues |
Cadence |
Event-driven. Fires when a call enters or exits a queue. |
Casing |
snake_case |
Field reference
|
{
"account_id": "Int",
"active": "Int",
"agent_first": {
"account_id": "Int",
"answered": "DateTime",
"avatar": "String",
"email": "String",
"id": "Int",
"is_admin": "Int",
"licence": {
"ccpro": "Int",
"type": "String"
},
"name": "String",
"phone": "String",
"username": "String"
},
"agent_last": {
"account_id": "Int",
"answered": "DateTime",
"avatar": "String",
"email": "String",
"id": "Int",
"is_admin": "Int",
"licence": {
"ccpro": "Int",
"type": "String"
},
"name": "String",
"phone": "String",
"username": "String"
},
"agent_list": "String",
"agent_list_answer": "String",
"agent_list_no_answer": "String",
"answered": "Int",
"call_direction": "String",
"customer_target": {
"number": "String"
},
"customer_telavox": {
"id": "Int",
"name": "String"
},
"duration": {
"hold": "Int",
"queue": "Int",
"ring": {
"first": "Int",
"last": "Int",
"total": "Int"
},
"talk": "Int",
"total": "Int",
"wait": "Int"
},
"id_call": "String",
"internal": "Int",
"ivr": {
"account_id": "Int",
"id": "Int",
"name": "String",
"phone": "String",
"timestamp": "DateTime"
},
"on_hooked_calls": "Int",
"queue": {
"account_id": "Int",
"free_agents": "Int",
"id": "Int",
"name": "String",
"not_answer_risk": "Int",
"phone": "String",
"queue_talking_agents": "Int",
"strategy": "String",
"timestart": "DateTime",
"total_talking_agents": "Int"
},
"recorded": "Int",
"time": {
"end": "DateTime",
"start": "DateTime"
},
"transfer": "Int",
"voicemail": "Int"
}
|
2.3 Agent Overview
One document per (call, agent) offer. An agent rung but who does not answer still produces a record; calls that never reach an agent emit nothing.
Field |
Value |
Endpoint |
wss://statistics-webhook.telavox.se/agents |
Cadence |
Event-driven. Fires when a call is offered to an agent (answered or not). |
Casing |
snake_case |
Sample payload
|
{
"id_call": "4d9c10dc-3f9f-41c7-a091-91d5fc746692",
"call_direction": "incoming",
"active": 0,
"answered": 0,
"agent": {
"account_type_id": 1,
"account_id": 3467672,
"id": 924328,
"name": "Jennifer Symoens",
"phone": "003228979411",
"username": "003228979411",
"email": "jennifer.symoens@telavox.com",
"avatar": "https://flow.telavox.com/api/internal/contacts/contact-3934178/avatar",
"is_admin": 0,
"licence": {
"type": "Flow Mobile Custom 10",
"ccpro": 1
}
},
"customer_telavox": {
"id": 43,
"name": "Telavox AB"
},
"customer_target": {
"number": "0032470624609"
},
"time": {
"start": 1779099997234,
"end": 1779100016445
},
"duration": {
"total": 19211,
"ring": 19211
},
"simultaneous_device_calls": 1,
"agent_end_reason": "NO_ANSWER"
}
|
Field reference
Field names use snake_case.
|
{
"account_id": "Int",
"active": "Int",
"agent": {
"account_id": "Int",
"avatar": "String",
"email": "String",
"id": "Int",
"is_admin": "Int",
"licence": {
"ccpro": "Int",
"type": "String"
},
"name": "String",
"phone": "String",
"username": "String"
},
"agent_end_reason": "String",
"answered": "Int",
"call_direction": "String",
"customer_target": {
"number": "String"
},
"customer_telavox": {
"id": "Int",
"name": "String"
},
"duration": {
"hold": "Int",
"ring": "Int",
"talk": "Int",
"total": "Int"
},
"id_call": "String",
"internal": "Int",
"ivr": {
"account_id": "Int",
"id": "Int",
"name": "String",
"phone": "String",
"timestamp": "DateTime"
},
"on_hooked_calls": "Int",
"queue": {
"account_id": "Int",
"id": "Int",
"name": "String",
"phone": "String",
"strategy": "String",
"timestart": "DateTime"
},
"recorded": "Int",
"simultaneous_device_calls": "Int",
"time": {
"answered": "DateTime",
"end": "DateTime",
"start": "DateTime"
},
"transfer": "Int",
"voicemail": "Int"
}
|
2.4 Profile Live
Streams the current profile of each agent (Available, Lunch, Meeting…), queue-login state, and active-call indicators. Live only — no historic equivalent.
Field |
Value |
Endpoint |
wss://statistics-webhook.telavox.se/profile_live |
Cadence |
State-change driven. Fires whenever an agent changes profile, queue-login state or active-call state. |
Historic equivalent |
None. For aggregated history, use `profile_history`. |
Casing |
snake_case |
Sample payload
|
{
"customer_telavox": {
"id": 43,
"name": "Telavox AB"
},
"agent": {
"account_type_id": 1,
"account_id": 3083293,
"id": 846851,
"name": "Patrik Hilvéus",
"phone": "0406200552",
"username": "0406200552",
"email": "patrik.hilveus@telavox.se",
"is_admin": 1,
"licence": {
"type": "Flow Mobile Arms",
"ccpro": 1
}
},
"profile": {
"active": {
"id": 4969080,
"name": "Tillgänglig",
"status": "available",
"status_message": "Tillgänglig",
"start_date": 1779099033656,
"default_duration": 1779116400000,
"is_wrapup": false
},
"not_active": [
{ "id": 4969081, "name": "Lunch", "status": "unavailable", "start_date": 1779099030867, "is_wrapup": false },
{ "id": 4969082, "name": "Möte", "status": "available", "start_date": 1779084010014, "is_wrapup": false },
{ "id": 4969083, "name": "Slutat för dagen", "status": "unavailable", "start_date": 1779084113000, "is_wrapup": false }
]
},
"queue_login": {
"logged_in": [
{ "id": 111834, "name": "Jour-Customer Support", "start_date": 1779099033642 },
{ "id": 19843, "name": "Telavox Support", "start_date": 1779099033644 }
],
"logged_out": [
{ "id": 137266, "name": "SOHO", "start_date": 1779084113000 }
]
},
"active_call": {
"id_call": "89913fac-a038-4fff-b9bd-a183014dc794",
"answered": 0,
"queue_id": 111834,
"queue_description": "Jour-Customer Support",
"call_direction": "incoming",
"time": { "start": 1779100022081 }
},
"mobile_active": 0,
"web_active": 1,
"admin_active": 1
}
|
Truncated for readability — Profile Live publishes the full list of all profiles for each agent in profile.not_active. The full schema is shown in the field reference below. |
Field reference
|
{
"active": "Object",
"active_call": {
"answered": "Int",
"call_direction": "String",
"id_call": "String",
"internal": "Int",
"ivr_description": "String",
"ivr_id": "Int",
"on_hooked_calls": "Int",
"queue_description": "String",
"queue_id": "Int",
"recorded": "Int",
"time": {
"answered": "Int",
"start": "Int"
},
"transfer": "Int"
},
"admin_active": "Int",
"agent": {
"account_id": "Int",
"avatar": "String",
"email": "String",
"id": "Int",
"is_admin": "Int",
"licence": {
"ccpro": "Int",
"type": "String"
},
"name": "String",
"phone": "String",
"username": "String"
},
"customer_telavox": {
"id": "Int",
"name": "String"
},
"last_call_update": "Int",
"last_call_update_sec": "Int",
"mobile_active": "Int",
"profile": {
"active": {
"default_duration": "Int",
"id": "Int",
"is_wrapup": "Boolean",
"name": "String",
"start_date": "Int",
"status": "String",
"status_message": "String"
},
"not_active": {
"default_duration": "Int",
"id": "Int",
"is_wrapup": "Boolean",
"name": "String",
"start_date": "Int",
"status": "String",
"status_message": "String"
}
},
"queue_login": {
"logged_in": {
"id": "Int",
"name": "String",
"start_date": "Int"
},
"logged_out": {
"id": "Int",
"name": "String",
"start_date": "Int"
}
},
"web_active": "Int"
}
|
2.5 Profile History
Per-agent profile slot timeline. Each record covers one continuous period of an agent in a given profile, plus a summary of the calls they handled inside that slot.
Field |
Value |
Endpoint |
wss://statistics-webhook.telavox.se/profile_history |
Cadence |
Cron-style. The aggregation runs once per hour at the top of the hour (HH:00 UTC) and pushes the whole hourly batch to all connected clients. |
Behaviour |
Between hourly ticks the socket is silent — leave the connection open through the next HH:00 to receive data. |
Historic equivalent |
`profile_history` GraphQL root query — see § 3.5. |
Casing |
snake_case |
Field reference
|
{
"agent": {
"account_id": "Int",
"id": "Int",
"name": "String",
"phone": "String"
},
"call_summary": {
"active_calls": "String",
"answered_calls": "String",
"completed_calls": "String",
"duration": {
"talk": "Int"
},
"id_calls": "String",
"total_answered": "Int",
"total_calls": "Int"
},
"customer_telavox": {
"id": "Int",
"name": "String"
},
"extra_profile_available_1..10": {
"id": "Int",
"name": "String",
"usage": "Int"
},
"extra_profile_not_available_1..20": {
"id": "Int",
"name": "String",
"usage": "Int"
},
"profile_available": {
"active": "Boolean",
"available": "Boolean",
"busy": "Int",
"id": "Int",
"is_wrapup": "Boolean",
"name": "String",
"time_start": "String",
"times_in_profile": "Int",
"usage": "Int"
},
"profile_not_available": {
"active": "Boolean",
"available": "Boolean",
"busy": "Int",
"id": "Int",
"is_wrapup": "Boolean",
"name": "String",
"time_start": "String",
"times_in_profile": "Int",
"usage": "Int"
},
"queue_summary": {
"logged_in": {
"account_id": "Int",
"active": "Boolean",
"number_times_logged_in": "Int",
"queue_id": "Int",
"queue_name": "String",
"time_start": "Int",
"usage": "Int"
},
"logged_out": {
"account_id": "Int",
"active": "Boolean",
"number_times_logged_out": "Int",
"queue_id": "Int",
"queue_name": "String",
"time_start": "Int",
"usage": "Int"
}
},
"slot": {
"busy": "Int",
"end_date": "Int",
"start_date": "Int",
"time_range": "Int"
}
}
|
Delete
Part 3: Studio API - Historic (GraphQL)
3. Historic (GraphQL)
The historic layer is a single GraphQL endpoint that exposes one root query per perspective (calls, agents, queues, profile_history). Use it for any question that needs a date range — yesterday, last week, last quarter.
Endpoint: https://statistics-api.telavox.se/graphql
Method: HTTP POST
Casing: camelCase (mirror of the snake_case live fields).
3.0 Connecting
Node.js
|
// Node.js — native fetch (Node 18+)
const res = await fetch("https://statistics-api.telavox.se/graphql", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${process.env.TELAVOX_TOKEN}`,
},
body: JSON.stringify({
query: `query($s:DateTime,$e:DateTime){
calls(filter:{startDate:$s,endDate:$e}, first:1){ totalCount } }`,
variables: { s: "2025-10-01T00:00:00Z", e: "2025-10-01T01:00:00Z" },
}),
});
const { data, errors } = await res.json();
if (errors) console.error(errors);
console.log(data);
|
Python
|
# Python — pip install requests
import os, requests
res = requests.post(
"https://statistics-api.telavox.se/graphql",
headers={
"Content-Type": "application/json",
"Authorization": f"Bearer {os.environ['TELAVOX_TOKEN']}",
},
json={
"query": "query($s:DateTime,$e:DateTime){
calls(filter:{startDate:$s,endDate:$e}, first:1){ totalCount } }",
"variables": {"s": "2025-10-01T00:00:00Z", "e":
"2025-10-01T01:00:00Z"},
},
)
print(res.json())
|
|
HTTP 401 means the token is missing or invalid. A 200 with an errors array
▎ in the body means the request was authenticated but the query is malformed
▎ or asks for fields that don't exist. Note: every root query (calls, queues,
▎ agents, agentHistory) requires a filter with startDate and endDate — calling
▎ them without it returns too_long_http_line_exception.
|
3.1 Filtering and pagination
All four root queries accept the same wrapping shape:
|
<root>(
filter: OverviewFilter
first: Int!
after: String
) {
data { ... }
totalCount
cursor
hasNextPage
}
|
Use cursor from one response as the after argument of the next request to paginate.
OverviewFilter fields
Field |
Type |
Meaning |
startDate |
DateTime |
Start of the window (ISO 8601 UTC). |
endDate |
DateTime |
End of the window (ISO 8601 UTC). |
queueId |
Int |
Stats-system queue ID. |
queueAccountId |
Int |
Queue ID shared with CAPI/PAPI. Use this for cross-system joins. |
queueName |
String |
Exact queue display name. |
ivrId |
Int |
Stats-system IVR ID. |
ivrAccountId |
Int |
IVR ID shared with CAPI/PAPI. |
ivrName |
String |
Exact IVR display name. |
agentId |
Int |
Stats-system agent ID. |
agentAccountId |
Int |
Agent ID shared with CAPI/PAPI. Use this for cross-system joins. |
agentName |
String |
Exact agent display name. |
callId |
String |
Exact call UUID. |
totalCount is the count for the date window only — it does not shrink when you add agentId, queueId or other filters. To know how many records a filter actually matched, look at the size of data or paginate with cursor / hasNextPage. |
Need accountId for a CAPI/PAPI join? Read account_id from the live WebSocket stream — that's the reliable source (see § 1.5). |
3.2 Call Overview
One document per call journey: the full path of a call across transfers, queues, agents and IVRs.
Field |
Value |
Root query |
calls |
Return type |
CallOverviewConnection! |
Record type |
CallOverviewRecord |
Live counterpart |
`/calls` (§ 2.1) |
Casing |
camelCase |
Example query
|
query Calls($startDate: DateTime, $endDate: DateTime) {
calls(filter: { startDate: $startDate, endDate: $endDate }, first: 25) {
data {
idCall
callDirection
answered
duration { total talk { total } ring { total } }
agentFirst { id name }
queueFirst { id name }
}
totalCount cursor hasNextPage
}
}
|
Run it from Node.js
|
const res = await fetch("https://statistics-api.telavox.se/graphql", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${process.env.TELAVOX_TOKEN}`,
},
body: JSON.stringify({
query: `query Calls($startDate: DateTime, $endDate: DateTime) {
calls(filter: { startDate: $startDate, endDate: $endDate }, first: 25) {
data { idCall callDirection answered }
totalCount cursor hasNextPage
}
}`,
variables: {
startDate: "2025-10-01T00:00:00Z",
endDate: "2025-10-06T23:59:59Z",
},
}),
});
const { data } = await res.json();
console.log(data.calls);
|
Run it from Python
|
import os, requests
QUERY = """query Calls($startDate: DateTime, $endDate: DateTime) {
calls(filter: { startDate: $startDate, endDate: $endDate }, first: 25) {
data { idCall callDirection answered }
totalCount cursor hasNextPage
}
}"""
res = requests.post(
"https://statistics-api.telavox.se/graphql",
headers={
"Content-Type": "application/json",
"Authorization": f"Bearer {os.environ['TELAVOX_TOKEN']}",
},
json={
"query": QUERY,
"variables": {
"startDate": "2025-10-01T00:00:00Z",
"endDate": "2025-10-06T23:59:59Z",
},
},
)
print(res.json()["data"]["calls"])
|
Field reference
Field names use camelCase. Source: GraphQL schema introspection of CallOverviewRecord.
|
{
"idCall": "String",
"customerTarget": {
"number": "String"
},
"callDirection": "String",
"offeredAndAnswered": "Int",
"offeredAndNotAnswered": "Int",
"onHookedCalls": {
"noWaitRequest": "Int",
"answerWaitRequest": "Int",
"customerNoAnswer": "Int"
},
"simultaneousDeviceCalls": "Int",
"inactiveNumber": "Int",
"duration": {
"ring": {
"first": "Int",
"last": "Int",
"total": "Int"
},
"queue": {
"first": "Int",
"last": "Int",
"total": "Int"
},
"talk": {
"first": "Int",
"last": "Int",
"total": "Int"
},
"total": "Int",
"onHold": "Int",
"wait": "Int"
},
"answered": "Int",
"active": "Int",
"agentFirst": {
"id": "Int",
"accountId": "Int",
"name": "String",
"phone": "String",
"email": "String",
"answered": "DateTime",
"toTransfer": "Int",
"fromTransfer": "Int",
"licence": {
"ccpro": "Int",
"type": "String"
}
},
"agentLast": {
"id": "Int",
"accountId": "Int",
"name": "String",
"phone": "String",
"email": "String",
"answered": "DateTime",
"toTransfer": "Int",
"fromTransfer": "Int",
"licence": {
"ccpro": "Int",
"type": "String"
}
},
"agentList": "[String!]",
"agentListAnswer": "[String!]",
"agentListNoAnswer": "[String!]",
"queueFirst": {
"id": "Int",
"accountId": "Int",
"name": "String",
"phone": "String",
"strategy": "String",
"timestart": "DateTime",
"failReason": "String"
},
"queueLast": {
"id": "Int",
"accountId": "Int",
"name": "String",
"phone": "String",
"strategy": "String",
"timestart": "DateTime",
"failReason": "String"
},
"queueList": "[String!]",
"ivrFirst": {
"id": "Int",
"accountId": "Int",
"name": "String",
"phone": "String",
"timestamp": "DateTime"
},
"ivrLast": {
"id": "Int",
"accountId": "Int",
"name": "String",
"phone": "String",
"timestamp": "DateTime"
},
"ivrList": "[String!]",
"queuePickup": "Int",
"time": {
"start": "DateTime",
"end": "DateTime"
},
"transferFirst": {
"type": "String",
"answered": "Int"
},
"transferLast": {
"type": "String",
"answered": "Int"
},
"transfers": "Int",
"redirected": {
"total": "Int",
"noReason": "Int",
"noAnswer": "Int",
"notReachable": "Int",
"userBusy": "Int",
"unconditional": "Int"
},
"recorded": "Int",
"menuDigit": {
"dialedNumber": "String",
"destination": "String",
"type": "String"
},
"terminatedCallReason": "String",
"internal": "Int",
"saveKeyInput": [
{
"variableName": "String",
"input": "String"
}
],
"timestamp": "DateTime"
}
|
3.3 Queue Overview
One document per (call, queue) pair. A call that traverses three queues emits three records; calls that never enter a queue emit none.
Field |
Value |
Root query |
queues |
Return type |
QueueOverviewConnection! |
Record type |
QueueOverviewRecord |
Live counterpart |
`/queues` (§ 2.2) |
Casing |
camelCase |
Example query
|
query Queues($startDate: DateTime, $endDate: DateTime) {
queues(filter: { startDate: $startDate, endDate: $endDate }, first: 25) {
data {
idCall
queue { id name strategy }
duration { queue wait talk }
answered
agentFirst { id name }
}
totalCount cursor hasNextPage
}
}
|
Run it from Node.js
|
const res = await fetch("https://statistics-api.telavox.se/graphql", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${process.env.TELAVOX_TOKEN}`,
},
body: JSON.stringify({
query: `query Queues($startDate: DateTime, $endDate: DateTime) {
queues(filter: { startDate: $startDate, endDate: $endDate }, first: 25) {
data { idCall queue { id name } duration { queue wait talk } answered }
totalCount cursor hasNextPage
}
}`,
variables: {
startDate: "2025-10-01T00:00:00Z",
endDate: "2025-10-06T23:59:59Z",
},
}),
});
const { data } = await res.json();
console.log(data.queues);
|
Run it from Python
|
import os, requests
QUERY = """query Queues($startDate: DateTime, $endDate: DateTime) {
queues(filter: { startDate: $startDate, endDate: $endDate }, first: 25) {
data { idCall queue { id name } duration { queue wait talk } answered }
totalCount cursor hasNextPage
}
}"""
res = requests.post(
"https://statistics-api.telavox.se/graphql",
headers={
"Content-Type": "application/json",
"Authorization": f"Bearer {os.environ['TELAVOX_TOKEN']}",
},
json={
"query": QUERY,
"variables": {
"startDate": "2025-10-01T00:00:00Z",
"endDate": "2025-10-06T23:59:59Z",
},
},
)
print(res.json()["data"]["queues"])
|
Field reference
Field names use camelCase. Source: GraphQL schema introspection of QueueOverviewRecord.
|
{
"idCall": "String",
"callDirection": "String",
"active": "Int",
"answered": "Int",
"agentFirst": {
"id": "Int",
"accountId": "Int",
"name": "String",
"phone": "String",
"email": "String",
"answered": "DateTime",
"toTransfer": "Int",
"fromTransfer": "Int",
"licence": {
"ccpro": "Int",
"type": "String"
}
},
"agentLast": {
"id": "Int",
"accountId": "Int",
"name": "String",
"phone": "String",
"email": "String",
"answered": "DateTime",
"toTransfer": "Int",
"fromTransfer": "Int",
"licence": {
"ccpro": "Int",
"type": "String"
}
},
"ivr": {
"id": "Int",
"accountId": "Int",
"name": "String",
"phone": "String",
"timestamp": "DateTime"
},
"queue": {
"id": "Int",
"accountId": "Int",
"name": "String",
"phone": "String",
"strategy": "String",
"timestart": "DateTime",
"failReason": "String"
},
"customerTarget": {
"number": "String"
},
"onHookedCalls": "Int",
"transfer": "Int",
"recorded": "Int",
"time": {
"start": "DateTime",
"end": "DateTime"
},
"duration": {
"total": "Int",
"queue": "Int",
"ring": {
"first": "Int",
"last": "Int",
"total": "Int"
},
"talk": "Int",
"hold": "Int",
"wait": "Int"
},
"simultaneousDeviceCalls": "Int",
"agentEndReason": "String",
"voicemail": "Int",
"internal": "Int"
}
|
3.4 Agent Overview
One document per (call, agent) offer. An agent rung but who does not answer still produces a record; calls that never reach an agent emit nothing.
Field |
Value |
Root query |
agents |
Return type |
AgentOverviewConnection! |
Record type |
AgentOverviewRecord |
Live counterpart |
`/agents` (§ 2.3) |
Casing |
camelCase |
Example query
|
query Agents($startDate: DateTime, $endDate: DateTime) {
agents(filter: { startDate: $startDate, endDate: $endDate }, first: 25) {
data {
idCall
agent { id name }
queue { id name }
duration { total talk hold }
agentEndReason
}
totalCount cursor hasNextPage
}
}
|
Run it from Node.js
|
const res = await fetch("https://statistics-api.telavox.se/graphql", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${process.env.TELAVOX_TOKEN}`,
},
body: JSON.stringify({
query: `query Agents($startDate: DateTime, $endDate: DateTime) {
agents(filter: { startDate: $startDate, endDate: $endDate }, first: 25) {
data { idCall agent { id name } duration { total talk hold } agentEndReason }
totalCount cursor hasNextPage
}
}`,
variables: {
startDate: "2025-10-01T00:00:00Z",
endDate: "2025-10-06T23:59:59Z",
},
}),
});
const { data } = await res.json();
console.log(data.agents);
|
Run it from Python
|
import os, requests
QUERY = """query Agents($startDate: DateTime, $endDate: DateTime) {
agents(filter: { startDate: $startDate, endDate: $endDate }, first: 25) {
data { idCall agent { id name } duration { total talk hold } agentEndReason }
totalCount cursor hasNextPage
}
}"""
res = requests.post(
"https://statistics-api.telavox.se/graphql",
headers={
"Content-Type": "application/json",
"Authorization": f"Bearer {os.environ['TELAVOX_TOKEN']}",
},
json={
"query": QUERY,
"variables": {
"startDate": "2025-10-01T00:00:00Z",
"endDate": "2025-10-06T23:59:59Z",
},
},
)
print(res.json()["data"]["agents"])
|
Field reference
Field names use camelCase. Source: GraphQL schema introspection of AgentOverviewRecord.
|
{
"idCall": "String",
"callDirection": "String",
"active": "Int",
"answered": "Int",
"agent": {
"id": "Int",
"accountId": "Int",
"name": "String",
"phone": "String",
"email": "String",
"answered": "DateTime",
"toTransfer": "Int",
"fromTransfer": "Int",
"licence": {
"ccpro": "Int",
"type": "String"
}
},
"ivr": {
"id": "Int",
"accountId": "Int",
"name": "String",
"phone": "String",
"timestamp": "DateTime"
},
"queue": {
"id": "Int",
"accountId": "Int",
"name": "String",
"phone": "String",
"strategy": "String",
"timestart": "DateTime",
"failReason": "String"
},
"customerTarget": {
"number": "String"
},
"onHookedCalls": "Int",
"transfer": "Int",
"recorded": "Int",
"time": {
"start": "DateTime",
"end": "DateTime",
"answered": "DateTime"
},
"duration": {
"total": "Int",
"ring": "Int",
"talk": "Int",
"hold": "Int"
},
"simultaneousDeviceCalls": "Int",
"agentEndReason": "String",
"voicemail": "Int",
"internal": "Int"
}
|
3.5 Profile History
Per-agent profile slot timeline. Each record covers one continuous period of an agent in a given profile, plus a summary of the calls they handled inside that slot.
Field |
Value |
Root query |
profile_history |
Return type |
ProfileHistoryOverviewConnection! |
Record type |
ProfileHistoryOverviewRecord |
Live counterpart |
`/profile_history` (§ 2.5) |
Casing |
camelCase |
Example query
|
query ProfileHistory($startDate: DateTime, $endDate: DateTime) {
profile_history(filter: { startDate: $startDate, endDate: $endDate }, first: 25) {
data {
agent { id name }
slot { startDate endDate timeRange busy }
profileAvailable { name usage busy isWrapup }
callSummary { totalCalls totalAnswered duration { talk } }
}
totalCount cursor hasNextPage
}
}
|
Run it from Node.js
|
const res = await fetch("https://statistics-api.telavox.se/graphql", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${process.env.TELAVOX_TOKEN}`,
},
body: JSON.stringify({
query: `query ProfileHistory($startDate: DateTime, $endDate: DateTime) {
profile_history(filter: { startDate: $startDate, endDate: $endDate }, first: 25) {
data { agent { id name } slot { startDate endDate } callSummary { totalCalls } }
totalCount cursor hasNextPage
}
}`,
variables: {
startDate: "2025-10-01T00:00:00Z",
endDate: "2025-10-06T23:59:59Z",
},
}),
});
const { data } = await res.json();
console.log(data.profile_history);
|
Run it from Python
|
import os, requests
QUERY = """query ProfileHistory($startDate: DateTime, $endDate: DateTime) {
profile_history(filter: { startDate: $startDate, endDate: $endDate }, first: 25) {
data { agent { id name } slot { startDate endDate } callSummary { totalCalls } }
totalCount cursor hasNextPage
}
}"""
res = requests.post(
"https://statistics-api.telavox.se/graphql",
headers={
"Content-Type": "application/json",
"Authorization": f"Bearer {os.environ['TELAVOX_TOKEN']}",
},
json={
"query": QUERY,
"variables": {
"startDate": "2025-10-01T00:00:00Z",
"endDate": "2025-10-06T23:59:59Z",
},
},
)
print(res.json()["data"]["profile_history"])
|
Field reference
|
{
"agent": {
"id": "Int",
"accountId": "Int",
"name": "String",
"phone": "String",
"email": "String",
"answered": "DateTime",
"toTransfer": "Int",
"fromTransfer": "Int",
"licence": {
"ccpro": "Int",
"type": "String"
}
},
"slot": {
"startDate": "Float",
"endDate": "Float",
"timeRange": "Int",
"busy": "Int"
},
"profileAvailable": {
"id": "Int",
"name": "String",
"usage": "Int",
"busy": "Int",
"available": "Boolean",
"active": "Boolean",
"isWrapup": "Boolean",
"timesInProfile": "Int",
"timeStart": "Float"
},
"callSummary": {
"totalCalls": "Int",
"totalAnswered": "Int",
"duration": {
"talk": "Int"
},
"idCalls": "[String!]",
"answeredCalls": "[String!]",
"completedCalls": "[String!]",
"activeCalls": "[String!]"
}
}
|
Delete