dashboard-v2 upstream contract
Upstream nodes (for example json-template, analytics runners) pass JSON into the dashboard-v2 node template. The runner stores canonical payloads and merged layout so the v2 dashboard API and ui/dashboard data service can ingest data without extra transforms.
Top-level envelope
The JSON object submitted to dashboard-v2 should include:
| Field | Required | Purpose |
|---|---|---|
$heat-dataservice | Yes | Channel data and groups in the format below. |
dashboard_users | Recommended | Array of HEAT Auth external user GUID strings for dimension access control (snake_case key matches the runner). |
The dashboard-v2 node configuration supplies dataSourceName, layoutConfiguration, and dashboardName. Those are platform settings. They are not realm identifiers.
Canonical $heat-dataservice
This matches HeatDirectIngestionPayload in the dashboard app TypeScript types (ui/dashboard, direct ingestion types).
{
"version": "1.0",
"groups": [
{ "id": "metrics", "name": "Metrics" }
],
"realms": [
{
"name": "pilot-a",
"channels": [ /* HeatDirectChannelData */ ]
},
{
"name": "pilot-b",
"channels": [ /* HeatDirectChannelData */ ]
}
]
}version: Schema version string (use"1.0").groups: UI grouping for channel pickers; each group hasidandname.realms: Non-empty array. Each item hasname(realm id used by the UI and data store) andchannels.
Accepted legacy input (normalized by the runner)
For convenience, upstream may send flat channels at the top level of $heat-dataservice instead of realms:
- Optional top-level
realmstring applies to every channel that does not set its ownrealmfield. - Optional per-channel
realmstring splits channels into multiple realms (first-seen order is preserved).
Do not combine non-empty realms and non-empty channels in the same payload.
The dashboard-v2 processor rewrites storage to canonical nested realms only.
Channel object (HeatDirectChannelData)
Each channel has:
| Field | Required | Description |
|---|---|---|
id | Yes | Stable id; layout configuration.channels and timeline items reference this string. Unique within a realm. |
name | Yes | Display name. |
groupId | Yes | Must match a groups[].id. |
shape | Yes | One of: series, timestamps, value, events, ranges. |
data | Yes | Shape-specific payload (see below). |
metadata | No | Hints for widgets (units, channelType, and so on). |
Shape guidelines
shape | When to use | data |
|---|---|---|
series | Time-varying data, playback, interpolation | Array of { "timeMs": number, "value": number | string | boolean } |
timestamps | Labelled instants | Array of { "timeMs", "annotation", "metadata?" } |
value | Non-time-series or arbitrary structured values (scores, summaries, nested objects) | Object { "value": <json>, "metadata?" : {} } — put structured payloads under value. |
events | Boolean or instantaneous events over time | Array of { "timeMs", "occurred", "metadata?" } |
ranges | Phases or intervals | Array of { "startTimeMs", "endTimeMs", "durationMs", "metadata?" } |
For arbitrary objects that are not inherently time-indexed, prefer shape: "value" with a JSON-serializable value. Use metadata for presentation hints. Keep id stable so the v2 layout can bind components to the channel.
Realms
- A realm is a namespace: the same
idandnamepair in two realms refers to different channel payloads (for example two trainees or two simulation runs side by side). - Realm names are taken from
realms[].namein the payload (after normalization). layoutConfiguration.realmsis a string array of realm ids for the session UI (realm filter, default selection). The runner merges payload realm names into the stored layout and sets sensible defaults forconfiguration.defaultRealmandconfiguration.showRealmFilterDropdownwhen multiple realms exist.
Realm count soft cap (50)
- Recommended maximum: 50 realms per dashboard payload.
- Beyond that, prefer more channels, separate dimensions, or separate dashboards instead of encoding high-cardinality sets as realms.
- The runner logs a warning when normalized realm count exceeds 50.
- Set node configuration
failOnRealmCountExceeded:trueto fail the node instead of only warning (for strict templates).
Layout (layoutConfiguration)
The v2 layout follows the layout schema: version, optional configuration, realms, and components.rows. Component rows reference channel id values from $heat-dataservice. The runner ensures realms on the stored layout aligns with payload realm names where possible.
Further reading
- Dashboard Tools Runner — node overview and runtime behavior
- Direct channel ingestion — same canonical shapes from the UI perspective
- Sample template:
projects/2026-04-Dashboard-V2-Sample/in the repository