# Alertas · Referencia

> Lo que ha pasado, y reconocerlo o cerrarlo desde la sala del cliente.

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

## Listar alertas

`GET /v1/alerts` · scope `alerts:read` · [ver en la web](https://safeonuba.com/desarrolladores/referencia/alertas#listAlerts)

### Parámetros de consulta

- `site_id` (string (uuid))
- `type` (AlertType): Qué ha pasado. `earthquake` y `tsunami_risk` son de la planta entera: no llevan `worker` ni `device`. Un vehículo cerca **no** es una alerta: sale dentro de la alerta de un incidente (`nearby`, `vehicle_warnings`). Valores: `sos`, `fall_detected`, `restricted_zone_entry`, `forbidden_zone_entry`, `permit_expired_inside`, `permit_closed_inside`, `heat_stress`, `evacuation_help`, `worker_call_request`, `device_offline`, `low_battery`, `earthquake`, `tsunami_risk`.
- `status` (AlertStatus): `acknowledged`: un operador se ha hecho cargo. No es lo mismo que `worker_acknowledged_at`, que es que el trabajador ha visto el aviso en su reloj. Valores: `unacknowledged`, `acknowledged`, `resolved`.
- `severity` (Severity): Gravedad mínima. Valores: `low`, `medium`, `high`, `critical`.
- `since` (string (date-time))
- `until` (string (date-time))
- `limit` (integer): Elementos por página (máximo 500). (de 1 a 500, por defecto 100)
- `cursor` (string): El `next_cursor` de la página anterior.

### Respuestas

- `200`: Alertas, de la más reciente a la más antigua. → `AlertPage` (`application/json`)
- `401`: Falta el token, ha caducado o se ha revocado. → `Problem` (`application/problem+json`)
- `403`: Al token le falta el scope que pide la ruta, o el centro no es del cliente. → `Problem` (`application/problem+json`)
- `429`: Demasiadas peticiones, o demasiado tiempo de consulta: cada cliente tiene un número de peticiones por minuto (600 de serie) y un tiempo de consulta por minuto (30 s de serie), y cada petición gasta lo que tarda, así que una lista larga cuenta más que una alerta suelta. `Retry-After` dice cuándo volver. → `Problem` (`application/problem+json`)

### Ejemplo de petición (sandbox)

```bash
curl "https://sandbox.api.safeonuba.com/v1/alerts" \
  -H "Authorization: Bearer $SAFEONUBA_TOKEN"
```

### Respuesta de ejemplo (`200`)

```json
{
  "data": [
    {
      "id": "0f6c2b7e-3a1d-4c55-9b0e-7d2f4a8c1e90",
      "type": "sos",
      "severity": "critical",
      "status": "unacknowledged",
      "title": "SOS activado — Ana Ejemplo",
      "date_created": "2026-09-24T11:04:12Z",
      "date_last_modified": "2026-09-24T11:04:12Z",
      "site": {
        "id": "11111111-1111-1111-1111-111111111111",
        "name": "Planta Ejemplo — simulación"
      },
      "zone": null,
      "worker": {
        "id": "33333333-3333-3333-3333-333333333305",
        "external_id": "EMP-004512",
        "name": "Ana Ejemplo",
        "contractor": "Contrata Ejemplo A"
      },
      "device": {
        "id": "44444444-4444-4444-4444-444444444405",
        "serial": "OS-W-0005",
        "battery": 64,
        "date_last_seen": "2026-09-24T11:04:10Z"
      },
      "location": {
        "latitude": 40.00121,
        "longitude": -3.00214,
        "uncertainty_m": 8,
        "source": "gps",
        "masked": false,
        "position_date_utc": "2026-09-24T11:04:08Z",
        "height": null
      },
      "location_withheld": false,
      "details": {
        "trigger": "watch_button"
      },
      "nearby": [
        {
          "kind": "vehicle",
          "worker": {
            "id": "33333333-3333-3333-3333-333333333309",
            "external_id": "EMP-002210",
            "name": "Carlos Ejemplo",
            "contractor": "Contrata Ejemplo B"
          },
          "vehicle_type": "Grúa móvil",
          "distance_m": 18,
          "reported_at": "2026-09-24T11:04:02Z"
        },
        {
          "kind": "person",
          "worker": {
            "id": "33333333-3333-3333-3333-333333333301",
            "external_id": "EMP-000981",
            "name": "Diego Ejemplo",
            "contractor": null
          },
          "vehicle_type": null,
          "distance_m": 26,
          "reported_at": "2026-09-24T11:03:58Z"
        }
      ],
      "vehicle_warnings": [],
      "worker_acknowledged_at": null,
      "acknowledged": null,
      "resolved": null,
      "assigned_to": null,
      "simulated": false,
      "panel_url": "https://app.safeonuba.com/alertas/0f6c2b7e-3a1d-4c55-9b0e-7d2f4a8c1e90"
    }
  ],
  "next_cursor": "eyJ0IjoiMjAyNi0wOS0yNFQxMTowNDoxMloifQ"
}
```

## Detalle de una alerta

`GET /v1/alerts/{id}` · scope `alerts:read` · [ver en la web](https://safeonuba.com/desarrolladores/referencia/alertas#getAlert)

### Parámetros de ruta

- `id` (string (uuid), obligatorio): Id de la alerta.

### Respuestas

- `200`: La alerta. → `Alert` (`application/json`)
- `401`: Falta el token, ha caducado o se ha revocado. → `Problem` (`application/problem+json`)
- `403`: Al token le falta el scope que pide la ruta, o el centro no es del cliente. → `Problem` (`application/problem+json`)
- `404`: No existe, o esta identidad no la ve (otro centro, otra contrata). → `Problem` (`application/problem+json`)
- `429`: Demasiadas peticiones, o demasiado tiempo de consulta: cada cliente tiene un número de peticiones por minuto (600 de serie) y un tiempo de consulta por minuto (30 s de serie), y cada petición gasta lo que tarda, así que una lista larga cuenta más que una alerta suelta. `Retry-After` dice cuándo volver. → `Problem` (`application/problem+json`)

### Ejemplo de petición (sandbox)

```bash
curl "https://sandbox.api.safeonuba.com/v1/alerts/0f6c2b7e-3a1d-4c55-9b0e-7d2f4a8c1e90" \
  -H "Authorization: Bearer $SAFEONUBA_TOKEN"
```

### Respuesta de ejemplo (`200`)

```json
{
  "id": "7a1e9c40-52d8-4b0f-a3c6-2e9d81f0b7a4",
  "type": "forbidden_zone_entry",
  "severity": "high",
  "status": "unacknowledged",
  "title": "Entrada en zona prohibida — Almacenamiento H₂ (ATEX) — EMP-003318",
  "date_created": "2026-09-24T10:12:40Z",
  "date_last_modified": "2026-09-24T10:12:40Z",
  "site": {
    "id": "11111111-1111-1111-1111-111111111111",
    "name": "Planta Ejemplo — simulación"
  },
  "zone": {
    "id": "22222222-2222-2222-2222-222222222202",
    "name": "Almacenamiento H₂ (ATEX)"
  },
  "worker": {
    "id": "33333333-3333-3333-3333-333333333303",
    "external_id": "EMP-003318",
    "contractor": "Contrata Ejemplo C"
  },
  "device": {
    "id": "44444444-4444-4444-4444-444444444403",
    "serial": "OS-W-0003",
    "battery": 81,
    "date_last_seen": "2026-09-24T10:12:38Z"
  },
  "location": {
    "latitude": 40.00344,
    "longitude": -3.00561,
    "uncertainty_m": 5,
    "source": "gps",
    "masked": false,
    "position_date_utc": "2026-09-24T10:12:36Z",
    "height": {
      "height_m": 7.6,
      "uncertainty_m": 1,
      "building": "Nave de proceso",
      "floor": "Planta 1"
    }
  },
  "location_withheld": false,
  "details": {
    "zone_type": "forbidden",
    "permit_reference": null
  },
  "nearby": [],
  "vehicle_warnings": [],
  "worker_acknowledged_at": "2026-09-24T10:12:51Z",
  "acknowledged": null,
  "resolved": null,
  "assigned_to": null,
  "simulated": true,
  "panel_url": "https://app.safeonuba.com/alertas/7a1e9c40-52d8-4b0f-a3c6-2e9d81f0b7a4"
}
```

## Reconocer o cerrar una alerta

`PUT /v1/alerts/{id}` · scope `alerts:write` · [ver en la web](https://safeonuba.com/desarrolladores/referencia/alertas#updateAlert)

La cronología de la alerta escribe «Cliente · Operador (vía integración)». Reconocer no es cerrar: una alerta reconocida sigue marcando a la persona en el mapa hasta que se cierra.

### Parámetros de ruta

- `id` (string (uuid), obligatorio): Id de la alerta.

### Cuerpo (`application/json`, esquema `AlertUpdate`)

- `status` (string, obligatorio) Valores: `acknowledged`, `resolved`.
- `resolution_reason` (string): Al cerrar: por qué. Texto libre; va a la cronología de la alerta junto a la nota. (hasta 200 caracteres)
- `note` (string) (hasta 500 caracteres)
- `actor` (Actor, obligatorio)
  - `name` (string, obligatorio) (hasta 120 caracteres)
  - `external_id` (string) (hasta 64 caracteres)

### Respuestas

- `200`: La alerta, ya actualizada. → `Alert` (`application/json`)
- `401`: Falta el token, ha caducado o se ha revocado. → `Problem` (`application/problem+json`)
- `403`: Al token le falta el scope que pide la ruta, o el centro no es del cliente. → `Problem` (`application/problem+json`)
- `404`: No existe, o esta identidad no la ve. → `Problem` (`application/problem+json`)
- `409`: La transición no es posible (por ejemplo, reconocer una alerta ya cerrada). → `Problem` (`application/problem+json`)
- `422`: El cuerpo no es válido. → `Problem` (`application/problem+json`)
- `429`: Demasiadas peticiones, o demasiado tiempo de consulta: cada cliente tiene un número de peticiones por minuto (600 de serie) y un tiempo de consulta por minuto (30 s de serie), y cada petición gasta lo que tarda, así que una lista larga cuenta más que una alerta suelta. `Retry-After` dice cuándo volver. → `Problem` (`application/problem+json`)

### Ejemplo de petición (sandbox)

```bash
curl -X PUT "https://sandbox.api.safeonuba.com/v1/alerts/0f6c2b7e-3a1d-4c55-9b0e-7d2f4a8c1e90" \
  -H "Authorization: Bearer $SAFEONUBA_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "status": "acknowledged",
  "actor": {
    "name": "E. Ejemplo"
  }
}'
```

### Respuesta de ejemplo (`200`)

```json
{
  "id": "0f6c2b7e-3a1d-4c55-9b0e-7d2f4a8c1e90",
  "type": "sos",
  "severity": "critical",
  "status": "unacknowledged",
  "title": "SOS activado — Ana Ejemplo",
  "date_created": "2026-09-24T11:04:12Z",
  "date_last_modified": "2026-09-24T11:04:12Z",
  "site": {
    "id": "11111111-1111-1111-1111-111111111111",
    "name": "Planta Ejemplo — simulación"
  },
  "zone": null,
  "worker": {
    "id": "33333333-3333-3333-3333-333333333305",
    "external_id": "EMP-004512",
    "name": "Ana Ejemplo",
    "contractor": "Contrata Ejemplo A"
  },
  "device": {
    "id": "44444444-4444-4444-4444-444444444405",
    "serial": "OS-W-0005",
    "battery": 64,
    "date_last_seen": "2026-09-24T11:04:10Z"
  },
  "location": {
    "latitude": 40.00121,
    "longitude": -3.00214,
    "uncertainty_m": 8,
    "source": "gps",
    "masked": false,
    "position_date_utc": "2026-09-24T11:04:08Z",
    "height": null
  },
  "location_withheld": false,
  "details": {
    "trigger": "watch_button"
  },
  "nearby": [
    {
      "kind": "vehicle",
      "worker": {
        "id": "33333333-3333-3333-3333-333333333309",
        "external_id": "EMP-002210",
        "name": "Carlos Ejemplo",
        "contractor": "Contrata Ejemplo B"
      },
      "vehicle_type": "Grúa móvil",
      "distance_m": 18,
      "reported_at": "2026-09-24T11:04:02Z"
    },
    {
      "kind": "person",
      "worker": {
        "id": "33333333-3333-3333-3333-333333333301",
        "external_id": "EMP-000981",
        "name": "Diego Ejemplo",
        "contractor": null
      },
      "vehicle_type": null,
      "distance_m": 26,
      "reported_at": "2026-09-24T11:03:58Z"
    }
  ],
  "vehicle_warnings": [],
  "worker_acknowledged_at": null,
  "acknowledged": null,
  "resolved": null,
  "assigned_to": null,
  "simulated": false,
  "panel_url": "https://app.safeonuba.com/alertas/0f6c2b7e-3a1d-4c55-9b0e-7d2f4a8c1e90"
}
```
