Skip to content

Boards ​

9 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/boardsList the organization's analytics boards with their widget definitions, ordered by name
POST/boardsCreate an analytics board, generating a slug from the name when none is given
GET/boards/{boardId}Fetch one analytics board with its widget definitions
PATCH/boards/{boardId}Update an analytics board's name, description, filters, time window or layout settings
DELETE/boards/{boardId}Delete an analytics board
GET/boards/{boardId}/dataRender every widget on a board in one call, with optional time-range and timezone overrides
POST/boards/{boardId}/widgetsAdd a widget to a board, storing its saved analytics query and its grid position
PATCH/boards/{boardId}/widgets/{widgetId}Update a board widget's query, presentation or grid position
DELETE/boards/{boardId}/widgets/{widgetId}Remove a widget from a board

Endpoints ​

List the organization's analytics boards with their widget definitions, ordered by name ​

GET/boards
  • Requires analytics.read.
  • Returns all boards; there is no pagination or filtering on this endpoint
bash
curl -X GET "$CALLAGENT_API_URL/boards" \
  -H "Authorization: Bearer $CALLAGENT_API_KEY" \
  -H "Accept: application/json"
js
const response = await fetch(`${process.env.CALLAGENT_API_URL}/boards`, {
  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') . '/boards');

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']}/boards",
    headers={"Authorization": f"Bearer {os.environ['CALLAGENT_API_KEY']}"},
    timeout=30,
)
response.raise_for_status()
data = response.json()["data"]

Responses

StatusBody
200data-wrapped array

Errors

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

Example response, generated from the schema:

json
{
  "data": [
    "string"
  ]
}

Create an analytics board, generating a slug from the name when none is given ​

POST/boards
  • Requires board.write.

Request body

FieldTypeRequiredNotes
namestringyesmax 255 chars
descriptionstring | null—
slugstring | null—max 96 chars
time_presetstring | null—max 24 chars
timezonestring | null—
filtersstring[] | null—
refresh_interval_secondsinteger | null—min 0. max 3600
is_defaultboolean | null—
bash
curl -X POST "$CALLAGENT_API_URL/boards" \
  -H "Authorization: Bearer $CALLAGENT_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
       "name": "Support Line",
       "refresh_interval_seconds": 30,
       "is_default": true
     }'
