Built-in Metric Queries

Predefined metric queries behind the Spotflow dashboards.

The Run a built-in metric query endpoint runs one of the queries behind the Spotflow dashboards. Pick a query with queryName and pass its arguments as parameters:

{
  "queryName": "DeviceCountPerFirmware",
  "parameters": {
    "from": "2026-01-05T00:00:00+00:00",
    "to": "2026-01-12T00:00:00+00:00",
    "aggregation": "1d"
  }
}

Every query returns the same envelope. columns names the columns and their types, and every entry of rows holds the values of one row in that same order. The columns themselves differ from query to query and are shown with each query.

Queries

UniqueDeviceCountWithLookbackPerDay

Number of distinct devices that connected to Spotflow at least once within the rolling window, one point per day.

{
  "columns": [
    { "name": "Timestamp", "type": "DateTimeOffset" },
    { "name": "UniqueDevicesCount", "type": "Number" }
  ],
  "rows": [
    ["2026-01-12T00:00:00.000Z", 1840],
    ["2026-01-13T00:00:00.000Z", 1852]
  ]
}

CrashingDeviceCountWithLookbackPerDay

Number of devices active in the rolling window, split into crashing and healthy, one point per day. ConnectedCount is the total, CrashingCount counts the devices that reported at least one crash, and HealthyCount is the rest.

{
  "columns": [
    { "name": "Timestamp", "type": "DateTimeOffset" },
    { "name": "ConnectedCount", "type": "Number" },
    { "name": "CrashingCount", "type": "Number" },
    { "name": "HealthyCount", "type": "Number" }
  ],
  "rows": [
    ["2026-01-12T00:00:00Z", 1840, 12, 1828],
    ["2026-01-13T00:00:00Z", 1852, 9, 1843]
  ]
}

CrashFreeHours

Percentage of device-hours that passed without a crash, returned as a single value. A device counts as active once it has reported a connection anywhere in the time range, and every active device contributes every hour of the range to the total.

{
  "columns": [
    { "name": "StabilityPercentage", "type": "Number" }
  ],
  "rows": [
    [99.4]
  ]
}

CrashingDevicesWithLastCrash

Every device that crashed, with the timestamp and reason of its last crash. CrashReason is the fatal error reason recorded in a Zephyr core dump, such as K_ERR_CPU_EXCEPTION. It's null until Spotflow has parsed the core dump, and always null for ESP-IDF and FreeRTOS crashes.

{
  "columns": [
    { "name": "DeviceId", "type": "String" },
    { "name": "LastCrashTimestamp", "type": "DateTimeOffset" },
    { "name": "CrashReason", "type": "String" }
  ],
  "rows": [
    ["thermostat-0d41", "2026-01-12T18:22:41+00:00", "K_ERR_ARM_SECURE_ENTRY_POINT"],
    ["thermostat-19b7", "2026-01-12T09:05:13+00:00", null]
  ]
}

DeviceCountPerFirmware

Number of devices per firmware and version over time. Each device counts under the firmware version it last reported before the end of the bucket, including reports from before from, so devices that stopped reporting are still counted. Firmware and Version are null when the reported build has no uploaded symbol file.

{
  "effectiveAggregation": "1d",
  "columns": [
    { "name": "Timestamp", "type": "DateTimeOffset" },
    { "name": "Firmware", "type": "String" },
    { "name": "Version", "type": "String" },
    { "name": "DeviceCount", "type": "Number" }
  ],
  "rows": [
    ["2026-01-10T00:00:00+00:00", "Thermostat application", "2.4.0", 1622],
    ["2026-01-10T00:00:00+00:00", "Thermostat application", "2.4.1", 218],
    ["2026-01-11T00:00:00+00:00", "Thermostat application", "2.4.1", 1840]
  ]
}

DeviceCrashCountPerFirmware

Number of crashes per firmware and version over time. Firmware and Version are null for crashes from a build that had no uploaded symbol file at the time.

{
  "effectiveAggregation": "1d",
  "columns": [
    { "name": "Timestamp", "type": "DateTimeOffset" },
    { "name": "Firmware", "type": "String" },
    { "name": "Version", "type": "String" },
    { "name": "CrashCount", "type": "Number" }
  ],
  "rows": [
    ["2026-01-12T00:00:00Z", "Thermostat application", "2.4.0", 7],
    ["2026-01-12T00:00:00Z", "Thermostat application", "2.4.1", 2]
  ]
}

