Skip to content

Analytics ​

3 endpoints. Every path below is relative to the API base URL and already includes the /api/v1 prefix — see conventions for authentication, pagination, errors and idempotency.

Endpoint
GET/analytics/overviewThe §116 dashboard figures that belong to Observe
POST/analytics/queryRun an ad-hoc analytics query and return timeseries, KPI, table, histogram, percentile or funnel results
GET/analytics/vocabularyThe vocabulary a client needs to build a query UI without hard-coding it

Endpoints ​

The §116 dashboard figures that belong to Observe ​

GET/analytics/overview
  • Requires analytics.read.
  • Window is always start-of-today to now in the given timezone query param (default UTC); it cannot be changed
bash
curl -X GET "$CALLAGENT_API_URL/analytics/overview" \
  -H "Authorization: Bearer $CALLAGENT_API_KEY" \
  -H "Accept: application/json"
js
const response = await fetch(`${process.env.CALLAGENT_API_URL}/analytics/overview`, {
  method: 'GET',
  headers: {
    Authorization: `Bearer ${process.env.CALLAGENT_API_KEY}`,
    Accept: 'application/json',
  },
});

if (!response.ok) {
  const { error } = await response.json();
  throw new Error(`${error.type}: ${error.message}`);
}

const { data } = await response.json();
php
<?php

$handle = curl_init(getenv('CALLAGENT_API_URL') . '/analytics/overview');

curl_setopt_array($handle, [
    CURLOPT_CUSTOMREQUEST => 'GET',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('CALLAGENT_API_KEY'),
        'Accept: application/json',
    ],
]);

$body = json_decode(curl_exec($handle), true);
$status = curl_getinfo($handle, CURLINFO_HTTP_CODE);
curl_close($handle);

if ($status >= 400) {
    throw new RuntimeException("{$body['error']['type']}: {$body['error']['message']}");
}

$data = $body['data'];
python
import os, requests

response = requests.get(
    f"{os.environ['CALLAGENT_API_URL']}/analytics/overview",
    headers={"Authorization": f"Bearer {os.environ['CALLAGENT_API_KEY']}"},
    timeout=30,
)
response.raise_for_status()
data = response.json()["data"]

Responses

StatusBody
200data-wrapped object

Errors

StatusWhen it happens
403The API key lacks the analytics.read scope

Example response, generated from the schema:

json
{
  "data": {
    "range": {
      "from": "+40712345678",
      "to": "+40712345678",
      "timezone": "string"
    },
    "calls_today": {
      "value": 1,
      "previous": 1,
      "change_percent": 1,
      "metric": "string",
      "format": "2026-01-01T12:00:00+00:00",
      "sample": 1
    },
    "minutes": {
      "value": 1,
      "previous": 1,
      "change_percent": 1,
      "metric": "string",
      "format": "2026-01-01T12:00:00+00:00",
      "sample": 1
    },
    "cost": {
      "value": 1,
      "previous": 1,
      "change_percent": 1,
      "metric": "string",
      "format": "2026-01-01T12:00:00+00:00",
      "sample": 1
    },
    "success_rate": {
      "value": 1,
      "previous": 1,
      "change_percent": 1,
      "metric": "string",
      "format": "2026-01-01T12:00:00+00:00",
      "sample": 1
    },
    "pickup_rate": {
      "value": 1,
      "previous": 1,
      "change_percent": 1,
      "metric": "string",
      "format": "2026-01-01T12:00:00+00:00",
      "sample": 1
    },
    "latency": {
      "metric": "string",
      "format": "2026-01-01T12:00:00+00:00",
      "sample": 1,
      "percentiles": {}
    }
  }
}

Run an ad-hoc analytics query and return timeseries, KPI, table, histogram, percentile or funnel results ​

POST/analytics/query
  • Requires analytics.read.
  • An invalid_query 422 uses {"error":{"code","message"}} instead of the platform's {"error":{"type",...}} envelope, so error type-switching breaks here

Request body

FieldTypeRequiredNotes
typetimeseries · kpi · table · histogram · percentile · funnel … | null—
metricstring | null—
metricsstring[] | null—
dimensionstring | null—
filtersstring[] | null—
granularityhour · day · week · month · null | null—
presetstring | null—
fromstring | null—date-time
tostring | null—date-time
percentilesstring[] | null—
bucketsinteger | null—min 2. max 200
stepsstring[] | null—
limitinteger | null—min 1. max 1000
timezonestring | null—
bash
curl -X POST "$CALLAGENT_API_URL/analytics/query" \
  -H "Authorization: Bearer $CALLAGENT_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
       "type": "timeseries",
       "granularity": "hour",
       "from": "+40712345678",
       "to": "+40712345678",
       "buckets": 1,
       "limit": 25
     }'