js
const response = await fetch(`${process.env.CALLAGENT_API_URL}/boards`, {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.CALLAGENT_API_KEY}`,
    Accept: 'application/json',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "name": "Support Line",
    "refresh_interval_seconds": 30,
    "is_default": true
  }),
});

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') . '/boards');

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([
    'name' => 'Support Line',
    'refresh_interval_seconds' => 30,
    'is_default' => true,
]),
]);

$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']}/boards",
    headers={"Authorization": f"Bearer {os.environ['CALLAGENT_API_KEY']}"},
    json={
      "name": "Support Line",
      "refresh_interval_seconds": 30,
      "is_default": True
    },
    timeout=30,
)
response.raise_for_status()
data = response.json()["data"]

Responses

StatusBody
201data-wrapped object

Errors

StatusWhen it happens
403The API key lacks the board.write scope
422The payload failed validation (name is required)

Example response, generated from the schema:

json
{
  "data": {
    "id": "01JQ0000000000000000000000",
    "name": "Support Line",
    "slug": "string",
    "description": "string",
    "time_preset": "string",
    "timezone": "string",
    "filters": {},
    "refresh_interval_seconds": 30,
    "is_default": true,
    "widgets": [
      "string"
    ]
  }
}

Fetch one analytics board with its widget definitions ​

GET/boards/{boardId}
  • Requires analytics.read.
  • Returns widget definitions only; the rendered figures come from GET /boards/{boardId}/data

Path parameters

NameTypeRequiredNotes
boardIdstringyes
bash
curl -X GET "$CALLAGENT_API_URL/boards/01JQ0000000000000000000000" \
  -H "Authorization: Bearer $CALLAGENT_API_KEY" \
  -H "Accept: application/json"
js
const response = await fetch(`${process.env.CALLAGENT_API_URL}/boards/01JQ0000000000000000000000`, {
  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') . '/boards/01JQ0000000000000000000000');

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']}/boards/01JQ0000000000000000000000",
    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
404No board with that id in this organization

Example response, generated from the schema:

json
{
  "data": {
    "id": "01JQ0000000000000000000000",
    "name": "Support Line",
    "slug": "string",
    "description": "string",
    "time_preset": "string",
    "timezone": "string",
    "filters": {},
    "refresh_interval_seconds": 30,
    "is_default": true,
    "widgets": [
      "string"
    ]
  }
}

Update an analytics board's name, description, filters, time window or layout settings ​

PATCH/boards/{boardId}
  • Requires board.write.
  • No validation runs here, so no 422 is possible. Accepts time_from, time_to and columns, which the create endpoint does not

Path parameters

NameTypeRequiredNotes
boardIdstringyes
bash
curl -X PATCH "$CALLAGENT_API_URL/boards/01JQ0000000000000000000000" \
  -H "Authorization: Bearer $CALLAGENT_API_KEY" \
  -H "Accept: application/json"
js
const response = await fetch(`${process.env.CALLAGENT_API_URL}/boards/01JQ0000000000000000000000`, {
  method: 'PATCH',
  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') . '/boards/01JQ0000000000000000000000');

curl_setopt_array($handle, [
    CURLOPT_CUSTOMREQUEST => 'PATCH',
    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.patch(
    f"{os.environ['CALLAGENT_API_URL']}/boards/01JQ0000000000000000000000",
    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 board.write scope
404No board with that id in this organization

Example response, generated from the schema:

json
{
  "data": {
    "id": "01JQ0000000000000000000000",
    "name": "Support Line",
    "slug": "string",
    "description": "string",
    "time_preset": "string",
    "timezone": "string",
    "filters": {},
    "refresh_interval_seconds": 30,
    "is_default": true,
    "widgets": [
      "string"
    ]
  }
}

Delete an analytics board ​

DELETE/boards/{boardId}
  • Requires board.write.
  • This response is not data-wrapped.
  • 204 with no body. The board is resolved before the scope check, so an unknown id returns 404 even for an unscoped key

Path parameters

NameTypeRequiredNotes
boardIdstringyes
bash
curl -X DELETE "$CALLAGENT_API_URL/boards/01JQ0000000000000000000000" \
  -H "Authorization: Bearer $CALLAGENT_API_KEY" \
  -H "Accept: application/json"
js
const response = await fetch(`${process.env.CALLAGENT_API_URL}/boards/01JQ0000000000000000000000`, {
  method: 'DELETE',
  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') . '/boards/01JQ0000000000000000000000');

curl_setopt_array($handle, [
    CURLOPT_CUSTOMREQUEST => 'DELETE',
    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.delete(
    f"{os.environ['CALLAGENT_API_URL']}/boards/01JQ0000000000000000000000",
    headers={"Authorization": f"Bearer {os.environ['CALLAGENT_API_KEY']}"},
    timeout=30,
)
response.raise_for_status()
data = response.json()["data"]

Responses

StatusBody
204No content

Errors

StatusWhen it happens
403The API key lacks the board.write scope
404No board with that id in this organization

Render every widget on a board in one call, with optional time-range and timezone overrides ​

GET/boards/{boardId}/data
  • Requires analytics.read.
  • A widget whose query fails is not an error: its envelope carries an error field while the response stays 200

Path parameters

NameTypeRequiredNotes
boardIdstringyes

Query parameters

NameTypeRequiredNotes
presetstring—
fromstring—
tostring—
timezonestring—
bash
curl -X GET "$CALLAGENT_API_URL/boards/01JQ0000000000000000000000/data?preset=string&from=%2B40712345678" \
  -H "Authorization: Bearer $CALLAGENT_API_KEY" \
  -H "Accept: application/json"
js
const response = await fetch(`${process.env.CALLAGENT_API_URL}/boards/01JQ0000000000000000000000/data?preset=string&from=%2B40712345678`, {
  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') . '/boards/01JQ0000000000000000000000/data?preset=string&from=%2B40712345678');

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']}/boards/01JQ0000000000000000000000/data?preset=string&from=%2B40712345678",
    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
404No board with that id in this organization

Example response, generated from the schema:

json
{
  "data": {
    "board": {
      "id": "01JQ0000000000000000000000",
      "name": "Support Line",
      "timezone": null,
      "preset": null
    },
    "widgets": [
      {}
    ]
  }
}

Add a widget to a board, storing its saved analytics query and its grid position ​

POST/boards/{boardId}/widgets
  • Requires board.write.
  • metric and dimension are validated only as short strings but cast to enums, so an unrecognised value yields a 500, not a 422

Path parameters

NameTypeRequiredNotes
boardIdstringyes

Request body

FieldTypeRequiredNotes
titlestringyesmax 255 chars
subtitlestring | null—max 255 chars
typetimeseries · kpi · table · histogram · percentile · funnelyes
metricstringyesmax 32 chars
metricsstring[] | null—
dimensionstring | null—max 32 chars
filtersstring[] | null—
granularityhour · day · week · month · null | null—
optionsstring[] | null—
position_xinteger | null—min 0. max 24
position_yinteger | null—min 0
widthinteger | null—min 1. max 12
heightinteger | null—min 1. max 12
bash
curl -X POST "$CALLAGENT_API_URL/boards/01JQ0000000000000000000000/widgets" \
  -H "Authorization: Bearer $CALLAGENT_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
       "title": "string",
       "type": "timeseries",
       "metric": "string",
       "granularity": "hour",
       "position_x": 1,
       "position_y": 1,
       "width": 1
     }'
js
const response = await fetch(`${process.env.CALLAGENT_API_URL}/boards/01JQ0000000000000000000000/widgets`, {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.CALLAGENT_API_KEY}`,
    Accept: 'application/json',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "title": "string",
    "type": "timeseries",
    "metric": "string",
    "granularity": "hour",
    "position_x": 1,
    "position_y": 1,
    "width": 1
  }),
});

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') . '/boards/01JQ0000000000000000000000/widgets');

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([
    'title' => 'string',
    'type' => 'timeseries',
    'metric' => 'string',
    'granularity' => 'hour',
    'position_x' => 1,
    'position_y' => 1,
    'width' => 1,
]),
]);

