# How to write a dashboard JSON

Define a dashboard as a JSON object and import it using [API](https://betterstack.com/docs/logs/api/dashboards/import/) or UI. This guide builds it up step by step, from an empty dashboard to a full example with charts and alerts.

[info]
#### The fastest way to learn the format

Create a dashboard from a [template](https://betterstack.com/dashboards) or in the UI, then export it with the [export API](https://betterstack.com/docs/logs/api/dashboards/export/) or the **Export** menu option. The exported JSON is the best reference for the available query types and settings.
[/info]

## Dashboard settings

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

```json
[label 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:

```json
[label 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.

```json
[label 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](https://betterstack.com/docs/logs/dashboards/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:

```json
[label 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:

```json
[label 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](https://betterstack.com/docs/logs/api/dashboards/import/) to create the dashboard:

```shell
[label 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](https://betterstack.com/docs/logs/api/dashboards/import/).

## Need help?

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