Skip to main content

Binding Data Sources to Dashboard Elements

A dashboard tile bound to a data source shows that source's latest cached result. Whenever the source refreshes (on demand, on a schedule, or because something pushed to it), every tile bound to it receives the new data and redraws, with no page reload.

Before you start​

  • Create the data source and check it returns data. See Creating Data Sources.
  • If the dashboard is shared, give the source project scope. A tile only shows a source's data to people who can read that source; everyone else sees the tile's sample content and a No access marker.
  • Note the tile's element type and the data shape it expects (see Dashboards). A table needs columns and rows; a metric card needs value and label.

What a binding is​

A binding is stored on the tile as its data_source field:

{
"type": "data_source",
"data_source_id": 42,
"query_params": { "state": "open" },
"mapping": {
"rows": "$.rows",
"columns": [
{ "key": "title", "label": "Pull request" },
{ "key": "author", "label": "Author" }
]
}
}
FieldPurpose
data_source_idThe source to read
query_paramsOptional. Passed to the connector on every fetch for this tile: added to a REST query string, merged into GraphQL variables, or merged into an internal query's params
mappingOptional. Shapes the cached result into the data the tile expects

Bind a tile​

Bindings are saved through the platform API or the platform MCP server's dashboard tools. The in-platform agent dashboard functions write tile data but do not set bindings.

When adding a tile, include data_source in the request:

POST /dashboards/{dashboard_id}/elements
{
"element_type": "table",
"title": "Open pull requests",
"size": "full",
"data": { "columns": [], "rows": [] },
"data_source": {
"type": "data_source",
"data_source_id": 42,
"mapping": {
"rows": "$.rows",
"columns": [
{ "key": "title", "label": "Pull request" },
{ "key": "author", "label": "Author" },
{ "key": "opened", "label": "Opened" }
]
}
}
}

On an existing tile, update its data_source:

PATCH /dashboards/{dashboard_id}/elements/{element_id}
{ "data_source": { "type": "data_source", "data_source_id": 42, "mapping": { "value": "$.rows[0].total_tokens", "label": "Tokens" } } }

With the MCP server, pass the same object as the data_source argument of add_dashboard_element or update_dashboard_element.

The platform checks a binding before saving it​

A binding that could only produce a blank or misleading tile is refused with an explanation:

  • the data source does not exist, or you cannot see it;
  • the source has never been fetched and fails its first fetch (a cold source is fetched at this point);
  • the source returned nothing, last failed, or is degraded;
  • a mapping path finds nothing in the source's result. The error lists the paths the result does have, so you can correct the mapping.

Field mappings​

Each mapping key is a field of the tile's data. Its value is either:

  • a JSONPath (a string starting $. or $[), evaluated against the source's cached result; or
  • a literal, used as it is. Anything that does not start with $. or $[ is a literal, including a lone $.

The cached result has the source's rows under rows, so paths usually start $.rows. Paths support keys, indexes ($.rows[0], $.rows[-1]) and a wildcard that collects a field from every row ($.rows[*].count).

TileExample mapping
Metric card{"value": "$.rows[0].total_tokens", "label": "Tokens this month"}
Table{"rows": "$.rows", "columns": [{"key": "status", "label": "Status"}, {"key": "count", "label": "Missions"}]}
Chart{"chart_type": "bar", "series": [{"name": "Missions", "data": "$.rows[*].count"}], "options": {"xaxis": {"categories": "$.rows[*].status"}}}

Only top-level mapping values are evaluated. In the chart example the nested paths are passed through as literal strings, so a chart needs its data shaped by the source's transform, or written by an agent, rather than by nested mapping paths.

Rules worth knowing:

  • No mapping: the tile receives the whole cached result (rows and schema).
  • Only rows, columns and value: rows defaults to $.rows if you leave it out.
  • A path that finds nothing is left out. If every path finds nothing, the tile receives no data rather than a hollow object: a custom tile falls back to its template's sample content, and a built-in tile shows as empty.
  • Custom tiles keep their template: the mapped data is delivered to the template as its live payload.

Query parameters​

Two tiles can bind to the same source with different query_params, for example {"state": "open"} and {"state": "merged"}. Each distinct set of parameters is fetched and cached separately, and a tile with no parameters shares the source's default cache with every other unparameterised tile.

When a source refreshes, the live update pushed to open dashboards carries that refresh's result to every tile bound to the source, whatever its parameters. A tile with its own parameters shows its own cached result again the next time the dashboard is loaded.

Refreshing and live updates​

  • When a source refreshes, every bound tile receives the new data immediately.
  • Refresh data on a tile, or the refresh button in the dashboard header, fetches fresh data from the bound sources.
  • Opening a dashboard fetches any bound source whose cache is empty or out of date first, within a short time limit, so tiles open with current data.
  • If an agent writes data to a bound tile, that data shows until the source's next refresh replaces it.

Previewing a binding in the dashboard​

Configure element (the gear on a tile) has a Data Source section where you can try a binding:

  1. Choose a Data Source.
  2. Optionally enter Query Parameters (JSON) and Field Mapping (JSON). Use $. paths in mappings; the example in the form's hint omits the $. prefix, which makes those values literals.
  3. Choose Bind to load the source's data into the tile, with the row count. The refresh icon re-fetches; the unbind icon clears it.
warning

The Data Source section does not save the binding. The tile shows the data until you reload the page, then returns to its saved state. To bind a tile permanently, use the API or MCP tools as shown above.

When a bound tile is not live​

A bound tile that cannot show live data shows a marker in its title bar: Not live, Partly live, Never loaded, Paused, Source deleted or No access. Hover over it for the reason and the fix. See Dashboards for what each means.

Unbinding, and deleting a bound source​

  • To unbind a tile, set its data_source to null through the API. It then shows whatever data it holds.
  • Deleting a data source from the Data Sources page does not ask for confirmation, even when tiles are bound to it. Bound tiles then show Source deleted. Through the API, the delete is refused with a list of bound tiles unless you pass confirm=true.