A complete worked example
A from-scratch scenario with curl. First set the variables from the API introduction: CELESTIAN, TOKEN, and WS. The responses below were taken from a running backend (IDs shortened; <...> = fill in your own).
1. Check the token and workspace
Section titled “1. Check the token and workspace”curl -s "$CELESTIAN/api/v1/workspaces/$WS" -H "Authorization: Bearer $TOKEN"{ "workspace": { "id": "…", "name": "Cold Storage Nusantara", "route": "cold-storage", "status": "enabled", "tags": ["demo"] }, "permissions": ["workspace.read", "device.read", "device.write", "channel.write", "rule.write", "…"], "memberCount": 1, "owner": { "id": "…", "username": "…" }, "isOwner": true, "pendingTransfer": null }permissions are this token’s effective rights. Check them when an action is refused.
2. Create a channel
Section titled “2. Create a channel”curl -s -X POST "$CELESTIAN/api/v1/workspaces/$WS/channels" -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" -d '{"name":"Warehouse Telemetry","route":"warehouse-telemetry","tags":["telemetry"]}'201 → {"channel": {"id": "<channel-id>", "name": "Warehouse Telemetry", "route": "warehouse-telemetry", "status": "enabled", …}}. route is optional (lowercase letters, digits, -).
3. Create a device and save its key
Section titled “3. Create a device and save its key”curl -s -X POST "$CELESTIAN/api/v1/workspaces/$WS/devices" -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" -d '{"name":"Freezer Room A1 Sensor","serial":"SN-A1","tags":["freezer-room"]}'{ "device": { "id": "<device-id>", "name": "Freezer Room A1 Sensor", "serial": "SN-A1", "isGateway": false, "status": "enabled", "keyRotatedAt": "…", "tags": ["freezer-room"] }, "key": "BVZvgFsU…" }key is shown only once. It is the device’s MQTT password. Regenerate it with POST .../devices/{id}/key (a body of {} for a random key, or {"key": "<your-own-value>"}). Send "isGateway": true to create a gateway.
4. Let the device send to the channel
Section titled “4. Let the device send to the channel”curl -s -X POST "$CELESTIAN/api/v1/workspaces/$WS/connections" -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"deviceIds":["<device-id>"],"channelIds":["<channel-id>"],"types":["publish"]}'204. types: publish (the device sends) and/or subscribe (the device receives). Requires device.connect and channel.connect, at most 100 pairs per request. Disconnect with POST .../connections/disconnect (the same body).
5. Add a storage rule
Section titled “5. Add a storage rule”curl -s -X POST "$CELESTIAN/api/v1/workspaces/$WS/rules/quick-store" -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" -d '{"channelId":"<channel-id>","format":"senml"}'201 → the rule Channel Subscriber (all subtopics) → Bypass → Internal DB (SenML). format: senml (numeric, boolean, and text metrics; supports latest values, aggregation, and charts) or json (free-form payloads). 409 when that channel already has an active storage rule.
6. The device sends data
Section titled “6. The device sends data”mosquitto_pub -h <broker> -p 1883 -V mqttv311 -i <device-id> -u <device-id> -P '<key>' \ -t "m/$WS/c/warehouse-telemetry/room-a1" \ -m '[{"bn":"room-a1:","n":"temp_inlet","u":"Cel","v":23.5},{"n":"humidity","u":"%RH","v":41.2},{"n":"door_open","vb":false}]'7. Read messages
Section titled “7. Read messages”curl -s "$CELESTIAN/api/v1/workspaces/$WS/messages?channelId=<channel-id>&format=senml&limit=3" \ -H "Authorization: Bearer $TOKEN"{ "page": { "format": "senml", "senml": [ { "time": "2026-10-02T22:42:05.432+07:00", "publisher": "<device-id>", "deviceId": "room-a1:", "subtopic": "room-a1", "protocol": "mqtt", "name": "room-a1:humidity", "unit": "%RH", "value": 41.2, "stringValue": null, "boolValue": null, "dataValue": null, "sum": null, "updateTime": null } ], "nextCursor": "MjAyNi0xMC0wMlQx…", "from": "2026-09-25T15:42:24Z", "to": null } }Without from and to, the last 7 days are shown. Other filters: subtopic, publisher, deviceId, name, protocol, valueOp (eq|ne|gt|gte|lt|lte) with valueNum and valueType, and sort=asc|desc. publisher is the authenticated sending device’s ID, not taken from the payload.
8. Metric names, aggregation, and device status
Section titled “8. Metric names, aggregation, and device status”curl -s "$CELESTIAN/api/v1/workspaces/$WS/messages/names?channelId=<channel-id>" -H "Authorization: Bearer $TOKEN"# {"names": ["room-a1:humidity", "room-a1:temp_inlet"]}
curl -s "$CELESTIAN/api/v1/workspaces/$WS/messages/aggregate?channelId=<channel-id>&name=room-a1:temp_inlet&interval=1h" \ -H "Authorization: Bearer $TOKEN"# {"interval":"1h","buckets":[{"time":"2026-10-02T22:00:00+07:00","name":"room-a1:temp_inlet","min":23.5,"max":23.5,"avg":23.5,"sum":47,"count":2}],"truncated":false,"from":"…"}
curl -s "$CELESTIAN/api/v1/workspaces/$WS/presence/devices?ids=<device-id>" -H "Authorization: Bearer $TOKEN"# {"health": {"<device-id>": {"online": false, "clients": 0, "lastConnectAt": "…", "lastDisconnectAt": "…", "protocol": "mqtt"}},# "presence": {"<device-id>": {"lastSeen": "…", "protocol": "mqtt"}}}interval: 1m, 5m, 15m, 1h, 6h, 1d. CSV export (at most 50,000 rows): GET .../messages/export.csv with the same filters.
9. Send a message to a channel from the API
Section titled “9. Send a message to a channel from the API”Requires channel.publish.
curl -s -X POST "$CELESTIAN/api/v1/workspaces/$WS/messages/send" -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"channelId":"<channel-id>","subtopic":"room-a1","payload":"[{\"n\":\"temp_inlet\",\"u\":\"Cel\",\"v\":24.1}]"}'204. payload is a string (the message content as is, at most 256 KB). The message is recorded with the ui protocol and processed by rules like a device message. The optional "form": "uuid" | "route" chooses the destination topic form (the broker matches topics literally).
10. Dashboards and rules
Section titled “10. Dashboards and rules”Dashboards and rules are the same JSON documents you download from the application. The easiest way to learn them: create one through the UI, then GET it and modify.
# list dashboards (without widgets)curl -s "$CELESTIAN/api/v1/workspaces/$WS/dashboards" -H "Authorization: Bearer $TOKEN"# one rule with its graphcurl -s "$CELESTIAN/api/v1/workspaces/$WS/rules/<rule-id>" -H "Authorization: Bearer $TOKEN"