@shmaplex/xplant-sdk 0.7.0 is out: what's new →
xPlantAPI
API referenceDevices

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.

POST/api/v1/device-events
Scope write:device_eventsAccepts a device token

To make a retry safe, send your own external_id for the event. An event that repeats one already stored for the same device, under the same external_id, is not recorded again: the answer is the stored event, still 201, with meta.duplicate: true. Without external_id every request is recorded, so a request retried after a dropped response is recorded twice.

Request body

FieldTypeRequiredDescription
device_idstring (uuid)YesThe device the event is about.
event_type"heartbeat" | "alert" | "firmware_update" | "config_change" | "error" | "other"YesWhat kind of event this is.
payloadobjectNoAny JSON object describing the event. Default {}.
occurred_atstring (date-time)NoWhen it happened, as an ISO 8601 timestamp in UTC (2026-09-25T14:00:00Z) or with an offset (2026-09-25T16:00:00+02:00). Defaults to the time the request arrives.
external_idstringNoYour own id for the event. An event with the same device and external_id as one already stored is not recorded again: the stored event is returned instead, with meta.duplicate: true, so a retried request records the event once. 1–200 characters.

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",
    "external_id": "shelf3-alert-20260925T135800Z"
  }'

Response

201 with { "ok": true, "data": … }. data holds the result.

FieldTypeRequiredDescription
idstring (uuid)Yes—
device_idstring (uuid)Yes—
team_idstring (uuid)YesThe id of the workspace the event belongs to.
event_typestringYesOne of heartbeat, alert, firmware_update, config_change, error, other.
payloadobjectYes—
occurred_atstringYesWhen it happened, as an ISO 8601 timestamp.
created_atstringYes—
Response
{
  "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"
  }
}

The envelope can also carry meta:

FieldTypeRequiredDescription
duplicatetrueYesSent only when the request repeated an event already stored for the device under the same external_id. data is that stored event, and nothing new was recorded.
Response headerMeaning
X-RateLimit-LimitThe request budget this answer reports on, per minute: your key's or device token's (1,000) or your workspace's (3,000), whichever has fewer requests left. Left out when the budget could not be read.
X-RateLimit-RemainingRequests left in that budget this minute, after this one. 0 on a 429. A workspace's other keys spend its budget too, so this can fall faster than your own requests alone would.
X-RateLimit-ResetWhole seconds until that budget's minute ends and it is full again. Never more than 60.
X-Request-IdIdentifies this request. Include it when you contact support.

Errors

StatusCodeWhen
400VALIDATION_ERRORThe request body is not valid JSON.
401UNAUTHORIZEDThe key is missing, malformed or revoked, or its owner is no longer a member of the workspace.
402PAID_PLAN_REQUIREDThe workspace has no paid plan. Connecting devices is included with every paid plan.
403FORBIDDENThe 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.
403DEVICE_TOKEN_WRONG_DEVICEA device token was used to write about a device other than its own.
404NOT_FOUNDdevice_id does not name a device in the workspace.
422VALIDATION_ERRORA field failed validation. error names the first one, for example event_type: Invalid enum value.
429RATE_LIMIT_EXCEEDEDToo many requests for this key, device token or workspace. Wait Retry-After seconds.
500DEVICE_EVENT_CREATE_FAILEDThe event could not be saved. Retry the same request — an event that carries external_id is not recorded twice.

Branch on code, never on the error text. See Errors.

On this page