Payload formats
Celestian accepts three message content formats. SenML and SenFlat produce the same metrics inside the platform (latest values, aggregation, charts, alarms). Free-form JSON can only be viewed and filtered.
| Format | Shape | Best for |
|---|---|---|
| SenML | An array of records | An open standard, third-party devices |
| SenFlat v1 | A single flat object | Small-memory devices, compact payloads |
| SenFlat v2 | Many sources and time points | Gateways, history data |
| Free-form JSON | Anything | Custom payloads |
The RFC 8428 standard: a JSON array of records.
[ {"bn": "room-a1:", "bt": 1790000000, "n": "temp_inlet", "u": "Cel", "v": 22.4}, {"n": "humidity", "u": "%RH", "v": 48.3}, {"n": "door_open", "vb": false}, {"n": "status", "vs": "OK"}]| Field | Description |
|---|---|
n |
The metric name (required, at most 200 characters). |
u |
The unit. |
v, vb, vs, vd, s |
A numeric, boolean, text (at most 500 characters), data (base64), and sum value. |
t, bt |
The time. Less than 2^28 seconds is relative to the receive time; anything else is epoch seconds. Without a time, the broker’s receive time is used. |
bn, bu, bv |
Base name, base unit, base value; they apply to the following records. The final name = bn + n (room-a1:temp_inlet). |
At most 1000 records per message.
SenFlat v1
Section titled “SenFlat v1”A flat JSON object, more compact than SenML. The result is identical inside the platform.
{ "f": "flat", "v": 1, "id": "001a2b3c4d5e", "t": 1790000000, "m": { "temp_inlet": 22.44, "voltage": 230.1, "door_front_open": false, "state": "online" }}| Field | Required | Description |
|---|---|---|
f |
yes | Exactly "flat". The sole determinant of the format. |
v |
yes | The format version: 1. |
id |
no | The source identity (the equivalent of bn without :). Without id, the source is the publisher itself. |
t |
no | The measurement time, epoch seconds UTC (fractions allowed). Its time rules are the same as SenML. |
m |
yes | The metrics: name → value. At least one. |
Rules for values in m:
| JSON value | Stored as | SenML equivalent |
|---|---|---|
| A finite number | A number | v |
true or false |
A boolean | vb |
| A string | Text (truncated to 500 characters) | vs |
null, an object, an array, NaN, Infinity |
Invalid |
A sensor that failed to read is not included (do not send null or a marker such as -1).
Several sources or times: send an array of objects, all SenFlat (it cannot be mixed with SenML).
[ {"f":"flat","v":1,"id":"001a2b3c4d5e","t":1790000000,"m":{"temp_inlet":22.4}}, {"f":"flat","v":1,"id":"001a2b3c4d5f","t":1790000000,"m":{"temp_inlet":23.1}}]The equivalent in SenML:
[{"bn":"001a2b3c4d5e:","bt":1790000000,"n":"temp_inlet","v":22.44}, {"n":"voltage","v":230.1}, {"n":"door_front_open","vb":false}, {"n":"state","vs":"online"}]An example size for 16 telemetry metrics: SenML 624 bytes, SenFlat 373 bytes.
Stored metric names and units
Section titled “Stored metric names and units”- A metric is stored with the name
<id>:<name>whenidis present (001a2b3c4d5e:power_draw), or just<name>. This name is used in Messages filters and dashboard widgets. - Names are recommended to be
^[a-z][a-z0-9_]*$and written in full (temp_inlet, notti). - Units are not part of the payload. They are filled in from the Metric Catalog when stored through Internal DB and when the latest value is displayed.
- A message without
f(for exampleconfigorcmd) is not a metric; it still counts toward Last Seen and appears in the Last messages tab.
How the format is recognized
Section titled “How the format is recognized”- An object with
"f":"flat"→ SenFlat. - An array whose first element is an object with
"f":"flat"→ combined SenFlat. - Any other array → SenML.
- Anything else → not a metric.
Strict and lenient modes
Section titled “Strict and lenient modes”A payload from a device is read leniently: invalid parts are skipped. A logic result in a rule is read strictly: one invalid part rejects the whole.
| Condition | Lenient | Strict |
|---|---|---|
v is not 1 |
Not a metric | Rejected |
An array element without "f":"flat" |
Skipped | Rejected |
m is empty or not an object |
No records | Rejected |
| An invalid metric value | The metric is skipped | Rejected |
| More than 1000 records | Truncated at 1000 | Rejected |
Free-form JSON
Section titled “Free-form JSON”Accepted when a rule uses the Internal DB (JSON) output:
{"temperature": 23.5, "humidity": 41, "location": "room-a1"}It can be viewed and filtered in the JSON tab of Messages, but it is not aggregated or charted.
Measurement time
Section titled “Measurement time”| Time in the payload | Treatment |
|---|---|
Absent or 0 |
The broker’s receive time. |
| A small value (< 2^28) | Relative to the receive time. |
| An epoch outside −1 year to +1 day of the receive time | Not trusted; the receive time is used. |
For devices whose clocks are not always in sync, SenFlat v2 marks untrusted times explicitly.
Reading the payload in a rule
Section titled “Reading the payload in a rule”| Format | Path in Comparison and Lua |
|---|---|
| SenML | message.payload[1].v (indexes start at 1) |
| SenFlat v1 | message.payload.m.temp_inlet |
| Free-form JSON | message.payload.temperature |