$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']}/boards/01JQ0000000000000000000000/widgets",
    headers={"Authorization": f"Bearer {os.environ['CALLAGENT_API_KEY']}"},
    json={
      "title": "string",
      "type": "timeseries",
      "metric": "string",
      "granularity": "hour",
      "position_x": 1,
      "position_y": 1,
      "width": 1
    },
    timeout=30,
)
response.raise_for_status()
data = response.json()["data"]

Responses

StatusBody
201Columns with database defaults (granularity, type, position) are not in memory after an insert; refreshing means the presenter reads real values instead of dereferencing a null cast.

Errors

StatusWhen it happens
403The API key lacks the board.write scope
404No board with that id in this organization
422The payload failed validation (title, type and metric are required)

Example response, generated from the schema:

json
{
  "data": {
    "id": "01JQ0000000000000000000000",
    "title": "string",
    "subtitle": "string",
    "type": "string",
    "metric": "string",
    "metrics": {},
    "dimension": "string",
    "filters": {},
    "granularity": "string",
    "options": {},
    "layout": {
      "x": 1,
      "y": 1,
      "w": 1,
      "h": 1
    }
  }
}

Update a board widget's query, presentation or grid position ​

PATCH/boards/{boardId}/widgets/{widgetId}
  • Requires board.write.
  • No validation runs here, so no 422 is possible; an unrecognised metric or type is stored and then breaks the enum cast with a 500

Path parameters

NameTypeRequiredNotes
boardIdstringyes
widgetIdstringyes
bash
curl -X PATCH "$CALLAGENT_API_URL/boards/01JQ0000000000000000000000/widgets/01JQ0000000000000000000000" \
  -H "Authorization: Bearer $CALLAGENT_API_KEY" \
  -H "Accept: application/json"
js
const response = await fetch(`${process.env.CALLAGENT_API_URL}/boards/01JQ0000000000000000000000/widgets/01JQ0000000000000000000000`, {
  method: 'PATCH',
  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') . '/boards/01JQ0000000000000000000000/widgets/01JQ0000000000000000000000');

curl_setopt_array($handle, [
    CURLOPT_CUSTOMREQUEST => 'PATCH',
    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.patch(
    f"{os.environ['CALLAGENT_API_URL']}/boards/01JQ0000000000000000000000/widgets/01JQ0000000000000000000000",
    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 board.write scope
404No such board, or no such widget on that board

Example response, generated from the schema:

json
{
  "data": {
    "id": "01JQ0000000000000000000000",
    "title": "string",
    "subtitle": "string",
    "type": "string",
    "metric": "string",
    "metrics": {},
    "dimension": "string",
    "filters": {},
    "granularity": "string",
    "options": {},
    "layout": {
      "x": 1,
      "y": 1,
      "w": 1,
      "h": 1
    }
  }
}

Remove a widget from a board ​

DELETE/boards/{boardId}/widgets/{widgetId}
  • Requires board.write.
  • This response is not data-wrapped.

Path parameters

NameTypeRequiredNotes
boardIdstringyes
widgetIdstringyes
bash
curl -X DELETE "$CALLAGENT_API_URL/boards/01JQ0000000000000000000000/widgets/01JQ0000000000000000000000" \
  -H "Authorization: Bearer $CALLAGENT_API_KEY" \
  -H "Accept: application/json"
js
const response = await fetch(`${process.env.CALLAGENT_API_URL}/boards/01JQ0000000000000000000000/widgets/01JQ0000000000000000000000`, {
  method: 'DELETE',
  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') . '/boards/01JQ0000000000000000000000/widgets/01JQ0000000000000000000000');

curl_setopt_array($handle, [
    CURLOPT_CUSTOMREQUEST => 'DELETE',
    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.delete(
    f"{os.environ['CALLAGENT_API_URL']}/boards/01JQ0000000000000000000000/widgets/01JQ0000000000000000000000",
    headers={"Authorization": f"Bearer {os.environ['CALLAGENT_API_KEY']}"},
    timeout=30,
)
response.raise_for_status()
data = response.json()["data"]

Responses

StatusBody
204No content

Errors

StatusWhen it happens
403The API key lacks the board.write scope
404No such board, or no such widget on that board

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