Record a device event
Records something that happened on a device — an alert, an error, a firmware update or a configuration change — with whatever detail you put in payload. Send it with the device's own token, which may only write about that device, or with a workspace key.
https://app.xplantpro.com/ api/ v1/ device-eventswrite:device_eventsAccepts a device tokenEvents are not deduplicated: a request retried after a dropped response is recorded twice.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
device_id | string (uuid) | Yes | The device the event is about. |
event_type | "heartbeat" | "alert" | "firmware_update" | "config_change" | "error" | "other" | Yes | What kind of event this is. |
payload | object | No | Any JSON object describing the event. Default {}. |
occurred_at | string (date-time) | No | When it happened, as an ISO 8601 timestamp in UTC ending in Z. Defaults to the time the request arrives. |
Example
curl -X POST https://app.xplantpro.com/api/v1/device-events \
-H "Authorization: Bearer $XPLANT_DEVICE_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"device_id": "8e3b1f52-6c0d-4a7e-9b21-5f4d8c2a7e13",
"event_type": "alert",
"payload": {
"reason": "humidity_high",
"humidity_percent": 94
},
"occurred_at": "2026-09-25T13:58:00.000Z"
}'Response
201 with { "ok": true, "data": … }. data holds the result.
| Field | Type | Required | Description |
|---|---|---|---|
id | string (uuid) | Yes | — |
device_id | string (uuid) | Yes | — |
team_id | string (uuid) | Yes | The id of the workspace the event belongs to. |
event_type | string | Yes | One of heartbeat, alert, firmware_update, config_change, error, other. |
payload | object | Yes | — |
occurred_at | string | Yes | When it happened, as an ISO 8601 timestamp. |
created_at | string | Yes | — |
{
"ok": true,
"data": {
"id": "c7d2a9e4-1b6f-4c3a-8e5d-0f9b2a7c4e61",
"device_id": "8e3b1f52-6c0d-4a7e-9b21-5f4d8c2a7e13",
"team_id": "5d2e8b17-9c4a-4e3f-b6d0-1a7c9e2f4b83",
"event_type": "alert",
"payload": {
"reason": "humidity_high",
"humidity_percent": 94
},
"occurred_at": "2026-09-25T13:58:00.000Z",
"created_at": "2026-09-25T13:58:01.000Z"
}
}| Response header | Meaning |
|---|---|
X-Request-Id | Identifies this request. Include it when you contact support. |
Errors
| Status | Code | When |
|---|---|---|
400 | VALIDATION_ERROR | The request body is not valid JSON. |
401 | UNAUTHORIZED | The key is missing, malformed or revoked, or its owner is no longer a member of the workspace. |
402 | PAID_PLAN_REQUIRED | The workspace has no paid plan. Connecting devices is included with every paid plan. |
403 | FORBIDDEN | The key lacks a scope this operation requires, or its owner's current role in the workspace cannot use it — a key never does more than its owner can in xPlant. error names the scope, and for a role, the role it needs. |
403 | DEVICE_TOKEN_WRONG_DEVICE | A device token was used to write about a device other than its own. |
404 | NOT_FOUND | device_id does not name a device in the workspace. |
422 | VALIDATION_ERROR | A field failed validation. error names the first one, for example event_type: Invalid enum value. |
429 | RATE_LIMIT_EXCEEDED | Too many requests for this key, device token or workspace. Wait Retry-After seconds. |
500 | DEVICE_EVENT_CREATE_FAILED | The event could not be saved. Retry later. |
Branch on code, never on the error text. See Errors.
Resolve a label
Turns a scanned or typed code into the record it identifies, for scanners and bench hardware. xPlant checks, in order: plant labels, explant labels, container labels, then your own plant and explant identifiers — so a code your lab writes on the jar itself, such as LINE-0412, resolves too.
List devices
Every device registered to the workspace, newest first, a page at a time. Pages hold up to 200 devices by default, so most workspaces receive every device in the first answer; follow meta.next_cursor until it comes back null to be sure you have them all.