Skip to content
SENS Platform Docs

Resource Inventory API

Tenant collections and creates use the public number, for example GET|POST /v1/tenants/{tenantNumber}/devices and GET|POST /v1/tenants/{tenantNumber}/device-types. Existing aggregate members use flat routes such as /v1/devices/{deviceId} and /v1/device-types/{deviceTypeId}. Every route requires a bearer access token, request auditing, and authorization against the internally resolved Tenant UUID. Cross-Tenant and organization-out-of-scope identifiers return 404; validation errors return 400; lifecycle, uniqueness, and stale-pointer conflicts return 409.

The numeric Tenant collection lists or creates Devices. Lists support cursor pagination plus status, q, deviceTypeId, and organizationUnitId. The default page size is 25; the only accepted values are 25, 50, and 100. A successful page contains items, optional nextCursor, and the non-negative integer totalCount. The count is exact for the caller’s current Tenant and organization visibility and the active filters. Items and count are evaluated from one database statement and snapshot, so organization-scoped callers cannot learn hidden rows through the count.

Pages use stable ascending createdAt order with deviceId as the unique tie-breaker. The opaque cursor is bound to the caller, current grants, filters, and page size. A malformed, oversized, stale, or query-incompatible cursor returns 400 VALIDATION_FAILED; clients must restart at the first page instead of modifying cursor contents.

The counted Device page replaces the earlier uncounted list response and is a breaking client contract. Deploy the Platform API and web console as a tested, compatible image pair. A rollback must restore the previous compatible pair; if one component cannot be rolled back, forward-fix the other component to the matching contract instead of leaving a mixed pair running.

GET|PATCH /v1/devices/{deviceId} reads or updates one visible Device. A new DevEUI is required, normalized to uppercase, and must contain exactly 16 hexadecimal characters. It is globally unique and immutable.

Status transitions are active → disabled | retired and disabled → active | retired; retired is terminal. A device type can change only while an already disabled device remains disabled. No device is physically deleted.

PUT /v1/devices/{deviceId}/organization-assignment immediately assigns or moves a device. Sending its existing organization is an idempotent no-op. DELETE ends the current assignment and is restricted to platform and tenant administrators. GET /organization-assignments returns only assignment periods within the caller’s visible scope and never exposes assigning-user data. The reverse order uses assignedAt plus the unique assignment ID. Pages contain 25 periods, optional nextCursor, and no exact total. The first request fixes an upper anchor; continuations retain that anchor, Tenant, Device, and active Organization visibility. An incompatible cursor returns 400 VALIDATION_FAILED.

GET /v1/devices/{deviceId}/current-values requires telemetry:read and applies the same tenant and organization visibility as the Device inventory. It returns every active field of the current Device Type; fields without a stored snapshot contain currentValue: null.

The Device Type collection uses the same counted cursor-page rules as the Device collection: 25 rows by default, only 25, 50, or 100 accepted, stable ascending createdAt / deviceTypeId ordering, and an exact totalCount from the same authorization-scoped database snapshot as items. The cursor is bound to the caller, current grants, q, status, and page size. Invalid, stale, or incompatible cursors return 400 VALIDATION_FAILED.

Device-type keys are immutable. Name, status, and flat metadata remain mutable. An active type cannot be disabled while active devices still use it. Disabling a type preserves its associated Flow. Device counts are tenant-wide only for platform and tenant administrators; organization-scoped callers receive counts calculated from their visible current assignments.

Device list items include the authoritative deviceTypeName and nullable currentOrganizationName labels for that page. Detail responses keep the aggregate Device contract unchanged. This counted Device Type response and the Device list-item projection are breaking client changes and follow the same paired Platform API/web-console rollout and rollback rule described above.

GET /v1/device-types/{deviceTypeId}/fields, PUT /fields/{key}, and POST /fields/{key}/deprecate manage the authoritative field contract. Updates use numeric ETags and If-Match; protected or concurrent changes return 409, and invalid schema, target, unit, or measurement-type combinations return 422. The complete field catalog is intentionally returned in one response and is hard-bounded to 100 definitions. Current-value details likewise return the complete current Device-Type catalog, including fields without a stored value, and are hard-bounded to 100 entries; neither detail collection uses cursors.

POST /v1/device-types/{deviceTypeId}/flow atomically creates the Device Type’s only Flow and its initial V2 draft. A second Flow returns 409; there is no separate assignment endpoint. The global Flow list remains available and can be filtered by deviceTypeId or searched by Flow key, Flow name, and Device-Type name through q.

The Flow collection is a counted administrative table. It defaults to 25 rows, accepts only 25, 50, or 100, and returns items, optional nextCursor, and an exact totalCount. Items and count come from the same Tenant-authorized database statement and snapshot. Stable ascending order uses createdAt and flowId. The opaque cursor is bound to the caller, current grants, Tenant, filters, and page size. Malformed, stale, or incompatible cursors return 400 VALIDATION_FAILED.

Flow selectors use the same endpoint with server search and 50 results per request. Clients must expose an explicit continuation control and may restore an authorized selected value with GET /v1/flows/{flowId}. Version histories use GET /v1/flows/{flowId}/versions, 25 entries per request, reverse chronological createdAt / flowVersionId order, and explicit older-result loading. They do not return an exact total. Version cursors retain PostgreSQL timestamp precision and are bound to the caller, Tenant, Flow, and page size.

Flow investigations use GET /v1/tenants/{tenantNumber}/flow-executions and GET /v1/tenants/{tenantNumber}/flow-dead-letters. Both endpoints return uncounted pages with items and optional nextCursor, never totalCount. The API defaults to 25 entries and accepts a limit from 1 through 100; the Web Console always requests 25 per explicit load. The first request fixes the exclusive upper to time anchor. Every continuation retains that anchor and reverse chronological createdAt / resource-ID ordering, including full PostgreSQL timestamp precision. The opaque cursor is bound to the caller, Tenant, requested limit, and the complete flowId, organizationUnitId, status, from, and to filter set. Clients must restart from page one when a filter changes or when the API returns 400 VALIDATION_FAILED; refresh starts a new investigation with a new anchor.

The counted Flow response is a breaking replacement for the earlier uncounted page. The progressive Execution and Dead-Letter page responses likewise replace the earlier bounded arrays. Deploy Platform API and Web Console as one tested, compatible image pair. A rollback must restore the preceding compatible pair; if only one component can be rolled back, forward-fix the other to its matching contract rather than operating a mixed pair.

Flow creation, versioning, validation, release and activation are documented in Telemetry Flow Contract V2. Tenant administrators can manage tenant-owned flows and blocks but not global platform blocks or templates.