v1 · vista previa

Prueba con webhook firma, alert.created, location_withheld o 429.

↑ ↓ moverseIntro abrirEsc cerrar

Tiempo real

Eventos

Los tipos de evento, el sobre común y cómo recuperar los que se perdieron con /v1/events, que devuelve exactamente lo mismo que se envió por webhook.

En esta página

Un evento es un cambio: una alerta que se crea, un operador que la reconoce, un recuento que sube. Los mismos eventos llegan por webhook y se pueden volver a pedir con GET /v1/events.

Tipos de evento

typeQué significa
alert.createdSe ha creado una alerta.
alert.acknowledgedUn operador se ha hecho cargo de la alerta, desde el panel o desde tu sala.
alert.resolvedLa alerta se ha cerrado.
alert.reopenedUna alerta cerrada se ha vuelto a abrir.
alert.worker_acknowledgedEl trabajador ha visto el aviso en su reloj (worker_acknowledged_at).
muster.startedHa empezado una evacuación.
muster.kind_changedHa cambiado el tipo de la evacuación (general, tsunami, terremoto).
muster.phase_changedHa cambiado la fase: evacuating o inspecting.
muster.targets_changedLa sala ha descartado o rehabilitado un punto de reunión.
muster.updatedEl recuento ha cambiado. Se comprueba cada 5 segundos y solo se emite si cambia.
muster.worker_missingFalta una persona.
muster.endedLa evacuación ha terminado.

Al crear un destino puedes suscribirte a grupos enteros (alert.*, muster.*) o a tipos concretos.

El sobre

Todos los eventos tienen la misma forma:

CampoQué es
idIdentificador del evento, para deduplicar.
typeUno de los tipos de arriba.
api_version"2026-09-24".
created_atCuándo ocurrió.
organization_idLa empresa del centro. null en los eventos de prueba.
site_idEl centro.
data{ "alert": … } en los alert.* y { "muster": … } en los muster.*.

Lo que va dentro de data es el objeto completo tal como lo devolvería el GET en ese instante: la alerta entera, o la evacuación con su recuento y sus puntos de reunión. No es un aviso de «algo ha cambiado, ven a mirarlo»; es el estado.

alert.created
{
  "id": "0f6c2b7e-3a1d-4c55-9b0e-7d2f4a8c1e90",
  "type": "alert.created",
  "api_version": "2026-09-24",
  "created_at": "2026-09-24T11:04:12Z",
  "organization_id": "0f6c2b7e-3a1d-4c55-9b0e-7d2f4a8c1e90",
  "site_id": "0f6c2b7e-3a1d-4c55-9b0e-7d2f4a8c1e90",
  "data": {
    "alert": {
      "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"
    }
  }
}

El esquema completo está en la referencia: Event.

Recuperar lo perdido

Si tu receptor ha estado caído, o quieres comprobar que no te falta nada, GET /v1/events devuelve los eventos de los últimos 30 días en orden de publicación, del más antiguo al más reciente. Son exactamente los mismos bytes que se enviaron por webhook, así que los procesas con el mismo código.

ParámetroQué es
sinceObligatorio. Desde cuándo, como mucho 30 días atrás.
typeSolo un tipo de evento.
limit, cursorPaginación (ver Paginación).

Vale con cualquier scope.

// Se pone al día desde el último evento procesado.
async function recuperar(desde) {
  let cursor = null;
  do {
    const url = new URL('https://sandbox.api.safeonuba.com/v1/events');
    url.searchParams.set('since', desde);
    url.searchParams.set('limit', '500');
    if (cursor) url.searchParams.set('cursor', cursor);

    const res = await fetch(url, { headers: { Authorization: `Bearer ${await tokenSafeOnuba()}` } });
    if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`);
    const pagina = await res.json();

    for (const evento of pagina.data) await procesar(evento); // el mismo código que el webhook; deduplica por id
    cursor = pagina.next_cursor;
  } while (cursor);
}

Cuándo usarlo

  • Al arrancar tu receptor tras una parada, desde el created_at del último evento que procesaste.
  • Tras un aviso de entrega perdida: si un envío agota sus reintentos, avisamos por correo a tu contacto técnico. El evento sigue en /v1/events.
  • Para rellenar huecos si ves que te falta un paso intermedio.

Como es la misma entrega «al menos una vez», deduplica por id: lo más normal es que parte de lo que recuperes ya lo hubieras recibido.