v1 · vista previa

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

↑ ↓ moverseIntro abrirEsc cerrar

Empezar

Autenticación y scopes

OAuth2 client_credentials: cómo pedir un token, qué scopes existen, cómo rotar el client_secret sin cortes y qué errores devuelve el endpoint del token.

En esta página

La API usa OAuth2 con el flujo client_credentials: tu servidor cambia sus credenciales por un token de una hora y manda ese token en cada llamada.

Credenciales

Cada cliente se da de alta a mano, con un motivo que queda registrado. Al darte de alta recibes:

  • un client_id, que identifica a tu empresa;
  • un client_secret, que empieza por cs_ y se enseña una sola vez.

Un cliente puede tener dos secretos activos a la vez. Es lo que permite rotarlo sin cortar la integración (ver Rotar el secreto).

El client_secret vive en tu servidor, en un gestor de secretos o una variable de entorno. Nunca en un navegador, una aplicación de escritorio o un repositorio.

Pedir un token

POST /v1/oauth/token, con el cuerpo en application/x-www-form-urlencoded:

CampoObligatorioValor
grant_typeSíclient_credentials
client_idSíTu identificador de cliente.
client_secretSíTu secreto.
scopeNoScopes separados por espacios, entre los que tiene tu cliente. Sin él, el token lleva todos los de tu cliente.
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" \
  -d scope="alerts:read musters:read"

La respuesta:

CampoQué es
access_tokenEl token. Es opaco: no intentes leerlo, solo reenvíalo.
token_typeSiempre Bearer.
expires_inSegundos de validez: 3600.
scopeLos scopes que lleva el token, separados por espacios.

El token se revoca al instante si se da de baja al cliente.

Usar el token

En todas las demás llamadas, en la cabecera Authorization:

HTTP
GET /v1/alerts?status=unacknowledged HTTP/1.1
Host: sandbox.api.safeonuba.com
Authorization: Bearer <token>

Renovarlo

client_credentials no tiene token de refresco: cuando uno caduca se pide otro con las mismas credenciales. Pide uno, guárdalo en memoria y renuévalo un poco antes de que caduque. No pidas un token por cada llamada: el endpoint del token tiene su propio límite por IP.

let token = null;
let caduca = 0;

export async function tokenSafeOnuba() {
  // Se renueva un minuto antes de caducar.
  if (token && Date.now() < caduca - 60_000) return token;
  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,
    }),
  });
  if (!res.ok) throw new Error(`token: ${res.status} ${await res.text()}`);
  const datos = await res.json();
  token = datos.access_token;
  caduca = Date.now() + datos.expires_in * 1000;
  return token;
}

Si una llamada devuelve 401 con un token que creías vigente (por ejemplo, porque se ha revocado), descarta el que tienes y pide otro una vez. Si vuelve a fallar, no reintentes en bucle: revisa las credenciales.

Scopes

Cada token lleva los scopes que tu cliente tiene concedidos, o el subconjunto que pidas en scope. Pide solo los que usa cada servicio: el receptor de webhooks, por ejemplo, no necesita ninguno, porque no llama a la API.

ScopePara quéOperaciones
alerts:readLeer alertas y zonas.
alerts:writeReconocer y cerrar alertas (siempre con el operador).
positions:readPosiciones que el velo deja ver.
musters:readEvacuaciones y recuento.
workers:readTrabajadores y relojes.
webhooks:manageDestinos de webhook.

GET /v1/sites, GET /v1/events y POST /v1/sandbox/scenarios valen con cualquier token válido.

alerts:write siempre actúa en nombre de una persona: el cuerpo de PUT /v1/alerts/{id} exige el actor, el operador de tu sala que reconoce o cierra la alerta.

Rotar el client_secret

Como un cliente puede tener dos secretos activos, la rotación no corta nada:

  1. Pide un secreto nuevo por el mismo canal por el que pediste el acceso.
  2. Despliega el nuevo en tus servicios. Mientras tanto, los que aún usan el antiguo siguen funcionando.
  3. Comprueba que todos los tokens se piden ya con el nuevo.
  4. Pide que se retire el antiguo.

El secreto de firma de los webhooks es otra cosa y se rota por la API: ver Verificar la firma.

Errores del token

El endpoint del token responde en el formato de OAuth2 (RFC 6749 §5.2), no en problem+json: un campo error y, a veces, error_description. Responde 401 si el cliente o el secreto no son correctos, y 400 para lo demás.

errorQué significa
invalid_requestFalta un campo obligatorio o la petición está mal formada.
invalid_clientEl client_id o el client_secret no son correctos, o el cliente se ha dado de baja.
unauthorized_clientEl cliente no está autorizado a usar este tipo de concesión.
unsupported_grant_typegrant_type no es client_credentials.
invalid_scopeSe ha pedido un scope que no existe o que el cliente no tiene.

En el resto de la API, un token que falta, ha caducado o se ha revocado da 401, y un token sin el scope que pide la ruta da 403. Ver Errores.