Model your data
Give your readings structure. A profile says what a sensor measures, in what units, and within what range — so bad data is caught at the door and good data is ready to chart.
Profiles describe your sensors
A SensorProfile is a versioned, typed schema for a channel: each field has a type, unit, optional range, and whether it's required or writable. A DeviceProfile describes a whole device type — which components it carries (each bound to a SensorProfile) and what's configurable.
Profiles are versioned major.minor and are immutable for a given version: an additive change bumps the minor version, a breaking change bumps the major. They're installed as data — typically by a Pack — rather than edited ad hoc, so a device type means the same thing everywhere. Each profile can also be exported as a DTDL-style JSON-LD interface.
Readings are typed and validated
Every Reading is a timestamped value stored in a wide, time-series table — one of four typed columns is filled depending on the channel's value type:
DOUBLE— floating-point measurementsBIGINT— integer countsBOOLEAN— on/off, open/closedTEXT— short string states
Readings are stored in a TimescaleDB hypertable, partitioned by time and organization. Structured, multi-field readings are validated against their SensorProfile before they're stored — required fields, types, array sizes, and numeric ranges are all checked. Anything that fails is parked in quarantine for review instead of being silently dropped.
In the app
Browse profiles under Sensor profiles (/{slug}/sensor-profiles) and Device profiles (/{slug}/device-profiles). Readings are charted on each device's detail page.
In the API
GET /api/v1/sensor-profiles— list or look up a specific versionGET /api/v1/device-profiles— list or look up a specific versionGET /api/schemas/sensor-profile/{id}— export a profile as DTDL JSON-LDGET /api/v1/readings/sensor/{sensorId}— raw readings;/windowfor a relative rangeGET /api/v1/readings/sensor/{sensorId}/aggregated— time-bucketed averages/min/max/count
See Visualize for how the aggregation endpoints keep large time ranges fast.
Profiles are read-only over the API by design — they're seeded as data so the platform stays vertical-neutral. Vertical-specific profiles ship inside Packs.