IngestedLogsPerSeverity

Number of logs sent from devices, broken down by severity, over time. Every bucket is reported, with 0 where no logs arrived.

{
  "effectiveAggregation": "1h",
  "columns": [
    { "name": "Timestamp", "type": "DateTimeOffset" },
    { "name": "SeverityDebugCount", "type": "Number" },
    { "name": "SeverityInfoCount", "type": "Number" },
    { "name": "SeverityWarningCount", "type": "Number" },
    { "name": "SeverityErrorCount", "type": "Number" },
    { "name": "SeverityCriticalCount", "type": "Number" },
    { "name": "SeverityUnknownCount", "type": "Number" }
  ],
  "rows": [
    ["2026-01-12T12:00:00Z", 4210, 18422, 96, 14, 0, 3],
    ["2026-01-12T13:00:00Z", 4188, 18510, 88, 21, 1, 0]
  ]
}

RebootsByReason

Number of reboots per reset reason over time, for one device or for the whole fleet. When firmware lists any identifiers, deviceId is ignored, so pass only one of them.

{
  "effectiveAggregation": "1h",
  "columns": [
    { "name": "Timestamp", "type": "DateTimeOffset" },
    { "name": "Reason", "type": "String" },
    { "name": "Count", "type": "Number" }
  ],
  "rows": [
    ["2026-01-12T12:00:00Z", "SOFTWARE", 14],
    ["2026-01-12T12:00:00Z", "WATCHDOG", 2]
  ]
}

CpuUtilisation

Average CPU utilization percentage of a device over time.

{
  "effectiveAggregation": "1h",
  "columns": [
    { "name": "Timestamp", "type": "DateTimeOffset" },
    { "name": "AverageCpuUtilisationPercent", "type": "Number" }
  ],
  "rows": [
    ["2026-01-12T12:00:00Z", 37.4],
    ["2026-01-12T13:00:00Z", 41.9]
  ]
}

DeviceHeapUsage

Average heap memory usage of a device over time, in bytes, both free and allocated. AverageHeapFreeBytes is null in buckets where the device reported only allocated bytes, and AverageHeapAllocatedBytes in buckets where it reported only free bytes.

{
  "effectiveAggregation": "1h",
  "columns": [
    { "name": "Timestamp", "type": "DateTimeOffset" },
    { "name": "AverageHeapFreeBytes", "type": "Number" },
    { "name": "AverageHeapAllocatedBytes", "type": "Number" }
  ],
  "rows": [
    ["2026-01-12T12:00:00Z", 41216, 24320],
    ["2026-01-12T13:00:00Z", 40880, 24656]
  ]
}

ThreadStackUsedPercent

Average stack utilization per thread, as a percentage of the assigned stack.

{
  "effectiveAggregation": "1h",
  "columns": [
    { "name": "Timestamp", "type": "DateTimeOffset" },
    { "name": "Thread", "type": "String" },
    { "name": "AverageThreadStackUsedPercent", "type": "Number" }
  ],
  "rows": [
    ["2026-01-12T12:00:00Z", "main", 42.1],
    ["2026-01-12T12:00:00Z", "mqtt_worker", 61.8]
  ]
}

DeviceLogsReceivedAndDropped

Number of logs Spotflow received from a device and number of logs the device dropped, over time. TotalLogsDroppedCount is null in buckets where the device didn't report a dropped count, and TotalLogsIngestedCount is null in buckets where Spotflow ingested no logs from it.

{
  "effectiveAggregation": "1h",
  "columns": [
    { "name": "Timestamp", "type": "DateTimeOffset" },
    { "name": "TotalLogsIngestedCount", "type": "Number" },
    { "name": "TotalLogsDroppedCount", "type": "Number" }
  ],
  "rows": [
    ["2026-01-12T12:00:00Z", 1284, 0],
    ["2026-01-12T13:00:00Z", 1190, 37]
  ]
}

DeviceNetworkUsage

Average network traffic received and sent by the device over time, in bytes per minute. A column is null when there was no traffic in that direction.

