Skip to content

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.

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.

  • A metric is stored with the name <id>:<name> when id is 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, not ti).
  • 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 example config or cmd) is not a metric; it still counts toward Last Seen and appears in the Last messages tab.
  1. An object with "f":"flat" → SenFlat.
  2. An array whose first element is an object with "f":"flat" → combined SenFlat.
  3. Any other array → SenML.
  4. Anything else → not a metric.

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

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.

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.

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