Developers

Use our app, or build your own on top.

Devices speak MQTT or HTTP. Your systems speak REST with scoped keys, and every route is in an OpenAPI document. Webhooks come back with a body you can parse.

Quickstart

  1. 01
    Add a device

    Its MQTT username and password are shown once.

  2. 02
    Add a measure

    Owned by that device; note its ID.

  3. 03
    Publish a reading

    It's on the device page and every open dashboard.

MQTT

One credential, two topics

A device publishes readings on devices/{deviceID}/data, or on …/process to run them through a process first. The payload names the measure; organization and branch come from the device, never from the message.

  • · The broker checks the login on every connection
  • · A device may only publish on its own topics
  • Commands arrive on a sub-topic you choose, such as commands/unload
# publish a reading
mosquitto_pub -h mqtt.kowiste.com -p 8883 \
  -i dev_01J8F2K7QX -u dev_01J8F2K7QX -P "$DEVICE_PASSWORD" \
  -t devices/dev_01J8F2K7QX/data \
  -m '{"measureID":"m_cmp04_discharge",
       "time":"2026-10-21T10:13:08Z",
       "data":{"pressure":9.42,"oilTemp":78.6}}'

# listen for commands
mosquitto_sub … -t devices/dev_01J8F2K7QX/commands/#
HTTP ingest

When MQTT isn't an option

Give the device an ingest key and it posts the same payload over HTTPS. The key authenticates the device, not a user; the same ownership rules and a per-device rate limit apply.

curl -X POST https://api.kowiste.com/api/ingest \
  -H "Authorization: Bearer $INGEST_KEY" \
  -H "Content-Type: application/json" \
  -d '{"measureID":"m_cmp04_discharge",
       "data":{"pressure":9.42}}'
Processes

Decoders in JavaScript

A process gets the reading and a state object for its measure, and returns the reading you want stored. It runs in a V8 sandbox; save it and the next message uses it.

How processes fit into automation →
function process(reading, state) {
const b = hex(reading.data.raw)
return {
pressure: int16(b, 0) / 100,
oilTemp: int16(b, 2) / 10,
}
}
REST API

A contract, not a guess

Every route is declared in the OpenAPI document and checked against it in our tests. Answers have one shape you can rely on.

Success
{"data": …}, lists paged with meta
Invalid input
422, naming the field
Dates
RFC 3339
Names
camelCase; IDs end in ID
Empty
[] and {}, never null
GET /api/incidents?status=active
Authorization: Bearer kw_live_••••4f2a

{
  "data": [{
    "incidentID": "inc_01J9A3…",
    "severity": "critical",
    "peakSeverity": "critical",
    "deviceID": "dev_01J8F2K7QX",
    "openedAt": "2026-10-21T09:11:04Z"
  }],
  "meta": { "page": 1, "total": 1 }
}
API keys

Keys with only the access they need

An organization admin creates a key with a name, the permissions it carries and an optional expiry. It acts in one branch. The secret is shown once; we keep only its hash.

Webhooks

Alerts into your own systems

A webhook action posts the alarm — policy, binding, trigger, asset, states, severity, time and readings — with your own payload alongside. Only public hosts on 80 and 443 are reachable; redirects aren't followed.

See it in automation →
POST https://maint.example.com/api/tickets
Content-Type: application/json

{ "policy": "Compressor overpressure",
  "severity": "critical",
  "readings": { "pressure": 9.42 },
  "payload": { "priority": "P1" } }

Your first reading in five minutes.

Next: run it on your own server →