Concepts
Positions
When the API returns a position, what location_withheld means, how privacy zones are represented and how accurate each point is.
On this page
A position is where a person is. It’s the most sensitive data in the API, and that’s why it follows exactly the same rule as the dashboard: if a person in the dashboard with those permissions can’t see it, neither can the API.
When a position is visible
It is decided by each site’s privacy level (privacy_mode in GET /v1/sites):
- “Emergencies only” (
on_demand), the default value. A person’s position is only visible when they have an open alert with severity equal to or aboveunveil_min_severity, during an evacuation or with a request approved in the dashboard. - Continuous (
continuous). The plant has chosen to see positions continuously.
Inside a privacy zone (changing rooms, canteens), nobody’s position is recorded, at any level.
In an alert: location_withheld
Every alert carries location and location_withheld:
- If the privacy level allows the position to be seen,
locationcarries it andlocation_withheldisfalse. - If the position exists but isn’t delivered —in “emergencies only”, an alert below the severity that lifts the veil—,
location_withheldistrue. It isn’t visible in the dashboard either.
Your control room has to handle both cases. An alert without a position isn’t an error: it’s the plant protecting its people. For that case, panel_url leads to the alert’s page in the dashboard.
Latest positions
GET /v1/positions/latest (scope positions:read) returns the positions that the privacy level allows to be seen at that moment. In “emergencies only”, anyone with nothing open doesn’t appear.
| Field | What it is |
|---|---|
device_id | The watch. |
worker | The person. |
location | The position. |
in_vehicle | Whether they are in a vehicle. |
Every position served with an alert behind it is recorded in that alert, under “Who has seen their position”.
The location object
| Field | What it is |
|---|---|
latitude, longitude | WGS84 coordinates. |
uncertainty_m | Radius of uncertainty in metres. null if unknown, which isn’t the same as having it perfect. |
source | Where the point comes from. |
masked | true if the point is the representative one for a privacy zone and not the person’s own. |
position_date_utc | When it was taken. |
height | Height above the ground by barometer, with its uncertainty, building and floor, when it can be stated. Otherwise, null. |
| source | What it means |
|---|---|
gps | The watch’s GPS. |
network | Position by network. |
zone | The person was in a privacy zone: the point is the zone’s, not theirs. |
Always draw the uncertainty_m radius next to the point: a point without a radius gives a precision it doesn’t have.
Distances without a position
In a critical alert, nearby says who was within 100 metres and at what distance, never where. Anyone who was in a privacy zone doesn’t count. See Alerts.
Don’t track anyone in a loop
Querying GET /v1/positions/latest every few seconds gives you nothing more than the alerts: in “emergencies only” only people who already have an open alert appear, and that alert already reaches you by webhook with the position inside. On top of that, every query counts towards your rate limits.