REST API

A versioned HTTP API covers the whole platform. Everything is organization-scoped and isolated by row-level security, so a request only ever sees its own tenant's data.

Base path and versioning

The API is served under /api, and resources are versioned under /v1 — so effective paths look like /api/v1/devices. Schema exports live at /api/schemas.

Authentication

Three schemes coexist, all stateless:

  • User sessions — the web app authenticates users and calls the API with a signed JWT.
  • API keys — machine clients send Authorization: Bearer sk_…. Keys are organization-scoped. This is what the CLI uses.
  • Devices — devices don't use either. They provision a certificate with a one-time registration token and then talk to the message broker over mutual TLS.

Every authenticated request resolves to an organization, and all data access is filtered to that organization in the database.

Examples

Every call carries an API key in the Authorization header. Here's the full round trip for two common tasks.

Register a device — create a one-time token, then hand it to the device:

curl -X POST https://<host>/api/v1/devices/registration/tokens \
  -H "Authorization: Bearer sk_live_…" \
  -H "Content-Type: application/json" \
  -d '{ "deviceName": "warehouse-gateway-01" }'
{
  "token": "a1b2c3d4-….d1e2f3a4-….J4kQ7m…",
  "deviceId": "d1e2f3a4-e5f6-7890-abcd-ef1234567890",
  "deviceName": "warehouse-gateway-01",
  "provisionEndpoint": "https://<host>/api/v1/devices/provision",
  "expiresAt": "2024-01-16T10:30:00Z"
}

The token is shown once — flash it into the device, which exchanges it at provisionEndpoint for a certificate.

Export data — submit a job, then poll until it's ready:

curl -X POST https://<host>/api/v1/exports \
  -H "Authorization: Bearer sk_live_…" \
  -H "Content-Type: application/json" \
  -d '{
    "scopeType": "DEVICE",
    "scopeId": "d1e2f3a4-e5f6-7890-abcd-ef1234567890",
    "from": "2024-01-01T00:00:00Z",
    "to": "2024-02-01T00:00:00Z",
    "includes": ["READINGS", "ANNOTATIONS"]
  }'

The job is created as PENDING (HTTP 201):

{
  "id": "ex1a2b3c-4d5e-6789-abcd-ef0123456789",
  "scopeType": "DEVICE",
  "scopeId": "d1e2f3a4-e5f6-7890-abcd-ef1234567890",
  "from": "2024-01-01T00:00:00Z",
  "to": "2024-02-01T00:00:00Z",
  "includes": ["READINGS", "ANNOTATIONS"],
  "status": "PENDING",
  "requestedBy": "user_8f2c…",
  "createdAt": "2024-01-15T10:30:00Z"
}

Poll GET /api/v1/exports/{id} until status is READY; the response then carries a fresh, time-limited downloadUrl:

{
  "id": "ex1a2b3c-4d5e-6789-abcd-ef0123456789",
  "status": "READY",
  "sizeBytes": 184320,
  "readingCount": 44021,
  "annotationCount": 12,
  "completedAt": "2024-01-15T10:30:42Z",
  "expiresAt": "2024-01-16T10:30:42Z",
  "downloadUrl": "https://<host>/…/exports/ex1a2b3c-….zip?X-Amz-Signature=…"
}

Resource groups

The main groups, each under /api/v1:

  • Devices & topology/devices, /components, /sensors, /device-groups, /sites, /devices/{id}/health
  • Provisioning/devices/registration, /devices/provision
  • Config & commands/config-artefacts, /desired-state-bindings, /devices/{id}/commands
  • Profiles & packs/sensor-profiles, /device-profiles, /packs
  • Readings/readings (raw, aggregated, stats, batch)
  • Alerting/alert-rules, /alerts, /notification-channels, /events
  • Analytics/annotations, /annotation-classes, /reference-pairs
  • Dashboards/dashboards
  • Media/cameras, /captures, /media-capture-rules, /dvr-clip-requests, /live
  • Data/exports, /admin/quarantine
  • Firmware & billing/firmware, /billing

OpenAPI

A machine-readable OpenAPI specification is published at /api/v3/api-docs in environments configured to expose it. It's the source for the CLI's typed client and bundled reference.

See the Glossary for what each resource represents.

Was this page helpful?