# Primeros pasos

> Del acceso a tu primer webhook en cinco minutos: pide un token, haz la primera llamada, crea un destino y lanza una alerta de prueba en el sandbox.

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

Esta guía va del acceso a tu primera alerta recibida, todo en el sandbox. Necesitas un terminal con `curl` y, para el paso 5, una URL pública en https donde recibir webhooks.

Los ejemplos leen las credenciales de variables de entorno. No las escribas en el código.

```bash
export SAFEONUBA_CLIENT_ID="cliente-ejemplo"
export SAFEONUBA_CLIENT_SECRET="cs_…"
```

## 1. Pide acceso

El acceso lo damos de alta nosotros, cliente a cliente, y el motivo queda registrado. La planta ve en su panel (Ajustes → Integraciones) quién tiene acceso, a qué y por qué.

Al darte de alta recibes un `client_id` y un `client_secret`, que empieza por `cs_`. **El secreto se enseña una sola vez**: guárdalo en tu gestor de secretos en ese momento.

Para pedirlo, escríbenos a [info@safeonuba.com](mailto:info@safeonuba.com?subject=Acceso%20a%20la%20API%20de%20SafeOnuba) con tu empresa, la planta y qué quieres integrar.

## 2. Pide un token

El token se pide con OAuth2 `client_credentials` y dura una hora.

```bash
curl -X POST https://sandbox.api.safeonuba.com/v1/oauth/token \
  -d grant_type=client_credentials \
  -d client_id="$SAFEONUBA_CLIENT_ID" \
  -d client_secret="$SAFEONUBA_CLIENT_SECRET"
```

```javascript
const res = await fetch('https://sandbox.api.safeonuba.com/v1/oauth/token', {
  method: 'POST',
  body: new URLSearchParams({
    grant_type: 'client_credentials',
    client_id: process.env.SAFEONUBA_CLIENT_ID,
    client_secret: process.env.SAFEONUBA_CLIENT_SECRET,
  }),
});
const { access_token } = await res.json();
```

```python
import os
import requests

res = requests.post(
    "https://sandbox.api.safeonuba.com/v1/oauth/token",
    data={
        "grant_type": "client_credentials",
        "client_id": os.environ["SAFEONUBA_CLIENT_ID"],
        "client_secret": os.environ["SAFEONUBA_CLIENT_SECRET"],
    },
    timeout=10,
)
res.raise_for_status()
access_token = res.json()["access_token"]
```

```csharp
using System.Net.Http.Json;
using System.Text.Json;

using var http = new HttpClient();
var res = await http.PostAsync("https://sandbox.api.safeonuba.com/v1/oauth/token",
    new FormUrlEncodedContent(new Dictionary<string, string>
    {
        ["grant_type"] = "client_credentials",
        ["client_id"] = Environment.GetEnvironmentVariable("SAFEONUBA_CLIENT_ID")!,
        ["client_secret"] = Environment.GetEnvironmentVariable("SAFEONUBA_CLIENT_SECRET")!,
    }));
res.EnsureSuccessStatusCode();
var token = (await res.Content.ReadFromJsonAsync<JsonElement>()).GetProperty("access_token").GetString();
```

La respuesta:

```json
{
  "access_token": "at_9c1e…",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "alerts:read alerts:write musters:read"
}
```

Guarda el `access_token` para los pasos siguientes:

```bash
export SAFEONUBA_TOKEN="…"
```

## 3. Haz la primera llamada

`GET /v1/sites` devuelve los centros a los que tienes acceso. Vale con cualquier scope, así que es la mejor forma de comprobar que el token funciona.

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

Anota el `id` de tu centro de sandbox: lo usarás al lanzar un escenario.

## 4. Crea un destino de webhook

Un destino es la URL de tu receptor y los eventos que quieres que le lleguen. Hace falta el scope `webhooks:manage`.

```bash
curl -X POST https://sandbox.api.safeonuba.com/v1/webhooks \
  -H "Authorization: Bearer $SAFEONUBA_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://sala.cliente.example/safeonuba",
    "event_types": ["alert.*", "muster.*"]
  }'
```

La respuesta trae el `secret` de firma, que empieza por `whsec_`.

> **El secreto del webhook solo se enseña ahora**
>
> Guárdalo en el mismo sitio que el `client_secret`. Si lo pierdes, rótalo con `POST /v1/webhooks/{id}/rotate-secret`.

## 5. Recibe y verifica

Tu receptor tiene que hacer tres cosas con cada envío:

1. Comprobar la firma de `X-SafeOnuba-Signature` sobre el cuerpo **en bruto**.
2. Responder `2xx` en menos de 5 segundos.
3. Procesar el evento después, fuera de esa petición.

[Verificar la firma](https://safeonuba.com/desarrolladores/webhooks/verificar-firma) tiene el receptor completo en Node, Python y C#. Cuando lo tengas desplegado, comprueba que le llega algo:

```bash
curl -X POST https://sandbox.api.safeonuba.com/v1/webhooks/ID_DEL_DESTINO/test \
  -H "Authorization: Bearer $SAFEONUBA_TOKEN"
```

Llega un `alert.created` marcado `simulated: true` y firmado como uno real.

## 6. Lanza un SOS de prueba

En el sandbox, un escenario pone a un reloj simulado a pulsar el SOS. Lo que recibes es lo mismo que recibirías de un reloj de verdad.

```bash
curl -X POST https://sandbox.api.safeonuba.com/v1/sandbox/scenarios \
  -H "Authorization: Bearer $SAFEONUBA_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "scenario": "sos" }'
```

A tu receptor le llega un `alert.created` con la alerta completa en `data.alert`. Si nadie la cierra, se cierra sola a los 10 minutos.

## 7. Reconócela desde tu sala

Con el scope `alerts:write`, tu sala puede hacerse cargo de la alerta. Siempre en nombre de una persona: el `actor` es el operador que la ha reconocido, y queda en la cronología de la alerta.

```bash
curl -X PUT https://sandbox.api.safeonuba.com/v1/alerts/ID_DE_LA_ALERTA \
  -H "Authorization: Bearer $SAFEONUBA_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "status": "acknowledged",
    "actor": { "name": "E. Ejemplo", "external_id": "OP-117" }
  }'
```

A tu receptor le llega `alert.acknowledged`, y en el panel de SafeOnuba la alerta aparece reconocida «vía integración».

## Siguientes pasos

- [Autenticación y scopes](https://safeonuba.com/desarrolladores/autenticacion): cachear el token y rotar el secreto sin cortes.
- [Webhooks](https://safeonuba.com/desarrolladores/webhooks): reintentos, duplicados, orden y filtros.
- [Sandbox](https://safeonuba.com/desarrolladores/sandbox): el escenario de evacuación completa, de principio a fin.