{
  "effectiveAggregation": "1h",
  "columns": [
    { "name": "Timestamp", "type": "DateTimeOffset" },
    { "name": "AverageNetworkRxBytes", "type": "Number" },
    { "name": "AverageNetworkTxBytes", "type": "Number" }
  ],
  "rows": [
    ["2026-01-12T12:00:00Z", 812.5, 1440.25],
    ["2026-01-12T13:00:00Z", 795.0, 1388.75]
  ]
}

DeviceUptime

Latest uptime a device reported in each bucket, in milliseconds.

{
  "effectiveAggregation": "1h",
  "columns": [
    { "name": "Timestamp", "type": "DateTimeOffset" },
    { "name": "UptimeMs", "type": "Number" }
  ],
  "rows": [
    ["2026-01-12T12:00:00Z", 176400000],
    ["2026-01-12T13:00:00Z", 180000000]
  ]
}

DeviceNumberOfCrashes

Number of crashes of a device over time.

{
  "effectiveAggregation": "1d",
  "columns": [
    { "name": "Timestamp", "type": "DateTimeOffset" },
    { "name": "CrashCount", "type": "Number" }
  ],
  "rows": [
    ["2026-01-12T00:00:00Z", 0],
    ["2026-01-13T00:00:00Z", 2]
  ]
}

DeviceCrashesPerFirmware

Number of crashes of a device, broken down per firmware and version. Firmware and Version are null for crashes from a build that had no uploaded symbol file at the time.

{
  "columns": [
    { "name": "Firmware", "type": "String" },
    { "name": "Version", "type": "String" },
    { "name": "CrashCount", "type": "Number" }
  ],
  "rows": [
    ["Thermostat application", "2.4.0", 5],
    ["Thermostat application", "2.4.1", 1]
  ]
}

DeviceConnectCount

Number of times a device connected and disconnected in each minute, one row per action. Minutes without either are left out.

{
  "columns": [
    { "name": "Timestamp", "type": "DateTimeOffset" },
    { "name": "Action", "type": "String" },
    { "name": "Count", "type": "Number" }
  ],
  "rows": [
    ["2026-01-12T12:03:00Z", "Connect", 1],
    ["2026-01-12T12:41:00Z", "Disconnect", 1]
  ]
}

DeviceLogAndMetricActivity

Count of logs and metrics reported by each of the given devices per UTC day. Every device gets a row for every day, with 0 where it reported nothing.

{
  "effectiveAggregation": "1d",
  "columns": [
    { "name": "Timestamp", "type": "DateTimeOffset" },
    { "name": "TotalActivity", "type": "Number" },
    { "name": "DeviceId", "type": "String" }
  ],
  "rows": [
    ["2026-01-12T00:00:00Z", 1284, "thermostat-0d41"],
    ["2026-01-12T00:00:00Z", 0, "thermostat-19b7"]
  ]
}

Parameters

Every parameter below belongs in the parameters object of the request. The workspace comes from the URL path, so it is never part of parameters.

from and to

The time range of the query, as ISO 8601 timestamps. Both are required by every query.

Buckets are aligned to the UTC offset of from, and the range is widened outward so that it covers whole buckets. A range from 08:30 with hourly buckets therefore starts at 08:00.

to must be later than from, and the range cannot span more than 90 days.

aggregation

The bucket size of the returned series. Supported values are listed with the query itself.

deviceId

Identifier of the single device to report on.

deviceIds

Array of device identifiers.

lookbackDays

Length of the rolling window in days, as an integer. Each point in the result covers the window ending at that point, so a lookbackDays of 7 reports what happened over the preceding week. The maximum is 7.

firmware

Narrows the result to what devices reported while running a particular firmware. Omit it to cover every device in the workspace.

The filter accepts firmware identifiers, firmware version identifiers, or both at once:

{
  "firmwareIds": ["q8Fd53pLn1Yr"],
  "firmwareVersionIds": ["r2Kt75mXc8Vd"]
}

Both arrays are optional, and listing values in both widens the result instead of narrowing it: data matches when it comes from a firmware in firmwareIds or a version in firmwareVersionIds. The example above therefore covers the listed firmware, plus the listed version of any other firmware.

The filter matches by build ID, which Spotflow reads from the symbol files uploaded to each firmware version. A version without a symbol file therefore matches nothing, even while devices run it.

Read the identifiers from List firmwares and List firmware versions.

On this page