IoT Cloud — API Reference
All endpoints use JSON. Authenticated routes require a Bearer token in the Authorization header. All timestamps are ISO 8601 UTC. All IDs are UUIDs.
For frontend developers: All responses below are representative dummy values. Use them directly to build and test UI components. The backend will return the same shape when implemented.
Authentication
POST
▼
Request body
Response
Errors
| Field | Type | Required | Description |
|---|---|---|---|
| display_name | string | required | Full name shown in the UI |
| string | required | Unique email address | |
| password | string | required | Min 8 characters |
Example request body
"display_name": "Arjun Mehta", "email": "arjun@example.com", "password": "securePass123"
201 Created
{
"success": true,
"data": {
"user": {
"id": "usr_01HXKJ2P3M4N5Q6R7S8T9V0W",
"display_name": "Arjun Mehta",
"email": "arjun@example.com",
"is_verified": false,
"created_at": "2025-01-15T09:23:45Z"
},
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refresh_token": "rt_8fKj2mP9xZqL3nR7sT...",
"expires_in": 3600
}
}
| Status | Code | Reason |
|---|---|---|
| 400 | VALIDATION_ERROR | Missing or invalid fields |
| 409 | EMAIL_ALREADY_EXISTS | Email is already registered |
POST
▼
Request body
Response
Errors
| Field | Type | Required | Description |
|---|---|---|---|
| string | required | Registered email | |
| password | string | required | Account password |
200 OK
{
"success": true,
"data": {
"user": {
"id": "usr_01HXKJ2P3M4N5Q6R7S8T9V0W",
"display_name": "Arjun Mehta",
"email": "arjun@example.com",
"is_verified": true,
"created_at": "2025-01-15T09:23:45Z"
},
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refresh_token": "rt_8fKj2mP9xZqL3nR7sT...",
"expires_in": 3600
}
}
| Status | Code | Reason |
|---|---|---|
| 401 | INVALID_CREDENTIALS | Wrong email or password |
| 403 | EMAIL_NOT_VERIFIED | Account not yet verified |
GET
▼
Response
200 OK
{
"success": true,
"data": {
"id": "usr_01HXKJ2P3M4N5Q6R7S8T9V0W",
"display_name": "Arjun Mehta",
"email": "arjun@example.com",
"is_verified": true,
"created_at": "2025-01-15T09:23:45Z",
"updated_at": "2025-03-10T14:05:00Z"
}
}
POST
▼
Request body
Response
| Field | Type | Required | Description |
|---|---|---|---|
| refresh_token | string | required | Refresh token from login/register |
200 OK
{
"success": true,
"data": {
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"expires_in": 3600
}
}
POST
▼
Request body
Response
| Field | Type | Required | Description |
|---|---|---|---|
| refresh_token | string | required | Token to revoke |
200 OK
{
"success": true,
"message": "Logged out successfully"
}
Projects
GET
▼
Response
200 OK
{
"success": true,
"data": [
{
"id": "prj_01HXKM3A2B4C5D6E7F8G9H0J",
"name": "Smart Greenhouse",
"description": "Climate and irrigation monitoring",
"device_count": 4,
"created_at": "2025-01-20T08:00:00Z",
"updated_at": "2025-03-01T12:30:00Z"
},
{
"id": "prj_02JYKN4B3C5D6E7F8G9H1K2L",
"name": "Factory Floor Monitor",
"description": "Temperature and vibration on CNC machines",
"device_count": 12,
"created_at": "2025-02-05T10:15:00Z",
"updated_at": "2025-03-15T09:00:00Z"
}
],
"meta": { "total": 2 }
}
POST
▼
Request body
Response
| Field | Type | Required | Description |
|---|---|---|---|
| name | string | required | Project name (max 80 chars) |
| description | string | optional | Short description (max 500 chars) |
201 Created
{
"success": true,
"data": {
"id": "prj_03KZLN5C4D6E7F8G9H0J1K3M",
"name": "Home Automation",
"description": "Smart home sensors and controls",
"device_count": 0,
"created_at": "2025-04-01T11:00:00Z",
"updated_at": "2025-04-01T11:00:00Z"
}
}
GET
▼
Response
Errors
200 OK
{
"success": true,
"data": {
"id": "prj_01HXKM3A2B4C5D6E7F8G9H0J",
"name": "Smart Greenhouse",
"description": "Climate and irrigation monitoring",
"device_count": 4,
"created_at": "2025-01-20T08:00:00Z",
"updated_at": "2025-03-01T12:30:00Z"
}
}
| Status | Code | Reason |
|---|---|---|
| 404 | PROJECT_NOT_FOUND | No project with that ID, or not owned by user |
PUT
▼
Request body
Response
| Field | Type | Required | Description |
|---|---|---|---|
| name | string | optional | New project name |
| description | string | optional | New description |
200 OK — returns updated project object (same shape as GET)
{ "success": true, "data": { /* updated project */ } }
DELETE
▼
Response
200 OK
{ "success": true, "message": "Project deleted" }
Devices
GET
▼
Response
200 OK
{
"success": true,
"data": [
{
"id": "dev_01HXKN6D5E7F8G9H0J1K2L3M",
"project_id": "prj_01HXKM3A2B4C5D6E7F8G9H0J",
"name": "GH-Sensor-Node-01",
"description": "North greenhouse bay sensors",
"firmware_version": "1.4.2",
"sensor_count": 3,
"status": "online",
"last_seen_at": "2025-04-30T18:55:00Z",
"created_at": "2025-01-22T10:00:00Z"
},
{
"id": "dev_02JYKP7E6F8G9H0J1K2L3M4N",
"project_id": "prj_01HXKM3A2B4C5D6E7F8G9H0J",
"name": "GH-Irrigation-Ctrl",
"description": "Irrigation valve controller",
"firmware_version": "2.0.0",
"sensor_count": 1,
"status": "offline",
"last_seen_at": "2025-04-29T03:12:00Z",
"created_at": "2025-01-25T14:00:00Z"
}
],
"meta": { "total": 2 }
}
POST
▼
Request body
Response
| Field | Type | Required | Description |
|---|---|---|---|
| name | string | required | Unique name within the project |
| description | string | optional | Human description of device's role |
201 Created
The
secret_key is only returned once at creation. Flash it to the physical device immediately — it cannot be retrieved again.{
"success": true,
"data": {
"id": "dev_03KZLQ8F7G9H0J1K2L3M4N5P",
"project_id": "prj_01HXKM3A2B4C5D6E7F8G9H0J",
"name": "GH-CO2-Sensor",
"description": "CO2 level monitor",
"secret_key": "sk_live_Xk9mP2nQ7rT4vW8yZ...",
"firmware_version": null,
"created_at": "2025-04-30T19:00:00Z"
}
}
GET
▼
Response
200 OK
{
"success": true,
"data": {
"id": "dev_01HXKN6D5E7F8G9H0J1K2L3M",
"project_id": "prj_01HXKM3A2B4C5D6E7F8G9H0J",
"name": "GH-Sensor-Node-01",
"description": "North greenhouse bay sensors",
"firmware_version": "1.4.2",
"status": "online",
"ip_address": "192.168.1.45",
"last_seen_at": "2025-04-30T18:55:00Z",
"sensor_count": 3,
"created_at": "2025-01-22T10:00:00Z",
"updated_at": "2025-03-10T08:00:00Z"
}
}
PUT
▼
Request body
Response
| Field | Type | Required | Description |
|---|---|---|---|
| name | string | optional | New device name |
| description | string | optional | New description |
{ "success": true, "data": { /* updated device object */ } }
DELETE
▼
Response
{ "success": true, "message": "Device deleted" }
GET
▼
Response
200 OK
{
"success": true,
"data": {
"device_id": "dev_01HXKN6D5E7F8G9H0J1K2L3M",
"status": "online",
"ip_address": "192.168.1.45",
"last_seen_at": "2025-04-30T18:55:00Z",
"updated_at": "2025-04-30T18:55:02Z"
}
}
Sensors
GET
▼
Response
200 OK
{
"success": true,
"data": [
{
"id": "sen_01HXKR9G8H0J1K2L3M4N5P6Q",
"device_id": "dev_01HXKN6D5E7F8G9H0J1K2L3M",
"name": "DS18B20",
"description": "Waterproof digital temperature sensor",
"unit": "°C",
"variable_count": 1,
"created_at": "2025-01-23T11:00:00Z"
},
{
"id": "sen_02JYKS0H9J1K2L3M4N5P6Q7R",
"device_id": "dev_01HXKN6D5E7F8G9H0J1K2L3M",
"name": "SHT31",
"description": "Temperature and humidity combo",
"unit": null,
"variable_count": 2,
"created_at": "2025-01-23T11:05:00Z"
}
],
"meta": { "total": 2 }
}
POST
▼
Request body
Response
| Field | Type | Required | Description |
|---|---|---|---|
| name | string | required | Sensor model or identifier name |
| description | string | optional | Human-readable description |
| unit | string | optional | Physical unit e.g. °C, hPa, % |
201 Created — returns created sensor object
{ "success": true, "data": { /* sensor object */ } }
GET
▼
Response
200 OK
{
"success": true,
"data": {
"id": "sen_02JYKS0H9J1K2L3M4N5P6Q7R",
"device_id": "dev_01HXKN6D5E7F8G9H0J1K2L3M",
"name": "SHT31",
"description": "Temperature and humidity combo",
"unit": null,
"variables": [
{ "id": "var_01...", "label": "temperature", "data_type": "float" },
{ "id": "var_02...", "label": "humidity", "data_type": "float" }
],
"created_at": "2025-01-23T11:05:00Z"
}
}
PUT
▼
Response
{ "success": true, "data": { /* updated sensor */ } }
DELETE
▼
Response
{ "success": true, "message": "Sensor deleted" }Variables
Data types: Variables support four types —
float, integer, boolean, string. The label is used as the key in telemetry payloads from the device.
GET
▼
Response
200 OK
{
"success": true,
"data": [
{
"id": "var_01HXKT1J0K2L3M4N5P6Q7R8S",
"sensor_id": "sen_02JYKS0H9J1K2L3M4N5P6Q7R",
"label": "temperature",
"data_type": "float",
"latest_value": 24.7,
"latest_at": "2025-04-30T18:54:30Z",
"created_at": "2025-01-23T12:00:00Z"
},
{
"id": "var_02JYKU2K1L3M4N5P6Q7R8S9T",
"sensor_id": "sen_02JYKS0H9J1K2L3M4N5P6Q7R",
"label": "humidity",
"data_type": "float",
"latest_value": 68.2,
"latest_at": "2025-04-30T18:54:30Z",
"created_at": "2025-01-23T12:00:00Z"
}
]
}
POST
▼
Request body
Response
Errors
| Field | Type | Required | Description |
|---|---|---|---|
| label | string | required | Unique label within the sensor e.g. temperature |
| data_type | enum | required | float | integer | boolean | string |
201 Created
{ "success": true, "data": { /* variable object */ } }
| Status | Code | Reason |
|---|---|---|
| 409 | LABEL_ALREADY_EXISTS | Label already used on this sensor |
| 400 | INVALID_DATA_TYPE | data_type must be one of the allowed enum values |
GET
▼
Response
{ "success": true, "data": { /* variable object with latest_value */ } }
PUT
▼
Response
{ "success": true, "data": { /* updated variable */ } }
DELETE
▼
Response
{ "success": true, "message": "Variable deleted" }Command Templates
GET
▼
Response
200 OK
{
"success": true,
"data": [
{
"id": "cmt_01HXKV3L2M4N5P6Q7R8S9T0V",
"device_id": "dev_01HXKN6D5E7F8G9H0J1K2L3M",
"name": "changeLevel",
"description": "Set irrigation valve level (0-100%)",
"parameters": [
{
"id": "cmp_01HXKW4M3N5P6Q7R8S9T0V1W",
"param_name": "level",
"param_type": "integer",
"param_order": 1,
"default_value": "0",
"is_required": true
}
],
"created_at": "2025-02-01T09:00:00Z"
},
{
"id": "cmt_02JYKW4M3N5P6Q7R8S9T0V1W",
"device_id": "dev_01HXKN6D5E7F8G9H0J1K2L3M",
"name": "reboot",
"description": "Reboot the device firmware",
"parameters": [],
"created_at": "2025-02-01T09:05:00Z"
}
]
}
POST
▼
Request body
Response
| Field | Type | Required | Description |
|---|---|---|---|
| name | string | required | Command name e.g. changeLevel |
| description | string | optional | What the command does |
| parameters | array | optional | Array of parameter definitions (see below) |
| parameters[].param_name | string | required | Parameter name e.g. level |
| parameters[].param_type | enum | required | integer | float | string | boolean |
| parameters[].param_order | integer | required | Display order (1-based) |
| parameters[].default_value | string | optional | Default value as string |
| parameters[].is_required | boolean | optional | Defaults to true |
Example — changeLevel(int level)
{
"name": "changeLevel",
"description": "Set irrigation valve level 0-100",
"parameters": [
{
"param_name": "level",
"param_type": "integer",
"param_order": 1,
"default_value": "0",
"is_required": true
}
]
}
201 Created — returns full template with parameters
{ "success": true, "data": { /* template object */ } }
GET
▼
Response
{ "success": true, "data": { /* template + parameters array */ } }
PUT
▼
Response
{ "success": true, "data": { /* updated template */ } }
DELETE
▼
Response
{ "success": true, "message": "Command template deleted" }Command Dispatch
POST
▼
Request body
Response
Errors
| Field | Type | Required | Description |
|---|---|---|---|
| parameters | object | optional | Key-value pairs matching parameter names defined in the template |
| ttl_seconds | integer | optional | Command expires after this many seconds if not delivered. Default: 3600 |
Example — dispatch changeLevel(level=75)
{
"parameters": {
"level": 75
},
"ttl_seconds": 300
}
202 Accepted
{
"success": true,
"data": {
"id": "cmi_01HXKX5N4P6Q7R8S9T0V1W2X",
"template_id": "cmt_01HXKV3L2M4N5P6Q7R8S9T0V",
"device_id": "dev_01HXKN6D5E7F8G9H0J1K2L3M",
"command_name": "changeLevel",
"parameters": { "level": 75 },
"status": "queued",
"issued_by": "usr_01HXKJ2P3M4N5Q6R7S8T9V0W",
"created_at": "2025-04-30T19:10:00Z",
"queued_at": "2025-04-30T19:10:01Z",
"sent_at": null,
"executed_at": null,
"expired_at": null,
"failure_reason": null,
"expires_at": "2025-04-30T19:15:00Z"
}
}
| Status | Code | Reason |
|---|---|---|
| 403 | NOT_DEVICE_OWNER | Authenticated user does not own this device |
| 400 | MISSING_REQUIRED_PARAM | A required parameter was not provided |
| 400 | INVALID_PARAM_TYPE | Parameter value does not match declared type |
| 422 | DEVICE_QUEUE_FULL | Too many pending commands for this device |
GET
▼
Response
200 OK — example after device executes successfully
{
"success": true,
"data": {
"id": "cmi_01HXKX5N4P6Q7R8S9T0V1W2X",
"command_name": "changeLevel",
"parameters": { "level": 75 },
"status": "success",
"created_at": "2025-04-30T19:10:00Z",
"queued_at": "2025-04-30T19:10:01Z",
"sent_at": "2025-04-30T19:10:03Z",
"executed_at": "2025-04-30T19:10:05Z",
"expired_at": null,
"failure_reason": null
}
}
GET
▼
Query params
Response
| Param | Type | Default | Description |
|---|---|---|---|
| page | integer | 1 | Page number |
| limit | integer | 20 | Items per page (max 100) |
| status | string | — | Filter by status e.g. failed |
200 OK
{
"success": true,
"data": [
{ "id": "cmi_01...", "command_name": "changeLevel", "status": "success", "created_at": "2025-04-30T19:10:00Z" },
{ "id": "cmi_02...", "command_name": "reboot", "status": "expired", "created_at": "2025-04-29T08:00:00Z" }
],
"meta": { "total": 24, "page": 1, "limit": 20, "total_pages": 2 }
}
Telemetry
GET
▼
Query params
Response
| Param | Type | Required | Description |
|---|---|---|---|
| from | ISO 8601 | required | Start of time range e.g. 2025-04-30T00:00:00Z |
| to | ISO 8601 | required | End of time range |
| limit | integer | — | Max datapoints (default 1000, max 5000) |
200 OK
{
"success": true,
"data": {
"variable_id": "var_01HXKT1J0K2L3M4N5P6Q7R8S",
"label": "temperature",
"data_type": "float",
"unit": "°C",
"readings": [
{ "value": 24.3, "recorded_at": "2025-04-30T18:00:00Z" },
{ "value": 24.5, "recorded_at": "2025-04-30T18:05:00Z" },
{ "value": 24.7, "recorded_at": "2025-04-30T18:10:00Z" },
{ "value": 25.1, "recorded_at": "2025-04-30T18:15:00Z" }
],
"meta": {
"count": 4,
"from": "2025-04-30T18:00:00Z",
"to": "2025-04-30T18:15:00Z"
}
}
}
GET
▼
Response
200 OK
{
"success": true,
"data": [
{
"variable_id": "var_01HXKT1J0K2L3M4N5P6Q7R8S",
"sensor_name": "SHT31",
"label": "temperature",
"data_type": "float",
"value": 24.7,
"recorded_at": "2025-04-30T18:54:30Z"
},
{
"variable_id": "var_02JYKU2K1L3M4N5P6Q7R8S9T",
"sensor_name": "SHT31",
"label": "humidity",
"data_type": "float",
"value": 68.2,
"recorded_at": "2025-04-30T18:54:30Z"
},
{
"variable_id": "var_03KZLV3L2M4N5P6Q7R8S9T0V",
"sensor_name": "DS18B20",
"label": "soil_temp",
"data_type": "float",
"value": 19.1,
"recorded_at": "2025-04-30T18:54:28Z"
}
]
}
GET
▼
Query params
Response
| Param | Type | Required | Description |
|---|---|---|---|
| from | ISO 8601 | required | Start of window |
| to | ISO 8601 | required | End of window |
| interval | string | optional | 1m | 5m | 1h | 1d — bucketing interval |
200 OK
{
"success": true,
"data": {
"variable_id": "var_01HXKT1J0K2L3M4N5P6Q7R8S",
"label": "temperature",
"interval": "1h",
"buckets": [
{ "time": "2025-04-30T16:00:00Z", "min": 23.1, "max": 24.9, "avg": 24.0, "count": 12 },
{ "time": "2025-04-30T17:00:00Z", "min": 23.8, "max": 25.4, "avg": 24.5, "count": 12 },
{ "time": "2025-04-30T18:00:00Z", "min": 24.3, "max": 25.1, "avg": 24.7, "count": 11 }
]
}
}
Real-time WebSocket Events
Connect to
wss://api.youriotcloud.io/ws?token=<access_token>. After connecting, subscribe to a device's feed. All messages are JSON with a type field.
↑ Client → Server: subscribe
Subscribe to real-time events for one or more devices
{
"type": "subscribe",
"device_ids": ["dev_01HXKN6D5E7F8G9H0J1K2L3M", "dev_02JYKP7E6..."]
}
// Server acknowledges: { "type": "subscribed", "device_ids": ["dev_01HXKN6D5E7F8G9H0J1K2L3M"] }
↓ Server → Client: telemetry.update
Pushed whenever a subscribed device sends a new sensor reading
{
"type": "telemetry.update",
"device_id": "dev_01HXKN6D5E7F8G9H0J1K2L3M",
"variable_id": "var_01HXKT1J0K2L3M4N5P6Q7R8S",
"label": "temperature",
"value": 25.3,
"recorded_at": "2025-04-30T19:12:00Z"
}
↓ Server → Client: device.status
Pushed when a subscribed device comes online or goes offline
{
"type": "device.status",
"device_id": "dev_01HXKN6D5E7F8G9H0J1K2L3M",
"status": "online",
"last_seen_at": "2025-04-30T19:12:01Z"
}
↓ Server → Client: command.status
Pushed whenever the status of a dispatched command changes (queued → sent → success/failed/expired)
{
"type": "command.status",
"instance_id": "cmi_01HXKX5N4P6Q7R8S9T0V1W2X",
"command_name": "changeLevel",
"device_id": "dev_01HXKN6D5E7F8G9H0J1K2L3M",
"status": "success",
"updated_at": "2025-04-30T19:12:05Z",
"failure_reason": null
}
↑ Client → Server: unsubscribe
Stop receiving events for specific devices
{
"type": "unsubscribe",
"device_ids": ["dev_01HXKN6D5E7F8G9H0J1K2L3M"]
}
Standard error envelope
All error responses follow this shape. Frontend should check
success: false and display error.message to users.{
"success": false,
"error": {
"code": "INVALID_PARAM_TYPE",
"message": "Parameter 'level' expected integer, got string",
"details": { "field": "level", "expected": "integer", "received": "string" }
}
}