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.
Devices and assignments
Section titled “Devices and assignments”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.
Compatibility and rollout
Section titled “Compatibility and rollout”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.
Device types
Section titled “Device types”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.
Field contract and unique Flow
Section titled “Field contract and unique Flow”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.