js
const response = await fetch(`${process.env.CALLAGENT_API_URL}/analytics/query`, {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.CALLAGENT_API_KEY}`,
    Accept: 'application/json',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "type": "timeseries",
    "granularity": "hour",
    "from": "+40712345678",
    "to": "+40712345678",
    "buckets": 1,
    "limit": 25
  }),
});

if (!response.ok) {
  const { error } = await response.json();
  throw new Error(`${error.type}: ${error.message}`);
}

const { data } = await response.json();
php
<?php

$handle = curl_init(getenv('CALLAGENT_API_URL') . '/analytics/query');

curl_setopt_array($handle, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('CALLAGENT_API_KEY'),
        'Accept: application/json',
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
    'type' => 'timeseries',
    'granularity' => 'hour',
    'from' => '+40712345678',
    'to' => '+40712345678',
    'buckets' => 1,
    'limit' => 25,
]),
]);

$body = json_decode(curl_exec($handle), true);
$status = curl_getinfo($handle, CURLINFO_HTTP_CODE);
curl_close($handle);

if ($status >= 400) {
    throw new RuntimeException("{$body['error']['type']}: {$body['error']['message']}");
}

$data = $body['data'];
python
import os, requests

response = requests.post(
    f"{os.environ['CALLAGENT_API_URL']}/analytics/query",
    headers={"Authorization": f"Bearer {os.environ['CALLAGENT_API_KEY']}"},
    json={
      "type": "timeseries",
      "granularity": "hour",
      "from": "+40712345678",
      "to": "+40712345678",
      "buckets": 1,
      "limit": 25
    },
    timeout=30,
)
response.raise_for_status()
data = response.json()["data"]

Responses

StatusBody
200data-wrapped object

Errors

StatusWhen it happens
403The API key lacks the analytics.read scope
422The payload failed validation, or the query itself was rejected as invalid

Example response, generated from the schema:

json
{
  "data": {
    "value": 1,
    "previous": 1,
    "change_percent": 1,
    "metric": "string",
    "format": "2026-01-01T12:00:00+00:00",
    "sample": 1
  }
}

The vocabulary a client needs to build a query UI without hard-coding it ​

GET/analytics/vocabulary
  • Requires analytics.read.
bash
curl -X GET "$CALLAGENT_API_URL/analytics/vocabulary" \
  -H "Authorization: Bearer $CALLAGENT_API_KEY" \
  -H "Accept: application/json"
js
const response = await fetch(`${process.env.CALLAGENT_API_URL}/analytics/vocabulary`, {
  method: 'GET',
  headers: {
    Authorization: `Bearer ${process.env.CALLAGENT_API_KEY}`,
    Accept: 'application/json',
  },
});

if (!response.ok) {
  const { error } = await response.json();
  throw new Error(`${error.type}: ${error.message}`);
}

const { data } = await response.json();
php
<?php

$handle = curl_init(getenv('CALLAGENT_API_URL') . '/analytics/vocabulary');

curl_setopt_array($handle, [
    CURLOPT_CUSTOMREQUEST => 'GET',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('CALLAGENT_API_KEY'),
        'Accept: application/json',
    ],
]);

$body = json_decode(curl_exec($handle), true);
$status = curl_getinfo($handle, CURLINFO_HTTP_CODE);
curl_close($handle);

if ($status >= 400) {
    throw new RuntimeException("{$body['error']['type']}: {$body['error']['message']}");
}

$data = $body['data'];
python
import os, requests

response = requests.get(
    f"{os.environ['CALLAGENT_API_URL']}/analytics/vocabulary",
    headers={"Authorization": f"Bearer {os.environ['CALLAGENT_API_KEY']}"},
    timeout=30,
)
response.raise_for_status()
data = response.json()["data"]

Responses

StatusBody
200data-wrapped object

Errors

StatusWhen it happens
403The API key lacks the analytics.read scope

Example response, generated from the schema:

json
{
  "data": {
    "metrics": {},
    "dimensions": {},
    "widget_types": {},
    "granularities": {}
  }
}

Documented against the code. Anything not yet implemented is labelled, never described as working.