Telemetry
Four endpoints, all scoped telemetry:read, all addressed by serial number. Read
Concepts first for units, bucket boundaries and how fresh each field
is.
Latest snapshot
Section titled “Latest snapshot”GET /api/v1/external/systems/{serialNumber}/telemetry/latestOne call for “what is this system doing”. Use it for dashboards and status pages.
curl -sS https://api.claytonpower.com/api/v1/external/systems/1234567890/telemetry/latest \ -H "X-API-Key: $CP_API_KEY"{ "serialNumber": "1234567890", "connectivity": { "status": "online", "lastSeenAt": "2026-09-17T09:41:12Z" }, "stateOfCharge": { "percent": 87.5, "measuredAt": "2026-09-17T09:41:00Z" }, "fault": null, "latestInterval": { "start": "2026-09-17T09:30:00Z", "durationMinutes": 15, "input": { "mainsKwh": 0.42, "solarKwh": 0.11, "vehicleKwh": 0.0, "totalKwh": 0.53 }, "output": { "consumptionKwh": 0.37 }, "peakOutputW": 1180 }}Every block is independently nullable, because the sources are independent:
-
connectivity-statusisonline,stale,offlineorunknown; see Connectivity states.lastSeenAtis null when the status isunknown. -
stateOfCharge- percent, minute-fresh. It falls back to the latest hourly bucket when the device has not reported a live value, in which casemeasuredAtcan be up to an hour old. Always readmeasuredAt; do not assume the value is from now. Null when neither source has a reading. -
fault- null when the system reports no failure code. Otherwise:{"fault": {"code": "27","level": 3,"description": "Inverter over-temperature","solution": "Check ventilation around the unit and restart it."}}levelis the platform’s severity ranking for that code; level 3 and above are treated as critical and raise acriticalalert.descriptionandsolutionare the catalogue text for the code and can be absent for a code we have no entry for. -
latestInterval- the most recent completed 15-minute bucket. Null when the system produced no bucket in the last 24 hours, and typically 15 to 30 minutes behind now even for a healthy system. The bucket currently filling is never returned.
A serial that does not exist, or belongs to another company, returns 404.
Energy series
Section titled “Energy series”GET /api/v1/external/systems/{serialNumber}/telemetry/energy ?from=2026-09-16T00:00:00Z &to=2026-09-17T00:00:00Z &interval=1h| Parameter | Required | Default | Notes |
|---|---|---|---|
from |
no | to - 24h |
ISO 8601 UTC. Clamped up to dataAvailableFrom |
to |
no | now | ISO 8601 UTC |
interval |
no | see reference | 15m, 1h or 1d |
Maximum range: 31 days for 15m, 366 days for 1h and 1d. Beyond that the request
is rejected with 400 and errorCode = ValidationFailed.
curl -sS -G https://api.claytonpower.com/api/v1/external/systems/1234567890/telemetry/energy \ -H "X-API-Key: $CP_API_KEY" \ --data-urlencode "from=2026-09-16T00:00:00Z" \ --data-urlencode "to=2026-09-17T00:00:00Z" \ --data-urlencode "interval=1h"{ "serialNumber": "1234567890", "interval": "1h", "from": "2026-09-16T00:00:00Z", "to": "2026-09-17T00:00:00Z", "points": [ { "start": "2026-09-16T00:00:00Z", "input": { "mainsKwh": 1.2, "solarKwh": 0.4, "vehicleKwh": 0.0, "totalKwh": 1.6 }, "output": { "consumptionKwh": 1.1 }, "peakOutputW": 2210 } ]}Notes that save debugging time:
- Values are per bucket. Sum them to get a period total; do not diff them.
- Buckets with no data are omitted. Do not index the array by position in time.
peakOutputWisnullwhen the bucket carries no peak-power measurement. A bucket can carry a peak while every energy channel reads zero: the unit was running, the meters had nothing to report.- Systems that do not carry cumulative energy meters report
0on every energy channel. See Product families. - Echoed
fromandtotell you the range that was actually used after clamping.
State-of-charge series
Section titled “State-of-charge series”GET /api/v1/external/systems/{serialNumber}/telemetry/soc ?from=…&to=…&interval=15m|1h|1dSame parameters, same defaults and same range limits as the energy series.
{ "serialNumber": "1234567890", "interval": "1h", "points": [ { "start": "2026-09-16T00:00:00Z", "avgPercent": 81.2, "minPercent": 74.0, "maxPercent": 90.1 } ]}State of charge is an instantaneous quantity, so buckets are averaged, never
summed. minPercent and maxPercent are the extremes seen inside the bucket,
which is what you want for “did this battery get dangerously low today” - a 1d
average of 60 percent can hide a dip to 8 percent at 04:00.
SoC is read across both firmware schemes, so a unit that changed product family mid-range still produces one continuous series.
Health
Section titled “Health”GET /api/v1/external/systems/{serialNumber}/healthThe latest daily health snapshot.
{ "assessedAt": "2026-09-17T02:00:00Z", "overallScore": 78, "status": "warning", "checks": [ { "name": "Connectivity", "score": 95, "status": "healthy", "details": "Reported on 1435 of 1440 minutes." }, { "name": "Error codes", "score": 60, "status": "warning", "details": "Error code 27 active for 4 hours." } ]}status is healthy, warning, critical or unknown. overallScore runs from 0
to 100.
Snapshots are produced once a day, so this endpoint is not a live signal. A new
system has no snapshot yet; that is unknown, not a failure. When a snapshot is
critical, a health.critical alert is raised as well, which is the
better way to react to it.
Choosing an approach
Section titled “Choosing an approach”| You want | Use |
|---|---|
| A live status tile | /telemetry/latest, polled every minute or two |
| A chart of today, yesterday or a month | /telemetry/energy or /telemetry/soc |
| Daily totals for reporting | /telemetry/energy with interval=1d |
| To know when something goes wrong | Webhooks, not polling |
Polling the series endpoints more often than every 15 minutes returns the same points, because the underlying aggregation only refreshes that often.