# Posiciones

> Cuándo entrega la API una posición, qué significa location_withheld, cómo se representan las zonas de privacidad y qué precisión trae cada punto.

Página: https://safeonuba.com/desarrolladores/conceptos/posiciones · API de SafeOnuba v1 (`api_version: "2026-09-24"`), en vista previa: el contrato puede cambiar.

Una posición es dónde está una persona. Es el dato más delicado de la API, y por eso sigue exactamente la misma regla que el panel: si una persona del panel con esos permisos no la ve, la API tampoco.

## Cuándo se ve una posición

Lo decide el nivel de privacidad de cada centro (`privacy_mode` en `GET /v1/sites`):

- **«Solo en emergencia»** (`on_demand`), el valor de serie. La posición de una persona solo se ve cuando tiene una alerta abierta con gravedad igual o superior a `unveil_min_severity`, durante una evacuación o con una solicitud aprobada en el panel.
- **Continuo** (`continuous`). La planta ha elegido ver las posiciones de forma continua.

Dentro de una zona de privacidad (vestuarios, comedores) no se registra la posición de nadie, con cualquier nivel.

## En una alerta: location_withheld

Cada alerta trae `location` y `location_withheld`:

- Si la privacidad deja ver la posición, `location` la trae y `location_withheld` es `false`.
- Si la posición existe pero no se entrega —en «Solo en emergencia», una alerta por debajo de la gravedad que destapa—, `location_withheld` es `true`. Tampoco se ve en el panel.

Tu sala tiene que contemplar los dos casos. Una alerta sin posición no es un error: es la planta protegiendo a su gente. Para ese caso, `panel_url` lleva a la ficha de la alerta en el panel.

## Últimas posiciones

`GET /v1/positions/latest` (scope `positions:read`) devuelve las posiciones que la privacidad deja ver **en ese instante**. En «Solo en emergencia», quien no tiene nada abierto no aparece.

| Campo | Qué es |
| --- | --- |
| `device_id` | El reloj. |
| `worker` | La persona. |
| `location` | La posición. |
| `in_vehicle` | Si va en un vehículo. |

Cada posición que se sirve con una alerta detrás queda registrada en esa alerta, en «Quién ha visto su posición».

## El objeto location

| Campo | Qué es |
| --- | --- |
| `latitude`, `longitude` | Coordenadas WGS84. |
| `uncertainty_m` | Radio de incertidumbre en metros. `null` si no se conoce, que no es lo mismo que tenerla perfecta. |
| `source` | De dónde sale el punto. |
| `masked` | `true` si el punto es el representativo de una zona de privacidad y no el de la persona. |
| `position_date_utc` | Cuándo se tomó. |
| `height` | Altura sobre el suelo por barómetro, con su incertidumbre, edificio y planta, cuando se puede afirmar. Si no, `null`. |

| source | Qué significa |
| --- | --- |
| `gps` | GPS del reloj. |
| `network` | Posición por red. |
| `zone` | La persona estaba en una zona de privacidad: el punto es el de la zona, no el suyo. |

Pinta siempre el radio de `uncertainty_m` junto al punto: un punto sin radio da una precisión que no tiene.

## Distancias sin posición

En una alerta crítica, `nearby` dice quién había a menos de 100 metros y a qué distancia, **nunca dónde**. Quien estaba en una zona de privacidad no cuenta. Ver [Alertas](https://safeonuba.com/desarrolladores/conceptos/alertas#un-vehiculo-cerca-no-es-una-alerta).

## No sigas a nadie en bucle

Consultar `GET /v1/positions/latest` cada pocos segundos no te da más que las alertas: en «Solo en emergencia» solo aparecen las personas que ya tienen una alerta abierta, y esa alerta ya te llega por webhook con la posición dentro. Además, cada consulta cuenta para tus [límites de uso](https://safeonuba.com/desarrolladores/limites).
