# API de SafeOnuba

> Recibe las alertas y evacuaciones de SafeOnuba en tu sala de control, tu SCADA, tu PSIM o tu intranet, y reconoce o cierra alertas desde allí.

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

> **v1 · vista previa**
>
> El contrato está pendiente del visto bueno del primer cliente y puede cambiar. Cualquier cambio quedará anotado en el [changelog](https://safeonuba.com/desarrolladores/changelog).

La API de SafeOnuba lleva las alertas y las evacuaciones de tu planta a los sistemas que ya usa tu sala de control: un SCADA, un PSIM o la intranet. Está pensada para que el operador de tu sala vea lo mismo que ve el panel de SafeOnuba, en su propia pantalla, y pueda responder desde allí.

## Qué puedes hacer

- **Recibir en tiempo real** cada alerta y cada cambio de una evacuación con [webhooks](https://safeonuba.com/desarrolladores/webhooks): un `POST` firmado a tu servidor por cada evento.
- **Consultar** alertas, evacuaciones y su recuento, quién falta, trabajadores, relojes, centros, zonas y las posiciones que la privacidad de la planta deja ver.
- **Reconocer y cerrar alertas** desde tu sala con `PUT /v1/alerts/{id}`. Siempre en nombre de un operador, que queda en la cronología de la alerta.
- **Recuperar lo perdido** si tu receptor se cae: `GET /v1/events` devuelve los eventos de los últimos 30 días, byte a byte iguales a los que se enviaron.
- **Integrar sin relojes** en el [sandbox](https://safeonuba.com/desarrolladores/sandbox), con escenarios simulados de SOS, caída, estrés térmico y una evacuación completa.

## Qué no hace

- **En v1 no abre evacuaciones.** El sistema propone y una persona decide en el panel de SafeOnuba. Tu sala recibe la evacuación en cuanto empieza y la sigue hasta que termina.
- **No da constantes vitales**, ni sueltas ni agregadas. Una alerta de estrés térmico dice qué se le ha indicado al trabajador, no su pulso.
- **No ve más que el panel.** La API ve lo mismo que vería una persona del panel con esos permisos. Lo explica [Privacidad y seguridad](https://safeonuba.com/desarrolladores/privacidad-y-seguridad).

## Entornos

| Entorno | Dirección | Estado |
| --- | --- | --- |
| Producción | `https://api.safeonuba.com` | Se activa al dar de alta al cliente. Hoy todavía no está abierta al público. |
| Pruebas (sandbox) | `https://sandbox.api.safeonuba.com` | Relojes simulados. Es donde se integra. |

Las dos tienen las mismas rutas y el mismo contrato. Lo único que cambia al pasar a producción es la dirección y las credenciales.

## Convenciones

- Todas las rutas van bajo `/v1`, y cada evento lleva `api_version: "2026-09-24"`. Más en [Versiones](https://safeonuba.com/desarrolladores/versiones).
- Cuerpos en JSON, salvo la petición del token, que es `application/x-www-form-urlencoded`.
- Los identificadores son UUID. Las fechas, ISO 8601 en UTC.
- Los errores llegan en `application/problem+json` con el código HTTP real. Ver [Errores](https://safeonuba.com/desarrolladores/errores).
- Las listas se paginan con cursor. Ver [Paginación](https://safeonuba.com/desarrolladores/paginacion).
- Dos rutas son públicas y no piden token: `GET /v1/health` y `GET /v1/openapi.json`, el contrato.

> **Llama a la API desde tu servidor**
>
> La API no tiene CORS: un navegador no puede llamarla directamente. Las peticiones salen de tu backend, que es además donde debe vivir el `client_secret`. Por eso esta documentación no tiene botón de «probar»: los ejemplos son código para copiar.

## Cómo encaja en tu sala

El camino habitual tiene tres piezas:

1. **Un receptor de webhooks** en tu red, con una URL pública en https. Verifica la firma, responde `2xx` enseguida y deja el evento en una cola.
2. **Tu sistema de sala** consume esa cola y pinta la alerta o el banner de evacuación. Cada alerta trae `panel_url`, el enlace a su ficha en el panel de SafeOnuba, por si el operador necesita el detalle.
3. **Llamadas puntuales a la API** cuando hacen falta: la lista de quién falta en una evacuación, reconocer una alerta, o `GET /v1/events` para ponerse al día tras un corte.

Desde que salta un SOS hasta que llega a tu receptor pasan normalmente 1 o 2 segundos. Es una medición típica, no un compromiso de servicio.

## Usar esta documentación con IA

Si integras con un asistente de IA, dale la documentación en Markdown en vez de la URL: la lee entera, sin menús ni pestañas, y con los enlaces ya completos.

- El botón **Copiar**, arriba a la derecha de cada página, copia esa página en Markdown.
- Cualquier página tiene su versión en texto añadiendo `.md` a la dirección: [/desarrolladores/webhooks.md](https://safeonuba.com/desarrolladores/webhooks.md).
- [/llms.txt](https://safeonuba.com/llms.txt) es el índice para las herramientas que lo leen solas, y [/llms-full.txt](https://safeonuba.com/llms-full.txt) es toda la documentación en un solo texto.
- Para generar código con tipos, el [contrato OpenAPI 3.1](https://safeonuba.com/desarrolladores/openapi.json) es la fuente de verdad.

## Todas las operaciones

| Operación | Qué hace | Scope |
| --- | --- | --- |
| [`POST /v1/oauth/token`](https://safeonuba.com/desarrolladores/referencia/autenticacion#createToken) | Pedir un token (client_credentials) | Sin token |
| [`GET /v1/sites`](https://safeonuba.com/desarrolladores/referencia/centros#listSites) | Centros del cliente | Cualquiera |
| [`GET /v1/sites/{id}/zones`](https://safeonuba.com/desarrolladores/referencia/centros#listSiteZones) | Zonas y puntos de reunión de un centro | `alerts:read` |
| [`GET /v1/alerts`](https://safeonuba.com/desarrolladores/referencia/alertas#listAlerts) | Listar alertas | `alerts:read` |
| [`GET /v1/alerts/{id}`](https://safeonuba.com/desarrolladores/referencia/alertas#getAlert) | Detalle de una alerta | `alerts:read` |
| [`PUT /v1/alerts/{id}`](https://safeonuba.com/desarrolladores/referencia/alertas#updateAlert) | Reconocer o cerrar una alerta | `alerts:write` |
| [`GET /v1/musters`](https://safeonuba.com/desarrolladores/referencia/evacuaciones#listMusters) | Listar evacuaciones | `musters:read` |
| [`GET /v1/musters/{id}`](https://safeonuba.com/desarrolladores/referencia/evacuaciones#getMuster) | Estado y recuento de una evacuación | `musters:read` |
| [`GET /v1/musters/{id}/missing`](https://safeonuba.com/desarrolladores/referencia/evacuaciones#listMissingWorkers) | Quién falta | `musters:read` |
| [`GET /v1/workers`](https://safeonuba.com/desarrolladores/referencia/trabajadores#listWorkers) | Listar trabajadores | `workers:read` |
| [`GET /v1/workers/{id}`](https://safeonuba.com/desarrolladores/referencia/trabajadores#getWorker) | Detalle de un trabajador | `workers:read` |
| [`GET /v1/devices`](https://safeonuba.com/desarrolladores/referencia/trabajadores#listDevices) | Listar relojes | `workers:read` |
| [`GET /v1/positions/latest`](https://safeonuba.com/desarrolladores/referencia/posiciones#listLatestPositions) | Últimas posiciones visibles | `positions:read` |
| [`GET /v1/events`](https://safeonuba.com/desarrolladores/referencia/eventos#listEvents) | Reproducir eventos | Cualquiera |
| [`GET /v1/webhooks`](https://safeonuba.com/desarrolladores/referencia/webhooks#listWebhooks) | Listar destinos | `webhooks:manage` |
| [`POST /v1/webhooks`](https://safeonuba.com/desarrolladores/referencia/webhooks#createWebhook) | Crear un destino | `webhooks:manage` |
| [`DELETE /v1/webhooks/{id}`](https://safeonuba.com/desarrolladores/referencia/webhooks#deleteWebhook) | Borrar un destino | `webhooks:manage` |
| [`POST /v1/webhooks/{id}/rotate-secret`](https://safeonuba.com/desarrolladores/referencia/webhooks#rotateWebhookSecret) | Rotar el secreto de firma | `webhooks:manage` |
| [`POST /v1/webhooks/{id}/test`](https://safeonuba.com/desarrolladores/referencia/webhooks#testWebhook) | Enviar un evento de prueba | `webhooks:manage` |
| [`POST /v1/sandbox/scenarios`](https://safeonuba.com/desarrolladores/referencia/sandbox#runSandboxScenario) | Lanzar un escenario de prueba | Cualquiera |

## Siguiente paso

[Primeros pasos](https://safeonuba.com/desarrolladores/primeros-pasos): del acceso a tu primer webhook en cinco minutos.
