The server-side REST API for configuration, entity management and live position streaming. JSON over HTTP, authenticated with OAuth 2.0.
Where MQTT is the push path for device telemetry, the REST API is the request path: it manages the entities a deployment is built from — venues, floors, zones, trackables, devices, users — and exposes live RTLS output through long-polling endpoints.
| Base URI | <host>/api/ |
| Media type | application/json |
| Authentication | OAuth 2.0 (bearer token) |
| Specification | RAML — a browsable API console is served from the deployment |
Every endpoint requires OAuth 2.0 except those under /public. A token is obtained from the token endpoint, then presented on every subsequent request.
| Token endpoint | POST <host>/api/public/token |
| Request body | application/x-www-form-urlencoded |
| Grant types | password, refresh_token, client_credentials |
| Request header | Authorization: Bearer <access token> |
The token endpoint sits under /public, which is what makes it reachable before a token exists. The request body is form-encoded, not JSON — the rest of the API is JSON, this endpoint is not.
Every grant identifies the calling client. Two equivalent methods are accepted; use one, not both.
HTTP Basic header. The client ID and secret are joined by a colon and the whole string is Base64-encoded:
base64( client_id + ":" + client_secret )
acme:s3cr3t → YWNtZTpzM2NyM3Q=
Authorization: Basic YWNtZTpzM2NyM3Q=
Encode the joined string once — not each part separately — and without a trailing newline. Client IDs are restricted to a–z, 0–9, ., _ and -, so the first colon is always the separator and a colon inside the secret is safe.
Body parameters. Alternatively, send client_id and client_secret as ordinary form fields, unencoded.
Base64 is used in exactly one place — the Basic header above. The password, the client secret sent as a body field, the refresh token and the access token are all plain values. Base64 is not encryption; it protects nothing on its own, so the token endpoint must be reached over TLS.
Grant availability is a property of the registered client, not a free choice:
| Client type | Permitted grants |
|---|---|
| User-facing client | password, refresh_token |
| Machine-to-machine client | client_credentials |
POST /api/public/token
Content-Type: application/x-www-form-urlencoded
grant_type=password
&username=alice%40example.com
&password=hunter2
&client_id=acme
&client_secret=s3cr3t
The username is matched case-insensitively. The password is sent as a plain form value.
POST /api/public/token
Authorization: Basic YWNtZTpzM2NyM3Q=
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials
POST /api/public/token
Content-Type: application/x-www-form-urlencoded
grant_type=refresh_token
&refresh_token=<refresh token>
&client_id=acme
&client_secret=s3cr3t
{
"token_type": "bearer",
"access_token": "…",
"expires_in": 6000,
"refresh_token": "…"
}
The access token is an opaque string, not a JWT — it carries no readable payload and is matched verbatim by the server. Store and send it exactly as received; do not decode, trim or re-encode it.
| Obtained by | Access token | Refresh token |
|---|---|---|
password — a user | 6000 s (100 min) | 60000 s (≈ 16 h 40 min) |
client_credentials — a client | no expiry | no expiry |
Machine-to-machine tokens do not expire on their own. A token issued once stays valid until the client's credentials are changed, so treat it with the same care as the secret that produced it.
Authorization: Bearer <access token>
The Bearer prefix is required. The server reads the second whitespace-separated word of the header, so a bare token without the prefix is rejected as unauthenticated.
Selected GET endpoints — those intended to be opened directly in a browser or embedded as a URL — also accept the token as a token query parameter instead of the header.
| Status | Meaning | What to do |
|---|---|---|
401 | Bad access token — expired, invalid, or missing the Bearer prefix | Obtain a new token and retry |
403 | Wrong username or password; the account is deactivated or still pending activation; or the account is not permitted to perform this operation | Do not retry — a new token will not help |
Each 403 carries an error_description distinguishing these cases.
The distinction matters operationally. A client that treats every failure as “re-authenticate” will loop indefinitely against a permissions problem, because the token was never the reason.
The RTLS endpoints deliver position updates by long polling: the request is held open until data is available, so a client receives updates promptly without polling in a tight loop.
| Endpoint | Scope |
|---|---|
GET /rtls/venues/{recordId} | positions within a venue |
GET /rtls/floors/{recordId} | positions within a floor |
GET /rtls/zones/{recordId} | positions within a zone |
next, then follow the next value returned in each response. The cursor is what guarantees no update is skipped between polls.recordId = next on the first call to get the latest state.next field; use that value as the recordId of the following request.408 Request Timeout is expected on a quiet channel — reissue the same request rather than treating it as an error.A filter query parameter takes a comma-separated list of locations. Omitting it applies no filter.
{
"id": 36,
"next": 37,
"data": [
{
"geojson": {
"type": "Feature",
"geometry": { "type": "Point", "coordinates": [30.5107845, 50.4375970] },
"properties": {}
},
"locations": {
"venue": ["106"],
"floor": ["107"],
"zone": ["209"]
},
"device": { "uid": "f1:e8:4f:ec:62:76", "type": "ble" },
"debugInfo": {
"distances": [
{ "beacon": 217, "value": 1 },
{ "beacon": 218, "value": 12.429373244299626 }
],
"cell": 210
},
"timestamp": "Mon, 07 Mar 2016 15:52:04 GMT"
}
]
}
| Field | Content |
|---|---|
geojson | Position as a GeoJSON Feature with a Point geometry — [longitude, latitude] |
locations | The venue, floor and zone records the position falls within; each is an array and may be empty |
device | Device uid and type |
debugInfo.distances | Contributing anchors and their measured distances |
debugInfo.cell | Cell used for the calculation |
timestamp | Time of the position |
Trackable is the base type. Equipment and Personnel are its two specialisations, each carrying the properties that only apply to that kind of subject. The three resources are addressed separately because they return different data.
| Resource | Content |
|---|---|
/trackables | Base type — the properties and status shared by everything tracked, regardless of kind |
/equipment | Tracked assets — vehicles, machinery, tools and other equipment |
/personnel | Tracked people — workers, with personnel ID |
Each specialisation follows the standard CRUD pattern, and associates a tracked subject with the device that reports it:
| Method | Path | Result |
|---|---|---|
GET | /equipment | list all tracked equipment |
POST | /equipment | create — 201 on success, 400 if properties are invalid |
GET | /equipment/{uid} | read one — 404 if the uid is unknown |
PATCH | /equipment/{uid} | update |
DELETE | /equipment/{uid} | remove |
GET, PUT | /equipment/{uid}/groups | group membership |
/personnel exposes the same set of methods.
Status and device commands sit on the base type, since they apply to any tracked subject whether it is equipment or a person:
| Method | Path | Result |
|---|---|---|
GET | /trackables/statuses | current status of tracked entities |
POST | /trackables/statuses/sendMessage | send a text message to a device — see Smart Lamp Locator |
POST | /trackables/statuses/led | set the LED indicator |
POST | /trackables/statuses/haptic | trigger haptic feedback |
Historical records, queried rather than streamed. Where /rtls/… delivers live positions, the archive endpoints return what has already been recorded.
| Endpoint | Content |
|---|---|
GET /archive/alarms | historical alarms |
GET /archive/rtls | historical positions |
GET /archive/geof | historical geofencing events |
GET /archive/wifi | historical WiFi connectivity records |
GET /archive/sms | historical messages |
Resources follow a consistent CRUD pattern — GET to list, POST to create, and GET / PATCH / DELETE on /{uid} to read, update and remove a single record.
| Path | Purpose |
|---|---|
/rtls/… | Live positions by location (long polling) |
/archive/… | Historical alarms, positions, messages and geofencing events |
/trackables | Base type shared by equipment and personnel; status, messaging, LED and haptic commands |
/equipment, /personnel | The two trackable specialisations |
/locations/venues, /floors, /zones | Site model — the spatial hierarchy positions are resolved against |
/devices/… | Device records, serial numbers, statuses and connections — see device resource names |
/cells | Positioning cells |
/groups/… | Groups of devices, trackables or locations |
/files/… | File storage for floor plans, venue maps and trackable images |
/users, /roles, /profile | Accounts, permissions and the current session |
/mobile-sdk/… | Endpoints shaped for the mobile SDK |
/public/… | No authentication — version, password reset, secrets |
Devices are split into four resources by the role they play in a deployment:
| Resource path | Role | Products |
|---|---|---|
/devices/anchors |
RTLS anchor — fixed infrastructure that observes tags | Locator Pro, Fieldbus Anchor, Locator Lite |
/devices/locators |
Reverse RTLS anchor — device carried on the asset or person that reports its own position | Smart Lamp Locator, Locator Lite XT |
/devices/tags |
Tags and beacons — the tracked or transmitting endpoints | Wristband Pro, Fieldbus Anchor (Tag Mode), Locator Lite (Tag Mode), BLE Beacons |
/devices/gates |
PowerGate — onsite edge RTLS server | edge processing of RTLS and IoT data |
A PowerGate processes RTLS and IoT data locally at the site rather than in the cloud, so positioning and alerting continue to run when the site's uplink is unavailable.
Corresponding serial-number resources exist under /devices/serialnumbers/, which additionally carries uwb-tags and third-party-beacons.
On the web portal, the endpoints that /devices/tags covers are presented as two separate entities — UWB Tags and BLE Beacons. The API path is a single resource, so an integration mapping portal entities to API calls should expect that one-to-many relationship.
The two are opposite arrangements and should not be confused:
/devices/anchors — fixed infrastructure that observes tags. The anchor reports about something else./devices/locators — a device carried on the asset or person, reporting its own position. Reporter and subject are the same device.The same term is used consistently in the MQTT interface — the LocatorID field and the <prefix>/Locators/<LocatorID>/ topic tree — so a device that appears under /devices/locators publishes in that tree.
GET /devices/statuses/{recordId} accepts a returnState boolean query parameter (default false); when set, the current state of all devices is included in the response.
GET /public/version requires no authentication and returns the server version — useful as a reachability check before authenticating.
| Status | Meaning |
|---|---|
200 | Operation successful |
201 | Created |
400 | Provided properties are invalid |
401 | Bad or expired access token |
403 | Not permitted for this account |
404 | Record not found |
408 | Request timeout — reissue the request |