Dashboards
The CubeAPM Dashboard APIs allow you to programmatically create, read, update, and delete dashboards and their panels.
Accessing Dashboard APIs
The Dashboard APIs are available on the Admin Port (default: 3199).
Example Endpoints:
http://<cubeapm-admin-host>:3199/api/dashboards/api/v1/dashboardshttp://<cubeapm-admin-host>:3199/api/dashboards/api/v1/panels?dashboard_id=1
Supported HTTP APIs
| API Endpoint | Method | Description |
|---|---|---|
/api/dashboards/api/v1/dashboards | GET, POST, PUT, DELETE | Manage dashboards. |
/api/dashboards/api/v1/panels?dashboard_id={id} | POST, PUT, DELETE | Manage panels on an existing dashboard. |
There is no separate GET endpoint for panels. To read panels, use Get Dashboards with ?id={dashboard_id} — the response includes the full panels array.
Authentication
These APIs can be accessed programmatically using the Admin Port 3199.
Headers Required:
- (Optional)
Authorization: Bearer <ADMIN_TOKEN> Content-Type: application/json
When http-token-admin is set in CubeAPM's config.properties, all Dashboard API requests (including GET) must include the corresponding token in the Authorization header. If http-token-admin is empty, the header can be omitted.
Dashboards
Create Dashboard
Endpoint: POST http://<cubeapm-admin-host>:3199/api/dashboards/api/v1/dashboards
Creates a new dashboard. Panels can be included in the same request.
Request Parameters
| Parameter | Type | Description |
|---|---|---|
title | string | Required. Display name of the dashboard. |
variables | array | Dashboard template variables. Pass [] if none. See Variables. |
panels | array | List of panel objects to create with the dashboard. See Panel Type Examples. |
permissions | array | Access control. Pass [] for default role-based access. See Permissions. |
Curl Example
curl -X POST "http://<cubeapm-admin-host>:3199/api/dashboards/api/v1/dashboards" \
-H "Authorization: Bearer <ADMIN_TOKEN>" \
-H "Content-Type: application/json" \
-d '{
"title": "Service Overview",
"variables": [],
"panels": [
{
"type": "linechart",
"title": "Request Rate",
"layout": { "x": 0, "y": 0, "w": 6, "h": 4 },
"config": {
"query": "",
"unit": "number",
"queries": [
{
"title": "Requests",
"query": "sum(rate(cube_apm_calls_total[5m]))",
"unit": "number",
"formula": "last"
}
],
"legend": { "label": "", "formula": "last", "pos": "bottom" }
}
}
],
"permissions": []
}'
Response Format
Returns the created dashboard object:
| Field | Type | Description |
|---|---|---|
id | integer | Unique identifier for the dashboard. |
title | string | Dashboard title. |
variables | array | Dashboard variables. |
panels | array | Created panels with assigned IDs. |
permissions | array | Permission entries applied to the dashboard. |
Example:
{
"id": 1,
"title": "Service Overview",
"variables": [],
"panels": [
{
"id": 1,
"type": "linechart",
"title": "Request Rate",
"layout": { "x": 0, "y": 0, "w": 6, "h": 4 },
"config": {
"query": "",
"unit": "number",
"queries": [
{
"title": "Requests",
"query": "sum(rate(cube_apm_calls_total[5m]))",
"unit": "number",
"formula": "last"
}
],
"legend": { "label": "", "formula": "last", "pos": "bottom" }
},
"dashboardId": 1
}
],
"permissions": []
}
Get Dashboards
Endpoint: GET http://<cubeapm-admin-host>:3199/api/dashboards/api/v1/dashboards
Curl Examples & Response Format
- Get Single Dashboard by ID
- Get All Dashboards
Request:
curl -X GET "http://<cubeapm-admin-host>:3199/api/dashboards/api/v1/dashboards?id=1" \
-H "Authorization: Bearer <ADMIN_TOKEN>"
Response:
Returns a single dashboard object with its panels:
{
"id": 1,
"title": "Service Overview",
"variables": [],
"panels": [
{
"id": 1,
"type": "linechart",
"title": "Request Rate",
"layout": { "x": 0, "y": 0, "w": 6, "h": 4 },
"config": {
"query": "",
"unit": "number",
"queries": [
{
"title": "Requests",
"query": "sum(rate(cube_apm_calls_total[5m]))",
"unit": "number",
"formula": "last"
}
],
"legend": { "label": "", "formula": "last", "pos": "bottom" }
},
"dashboardId": 1
}
],
"permission": "admin"
}
Request:
curl -X GET "http://<cubeapm-admin-host>:3199/api/dashboards/api/v1/dashboards" \
-H "Authorization: Bearer <ADMIN_TOKEN>"
Response:
Returns a JSON array of dashboard objects. The list endpoint does not include panel details (panels array is empty); use the single-dashboard endpoint to fetch panels.
[
{
"id": 1,
"title": "Service Overview",
"variables": [],
"panels": [],
"permission": "admin"
}
]
| Field | Type | Description |
|---|---|---|
id | integer | Unique identifier for the dashboard. |
title | string | Dashboard title. |
variables | array | Dashboard template variables. |
panels | array | Panels on the dashboard (populated only in single-dashboard GET). |
permission | string | Effective access level of the requesting user (viewer, editor, admin). |
Update Dashboard
Endpoint: PUT http://<cubeapm-admin-host>:3199/api/dashboards/api/v1/dashboards
Updates dashboard metadata (title, variables) and panel layouts. To add, update, or remove panel content, use the Panels API.
| Parameter | Type | Description |
|---|---|---|
id | integer | Required. Dashboard ID to update. |
title | string | Required. Display name. |
variables | array | Replaces the dashboard's variables. |
panels | array | Existing panels by id; only layout is applied. |
permissions | array | Replaces custom access control. Pass [] for role-based. |
The PUT request updates the dashboard's title, variables, and permissions. For panels included in the payload, only the layout field is updated on existing panels — panel type, title, and config are not changed via this endpoint.
Curl Example
curl -X PUT "http://<cubeapm-admin-host>:3199/api/dashboards/api/v1/dashboards" \
-H "Authorization: Bearer <ADMIN_TOKEN>" \
-H "Content-Type: application/json" \
-d '{
"id": 1,
"title": "Service Overview (Updated)",
"variables": [],
"panels": [
{
"id": 1,
"layout": { "x": 0, "y": 0, "w": 12, "h": 4 }
}
],
"permissions": []
}'
Returns 204 No Content on success.
Delete Dashboard
Endpoint: DELETE http://<cubeapm-admin-host>:3199/api/dashboards/api/v1/dashboards?id={id}
Deletes a dashboard and its associated resource permissions.
curl -X DELETE "http://<cubeapm-admin-host>:3199/api/dashboards/api/v1/dashboards?id=1" \
-H "Authorization: Bearer <ADMIN_TOKEN>"
Returns 204 No Content on success.
Panels
Panels can be managed individually after a dashboard is created. To read existing panels, use Get Dashboards with a dashboard id — there is no separate panels GET endpoint.
Panel Object
| Field | Type | Description |
|---|---|---|
id | integer | Panel ID (required for PUT/DELETE, omit for POST). |
type | string | Panel type. See Panel Types. |
title | string | Required. Display title for the panel. |
layout | object | Grid position: x, y, w, h. |
config | object | Panel-specific configuration (queries, legend, etc.). |
Panel Types
| Type | Description |
|---|---|
linechart | Time-series line chart |
scorecard | Single-value metric display |
table | Tabular data |
logs | Log viewer panel |
markdown | Static markdown text |
bar_horizontal | Horizontal bar chart |
pie | Pie chart |
treemap | Treemap visualization |
Panel Config
Each panel has a config object. Most query-based panels use a queries array (preferred) or a top-level query field. Common fields:
| Field | Type | Description |
|---|---|---|
queries | array | List of query objects. Each item supports title, query, unit, formula (last, avg, sum), and optional datasource. |
query | string | Legacy single-query fallback (used only when queries is empty). Prefer queries. |
unit | string | number or time. |
legend | object | label (string or array), formula (last, avg, sum), pos (none, bottom, right). |
stack | boolean | (linechart) Stack series. |
showSearch | boolean | (table, logs) Show search bar. |
defaultSortCol | integer | (table) Column index to sort by default. |
logs | object | (logs) Display options — showFieldsSelector, showAllFields, showMenu, showText, showStream, loadOnScroll. |
markdown | object | (markdown) text and optional fontSize. |
Panel Type Examples
- Line Chart
- Scorecard
- Table
- Logs
- Markdown
- Horizontal Bar
- Pie Chart
- Treemap
{
"type": "linechart",
"title": "Request Rate",
"layout": { "x": 0, "y": 0, "w": 6, "h": 4 },
"config": {
"query": "",
"unit": "number",
"queries": [
{
"title": "Requests",
"query": "sum(rate(cube_apm_calls_total[5m]))",
"unit": "number",
"formula": "last"
}
],
"legend": { "label": "", "formula": "last", "pos": "bottom" },
"stack": false
}
}
{
"type": "scorecard",
"title": "Avg Latency",
"layout": { "x": 0, "y": 0, "w": 3, "h": 2 },
"config": {
"query": "",
"unit": "time",
"queries": [
{
"title": "Latency",
"query": "sum(rate(cube_apm_latency_total[5m])) / sum(rate(cube_apm_calls_total[5m]))",
"unit": "time",
"formula": "avg"
}
],
"legend": { "label": "", "formula": "last", "pos": "none" }
}
}
{
"type": "table",
"title": "Top Services",
"layout": { "x": 0, "y": 0, "w": 6, "h": 4 },
"config": {
"query": "",
"unit": "number",
"queries": [
{
"title": "Calls",
"query": "topk(10, sum by (service) (rate(cube_apm_calls_total[5m])))",
"unit": "number",
"formula": "last"
}
],
"legend": { "label": [{ "label": "service" }], "formula": "last", "pos": "none" },
"showSearch": true,
"defaultSortCol": 1
}
}
{
"type": "logs",
"title": "Error Logs",
"layout": { "x": 0, "y": 0, "w": 12, "h": 6 },
"config": {
"query": "",
"unit": "number",
"queries": [
{
"title": "Errors",
"query": "{env=\"production\"} severity=\"ERROR\"",
"unit": "number",
"datasource": "vlogs",
"formula": "last"
}
],
"legend": { "label": [{ "label": "service.name" }], "formula": "last", "pos": "none" },
"showSearch": true,
"logs": {
"showFieldsSelector": true,
"showAllFields": false,
"showMenu": true,
"showText": true,
"showStream": true,
"loadOnScroll": true
}
}
}
{
"type": "markdown",
"title": "Notes",
"layout": { "x": 0, "y": 0, "w": 6, "h": 2 },
"config": {
"query": "",
"unit": "number",
"legend": { "label": "", "formula": "last", "pos": "none" },
"markdown": {
"text": "# Service Overview\n\nThis dashboard monitors production services.",
"fontSize": 14
}
}
}
{
"type": "bar_horizontal",
"title": "Errors by Service",
"layout": { "x": 0, "y": 0, "w": 6, "h": 4 },
"config": {
"query": "",
"unit": "number",
"queries": [
{
"title": "Errors",
"query": "topk(10, sum by (service) (rate(cube_apm_errors_total[5m])))",
"unit": "number",
"formula": "sum"
}
],
"legend": { "label": "service", "formula": "last", "pos": "none" }
}
}
{
"type": "pie",
"title": "Traffic by Service",
"layout": { "x": 0, "y": 0, "w": 4, "h": 4 },
"config": {
"query": "",
"unit": "number",
"queries": [
{
"title": "Calls",
"query": "sum by (service) (rate(cube_apm_calls_total[5m]))",
"unit": "number",
"formula": "sum"
}
],
"legend": { "label": "service", "formula": "last", "pos": "none" }
}
}
{
"type": "treemap",
"title": "Latency by Endpoint",
"layout": { "x": 0, "y": 0, "w": 6, "h": 4 },
"config": {
"query": "",
"unit": "number",
"queries": [
{
"title": "Latency",
"query": "sum by (endpoint) (rate(cube_apm_latency_total[5m]))",
"unit": "time",
"formula": "sum"
}
],
"legend": { "label": "endpoint", "formula": "last", "pos": "none" }
}
}
Layout Object
| Field | Type | Description |
|---|---|---|
x | integer | Column position in the grid. |
y | integer | Row position in the grid. |
w | integer | Width in grid units. |
h | integer | Height in grid units. |
Create Panel
Endpoint: POST http://<cubeapm-admin-host>:3199/api/dashboards/api/v1/panels?dashboard_id={id}
curl -X POST "http://<cubeapm-admin-host>:3199/api/dashboards/api/v1/panels?dashboard_id=1" \
-H "Authorization: Bearer <ADMIN_TOKEN>" \
-H "Content-Type: application/json" \
-d '{
"type": "scorecard",
"title": "Error Rate",
"layout": { "x": 6, "y": 0, "w": 3, "h": 2 },
"config": {
"query": "",
"unit": "number",
"queries": [
{
"title": "Errors",
"query": "sum(rate(cube_apm_errors_total[5m]))",
"unit": "number",
"formula": "last"
}
],
"legend": { "label": "", "formula": "last", "pos": "none" }
}
}'
Response Format
Returns the created panel object:
{
"id": 2,
"type": "scorecard",
"title": "Error Rate",
"layout": { "x": 6, "y": 0, "w": 3, "h": 2 },
"config": {
"query": "",
"unit": "number",
"queries": [
{
"title": "Errors",
"query": "sum(rate(cube_apm_errors_total[5m]))",
"unit": "number",
"formula": "last"
}
],
"legend": { "label": "", "formula": "last", "pos": "none" }
},
"dashboardId": 1
}
Update Panel
Endpoint: PUT http://<cubeapm-admin-host>:3199/api/dashboards/api/v1/panels?dashboard_id={id}
Panel type cannot be changed after creation. Include the full panel object with the existing id and type.
curl -X PUT "http://<cubeapm-admin-host>:3199/api/dashboards/api/v1/panels?dashboard_id=1" \
-H "Authorization: Bearer <ADMIN_TOKEN>" \
-H "Content-Type: application/json" \
-d '{
"id": 2,
"type": "scorecard",
"title": "Error Rate (Updated)",
"layout": { "x": 6, "y": 0, "w": 4, "h": 2 },
"config": {
"query": "",
"unit": "number",
"queries": [
{
"title": "Errors",
"query": "sum(rate(cube_apm_errors_total[5m]))",
"unit": "number",
"formula": "last"
}
],
"legend": { "label": "", "formula": "last", "pos": "none" }
}
}'
Returns 204 No Content on success.
Delete Panel
Endpoint: DELETE http://<cubeapm-admin-host>:3199/api/dashboards/api/v1/panels?dashboard_id={dashboard_id}&id={panel_id}
curl -X DELETE "http://<cubeapm-admin-host>:3199/api/dashboards/api/v1/panels?dashboard_id=1&id=2" \
-H "Authorization: Bearer <ADMIN_TOKEN>"
Returns 204 No Content on success.
Dashboard Variables
Variables let dashboards accept dynamic filters (e.g., environment, service name). Reference them in panel queries using {{variable_name}} syntax — CubeAPM substitutes the user's selected value at query time.
| Field | Type | Description |
|---|---|---|
name | string | Variable identifier. Referenced in queries as {{name}}. |
options | array | Selectable values for static variables. Leave empty ([]) for dynamic variables. |
label | string | (Optional) Metric label or log/trace field whose values are auto-discovered for the dropdown. Used only for dynamic variables. Example: service, env, service.name. |
match | string | (Optional) Series selector used only to discover dropdown values for label — not what panels query. For metrics: a PromQL selector (e.g. cube_apm_calls_total or cube_apm_calls_total{env="production"}). For logs/traces: a query filter (empty defaults to *). |
datasource | string | (Optional) Where to discover values: prometheus (metric labels), vlogs (log fields), or traces (span fields). Required when label is set. Maps to the UI “Source of options” choices other than “Static list”. |
selection | string | single or multiple. Use multiple for PromQL label matchers like env{{env}} (inserts ="value" / =~"..."). Use single only when you want the raw value (e.g. env="{{env}}"). |
initialValue | object | (Optional) Default selection. Shape: { "items": ["value"] }. For multiple, you may also set "all": true (match all label values) or "exclude": true (negate the selection). |
Static vs dynamic variables
| Mode | Configure with | Dropdown options come from |
|---|---|---|
| Static | options only (do not set label) | The fixed list you provide |
| Dynamic | label + datasource (+ optional match) | Live discovery from metrics, logs, or traces |
When both label and match are set:
- CubeAPM uses
matchto narrow which series/logs/traces to look at. - From that subset, it collects distinct values of the field/label named in
label.
Example: label: "service" with match: "cube_apm_calls_total" lists services that appear on that metric. Omit match to list every distinct service value across all metrics in the time range.
- Use
{{env}}in queries to refer to the built-in environment picker (cube:envvariable). - Use
{[label]}in queries to refer to a chart label value (e.g.,{[service]}). - PromQL label matchers (
env{{env}}) requireselection: "multiple". Substitution then inserts the operator and quotes: one value →="order", several →=~"order|payment"(orin(...)for logs/traces). selection: "single"inserts the raw value only (no=/ quotes). Use that for non-matcher contexts (e.g.env="{{env}}"or URL path segments), not with theenv{{env}}pattern.
Example: Static variables
curl -X POST "http://<cubeapm-admin-host>:3199/api/dashboards/api/v1/dashboards" \
-H "Authorization: Bearer <ADMIN_TOKEN>" \
-H "Content-Type: application/json" \
-d '{
"title": "Service Dashboard",
"variables": [
{
"name": "env",
"options": ["production", "staging"],
"selection": "multiple",
"initialValue": { "items": ["production"] }
},
{
"name": "service",
"options": ["order", "payment"],
"selection": "multiple",
"initialValue": { "items": ["order"] }
}
],
"panels": [
{
"type": "linechart",
"title": "Request Rate",
"layout": { "x": 0, "y": 0, "w": 6, "h": 4 },
"config": {
"query": "",
"unit": "number",
"queries": [
{
"title": "Requests",
"query": "sum(rate(cube_apm_calls_total{env{{env}},service{{service}}}[5m]))",
"unit": "number",
"formula": "last"
}
],
"legend": { "label": "", "formula": "last", "pos": "bottom" }
}
}
],
"permissions": []
}'
When env is production and service is order, the query above resolves to:
sum(rate(cube_apm_calls_total{env="production",service="order"}[5m]))
Do not use selection: "single" with the env{{env}} matcher pattern. With no value selected, {{env}} becomes empty and the query turns into invalid PromQL like cube_apm_calls_total{env,service} (VictoriaMetrics then errors with cannot find WITH template for "env").
Example: Dynamic variables (label + match)
{
"variables": [
{
"name": "service",
"options": [],
"label": "service",
"match": "cube_apm_calls_total",
"datasource": "prometheus",
"selection": "multiple",
"initialValue": { "items": ["order"] }
}
]
}
How to Configure Role Based and Custom Permissions
The permissions array in the JSON payload controls who can view and edit a dashboard.
For global roles, teams, and how permissions work in the UI, see Roles and Permissions and Teams.
1. Role Based (Default)
When role-based access is used, the dashboard inherits the user's global workspace role (viewers can view, editors can edit).
Pass an empty array:
{
"...": "...",
"permissions": []
}
2. Custom
Grant specific roles (viewer, editor, or admin) to individual users (by email) or teams (by ID):
{
"...": "...",
"permissions": [
{
"entity_type": "user",
"entity_id": "john.doe@company.com",
"permission": "editor"
},
{
"entity_type": "team",
"entity_id": "4",
"permission": "viewer"
}
]
}
Custom permissions form an allowlist in the CubeAPM UI. Only listed users and teams can access the resource there. Effective permission is capped at the user's global role — a global viewer cannot edit even if granted editor on the resource.
Admin Port APIs (port 3199) bypass per-resource ACL. They can list, get, update, and delete all dashboards regardless of Custom permissions.
Error Responses
Common failure cases:
| Status | When |
|---|---|
401 Unauthorized | http-token-admin is set and the Authorization header is missing or invalid. |
400 Bad Request | Required fields are missing or the request body is invalid (for example, unknown panel type). |
404 Not Found | The dashboard or panel ID does not exist. |