How to write a dashboard JSON

Define a dashboard as a JSON object and import it using API or UI. This guide builds it up step by step, from an empty dashboard to a full example with charts and alerts.

The fastest way to learn the format

Create a dashboard from a template or in the UI, then export it with the export API or the Export menu option. The exported JSON is the best reference for the available query types and settings.

Dashboard settings

Every dashboard starts with a few top-level settings and empty charts and sections arrays:

dashboard.json
{
  "name": "My dashboard",
  "refresh_interval": 30,
  "date_range_from": "now-3h",
  "date_range_to": "now",
  "charts": [],
  "sections": []
}
  • refresh_interval is in seconds. Use 0 to refresh only on load.
  • date_range_from and date_range_to accept relative expressions such as now-3h and now, or absolute dates.

Positioning charts

Charts sit on a 12-column grid. Each chart sets its position with x and y and its size with w and h. Sections are horizontal dividers placed at a given y.

This example places two charts side by side under one section. The next section explains the queries:

dashboard.json
{
  "name": "My dashboard",
  "refresh_interval": 30,
  "date_range_from": "now-3h",
  "date_range_to": "now",
  "sections": [
    { "name": "Overview", "y": 0, "collapsed": false }
  ],
  "charts": [
    {
      "chart_type": "line_chart",
      "name": "Requests",
      "x": 0, "y": 1, "w": 6, "h": 6,
      "chart_queries": [
        {
          "query_type": "sql_expression",
          "sql_query": "SELECT {{time}} AS time, label('status') AS series, countMerge(events_count) AS value FROM {{source}} WHERE {{time}} BETWEEN {{start_time}} AND {{end_time}} GROUP BY time, series ORDER BY time"
        }
      ],
      "chart_alerts": []
    },
    {
      "chart_type": "number_chart",
      "name": "Total requests",
      "x": 6, "y": 1, "w": 6, "h": 6,
      "chart_queries": [
        {
          "query_type": "sql_expression",
          "sql_query": "SELECT countMerge(events_count) AS value FROM {{source}} WHERE {{time}} BETWEEN {{start_time}} AND {{end_time}}"
        }
      ],
      "chart_alerts": []
    }
  ]
}

Available chart types include line_chart, bar_chart, number_chart, table_chart, scatter_chart, and heatmap_chart.

Chart queries

Each chart holds one or more queries in chart_queries. A SQL query uses the {{source}}, {{time}}, {{start_time}}, and {{end_time}} placeholders, and reads metrics with the *Merge aggregate functions and label() for tags.

A chart with a SQL query
{
  "chart_type": "line_chart",
  "name": "Requests",
  "x": 0, "y": 1, "w": 6, "h": 6,
  "settings": {
    "unit": "shortened",
    "y_axis_scale": "linear"
  },
  "chart_queries": [
    {
      "query_type": "sql_expression",
      "sql_query": "SELECT {{time}} AS time, label('status') AS series, countMerge(events_count) AS value FROM {{source}} WHERE {{time}} BETWEEN {{start_time}} AND {{end_time}} GROUP BY time, series ORDER BY time"
    }
  ],
  "chart_alerts": []
}

For the SQL discipline behind these queries, see SQL queries.

Chart alerts

Add alerts to a chart in chart_alerts. An alert watches the chart's query and opens an incident when its condition is met:

A chart alert
{
  "name": "Spike in errors",
  "alert_type": "relative",
  "operator": "increases_by",
  "value": 50.0,
  "query_period": 3600,
  "recovery_period": 7200,
  "escalation_target": "current_team",
  "aggregation_interval": 300,
  "check_period": 600
}
  • query_period, recovery_period, aggregation_interval, and check_period are in seconds.
  • escalation_target chooses who is notified when the alert fires.

Full example

This dashboard combines a section, two charts, a SQL query, and an alert:

dashboard.json
{
  "name": "API performance",
  "refresh_interval": 30,
  "date_range_from": "now-3h",
  "date_range_to": "now",
  "sections": [
    { "name": "Overview", "y": 0, "collapsed": false }
  ],
  "charts": [
    {
      "chart_type": "line_chart",
      "name": "Requests by status",
      "x": 0, "y": 1, "w": 8, "h": 6,
      "settings": { "unit": "shortened", "y_axis_scale": "linear" },
      "chart_queries": [
        {
          "query_type": "sql_expression",
          "sql_query": "SELECT {{time}} AS time, label('status') AS series, countMerge(events_count) AS value FROM {{source}} WHERE {{time}} BETWEEN {{start_time}} AND {{end_time}} GROUP BY time, series ORDER BY time"
        }
      ],
      "chart_alerts": [
        {
          "name": "Spike in errors",
          "alert_type": "relative",
          "operator": "increases_by",
          "value": 50.0,
          "query_period": 3600,
          "recovery_period": 7200,
          "escalation_target": "current_team",
          "aggregation_interval": 300,
          "check_period": 600
        }
      ]
    },
    {
      "chart_type": "number_chart",
      "name": "Total requests",
      "x": 8, "y": 1, "w": 4, "h": 6,
      "settings": { "unit": "shortened" },
      "chart_queries": [
        {
          "query_type": "sql_expression",
          "sql_query": "SELECT countMerge(events_count) AS value FROM {{source}} WHERE {{time}} BETWEEN {{start_time}} AND {{end_time}}"
        }
      ],
      "chart_alerts": []
    }
  ]
}

Importing your dashboard

Send the JSON to the import API to create the dashboard:

Import a dashboard
curl --request POST \
  --url "https://telemetry.betterstack.com/api/v2/dashboards/import" \
  --header "Authorization: Bearer $TOKEN" \
  --header "Content-Type: application/json" \
  --data '{ "data": '"$(cat dashboard.json)"' }'


For the full list of fields and settings, see the import API reference.

Need help?

Please let us know at hello@betterstack.com.
We're happy to help! 🙏