API de Webhooks
Los webhooks permiten que tu aplicación reciba callbacks HTTP en tiempo real cuando ocurren eventos en Auris. Cuando un evento suscrito se dispara (p. ej., un usuario inicia sesión, se asigna un rol, una cuenta es bloqueada), Auris envía una solicitud POST a tu URL configurada con un payload JSON que describe el evento.
Cada entrega de webhook está firmada con HMAC-SHA256 usando un secreto de firma por webhook. Esto permite a tu servidor verificar que el payload se originó en Auris y no fue manipulado en tránsito.
Todos los endpoints de gestión de webhooks requieren el permiso manage:webhooks y la cabecera x-tenant.
CRUD de Webhooks
Listar Webhooks
/api/webhooksRequires: manage:webhooksLista todos los endpoints de webhook configurados para el tenant. Devuelve metadatos del webhook incluyendo los eventos suscritos, el estado activo y las estadísticas de fallos. El secreto de firma nunca se devuelve en las respuestas de lista.
Parámetros de consulta
| Parámetro | Tipo | Descripción |
|---|---|---|
page | integer | Número de página (predeterminado: 1) |
limit | integer | Elementos por página (predeterminado: 20) |
Respuesta exitosa
{
"ok": true,
"data": {
"data": [
{
"id": "whk_abc123",
"name": "Production Event Handler",
"url": "https://api.yourapp.com/webhooks/auris",
"events": ["user.created", "user.updated", "login.success", "login.failed"],
"isActive": true,
"lastDeliveryAt": "2025-02-18T09:45:00Z",
"failureCount": 0,
"createdAt": "2025-01-15T10:00:00Z"
}
],
"pagination": {
"page": 1,
"limit": 20,
"total": 3,
"totalPages": 1
}
}
}Crear Webhook
/api/webhooksRequires: manage:webhooksCrea un nuevo endpoint de webhook. Se genera automáticamente un secreto de firma con el prefijo
whsec_. El secreto se devuelve únicamente en la respuesta de creación — guárdalo de forma segura,
ya que no puede recuperarse más adelante (solo rotarse).
Cuerpo de la solicitud
{
"name": "Production Event Handler",
"url": "https://api.yourapp.com/webhooks/auris",
"events": ["user.created", "user.updated", "user.deleted", "login.success"]
}| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
name | string | Sí | Nombre legible para este webhook |
url | string | Sí | URL del endpoint HTTPS que recibirá las solicitudes POST |
events | string[] | Sí | Array de tipos de evento a suscribir (ver Tipos de Evento) |
Las URLs de webhook deben usar HTTPS. Las URLs con HTTP son rechazadas para evitar que los secretos y datos de usuario se transmitan en texto plano.
Respuesta exitosa
{
"ok": true,
"data": {
"id": "whk_def456",
"name": "Production Event Handler",
"url": "https://api.yourapp.com/webhooks/auris",
"events": ["user.created", "user.updated", "user.deleted", "login.success"],
"secret": "whsec_7f3a8b2c4d5e6f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f",
"isActive": true,
"createdAt": "2025-02-18T10:00:00Z"
}
}El campo secret solo se devuelve en la respuesta de creación y después de la rotación del secreto. Cópialo y guárdalo inmediatamente en un lugar seguro (p. ej., variable de entorno o gestor de secretos). No puede recuperarse de nuevo.
Códigos de error
| Código | HTTP | Descripción |
|---|---|---|
VALIDATION_ERROR | 400 | Faltan campos requeridos o los tipos de evento son inválidos |
INVALID_URL | 400 | La URL no es un endpoint HTTPS válido |
INVALID_EVENTS | 400 | Uno o más tipos de evento no son reconocidos |
Obtener Webhook
/api/webhooks/[id]Requires: manage:webhooksRecupera un webhook individual por ID. Devuelve todos los detalles del webhook excluyendo el secreto de firma.
Respuesta exitosa
{
"ok": true,
"data": {
"id": "whk_abc123",
"name": "Production Event Handler",
"url": "https://api.yourapp.com/webhooks/auris",
"events": ["user.created", "user.updated", "login.success", "login.failed"],
"isActive": true,
"failureCount": 0,
"lastDeliveryAt": "2025-02-18T09:45:00Z",
"createdAt": "2025-01-15T10:00:00Z",
"updatedAt": "2025-02-10T14:00:00Z"
}
}Actualizar Webhook
/api/webhooks/[id]Requires: manage:webhooksActualiza el nombre, la URL, los eventos suscritos o el estado activo de un webhook. Todos los campos son opcionales — solo se actualizan los campos proporcionados.
Cuerpo de la solicitud
{
"name": "Production Event Handler v2",
"events": ["user.created", "user.updated", "user.deleted", "login.success", "login.failed", "role.assigned"],
"isActive": true
}Respuesta exitosa
{
"ok": true,
"data": {
"id": "whk_abc123",
"name": "Production Event Handler v2",
"url": "https://api.yourapp.com/webhooks/auris",
"events": ["user.created", "user.updated", "user.deleted", "login.success", "login.failed", "role.assigned"],
"isActive": true,
"updatedAt": "2025-02-18T11:00:00Z"
}
}Eliminar Webhook
/api/webhooks/[id]Requires: manage:webhooksElimina un endpoint de webhook. Las entregas pendientes para este webhook son canceladas. El historial de entregas se conserva con fines de auditoría.
Respuesta exitosa
{
"ok": true,
"data": { "deleted": true }
}Rotación de Secreto
/api/webhooks/[id]/rotate-secretRequires: manage:webhooksGenera un nuevo secreto de firma para el webhook. El secreto antiguo se invalida inmediatamente. El nuevo secreto se devuelve en la respuesta. Todas las entregas posteriores serán firmadas con el nuevo secreto.
Solicitud: No se requiere cuerpo.
Respuesta exitosa
{
"ok": true,
"data": {
"secret": "whsec_9a8b7c6d5e4f3a2b1c0d9e8f7a6b5c4d3e2f1a0b9c8d7e6f5a4b3c2d1e0f9a"
}
}Después de rotar un secreto, actualiza inmediatamente tu manejador de webhook para usar el nuevo secreto. Las entregas en vuelo en el momento de la rotación pueden seguir usando el secreto antiguo. Buenas prácticas: admite la verificación tanto con el secreto antiguo como con el nuevo durante un breve período de gracia durante la rotación.
Pruebas
/api/webhooks/[id]/testRequires: manage:webhooksEnvía una entrega de prueba a la URL del webhook. Auris envía un evento webhook.test con un
payload de ejemplo. La entrega se registra en el historial de entregas. Úsalo para verificar que
tu endpoint es accesible y verifica las firmas correctamente.
Solicitud: No se requiere cuerpo.
Respuesta exitosa
{
"ok": true,
"data": {
"deliveryId": "del_xyz789",
"statusCode": 200,
"success": true,
"responseTime": 142
}
}Payload del evento de prueba (lo que recibe tu endpoint)
{
"event": "webhook.test",
"timestamp": "2025-02-18T10:30:00Z",
"tenant": "acme-corp",
"data": {
"message": "This is a test delivery from Auris.",
"webhookId": "whk_abc123"
}
}Códigos de error
| Código | HTTP | Descripción |
|---|---|---|
DELIVERY_FAILED | 502 | El endpoint del webhook devolvió un código de estado no 2xx |
ENDPOINT_UNREACHABLE | 502 | No se pudo conectar a la URL del webhook (fallo DNS, timeout, etc.) |
Historial de Entregas
Listar Entregas
/api/webhooks/[id]/deliveriesRequires: manage:webhooksLista los intentos de entrega de un webhook, ordenados por fecha descendente. Cada entrega incluye el tipo de evento, el código de estado HTTP, el tiempo de respuesta y si la entrega fue exitosa. Las entregas reintentadas son entradas separadas vinculadas al mismo evento.
Parámetros de consulta
| Parámetro | Tipo | Descripción |
|---|---|---|
page | integer | Número de página (predeterminado: 1) |
limit | integer | Elementos por página (predeterminado: 20) |
Respuesta exitosa
{
"ok": true,
"data": {
"data": [
{
"id": "del_abc123",
"event": "user.created",
"statusCode": 200,
"success": true,
"responseTime": 89,
"attempts": 1,
"payload": {
"event": "user.created",
"timestamp": "2025-02-18T09:45:00Z",
"tenant": "acme-corp",
"data": {
"userId": "usr_abc123",
"email": "[email protected]"
}
},
"createdAt": "2025-02-18T09:45:00Z",
"completedAt": "2025-02-18T09:45:01Z"
},
{
"id": "del_def456",
"event": "login.failed",
"statusCode": 500,
"success": false,
"responseTime": 2034,
"attempts": 3,
"error": "Server returned 500 Internal Server Error",
"payload": {
"event": "login.failed",
"timestamp": "2025-02-18T09:30:00Z",
"tenant": "acme-corp",
"data": {
"email": "[email protected]",
"reason": "INVALID_CREDENTIALS",
"ipAddress": "203.0.113.50"
}
},
"createdAt": "2025-02-18T09:30:00Z",
"completedAt": "2025-02-18T09:35:12Z"
}
],
"pagination": {
"page": 1,
"limit": 20,
"total": 156,
"totalPages": 8
}
}
}Reintentar Entrega
/api/webhooks/[id]/deliveries/[deliveryId]/retryRequires: manage:webhooksReintenta manualmente una entrega fallida. El payload original se reenvía con una nueva firma. Crea una nueva entrada de entrega vinculada al mismo evento.
Solicitud: No se requiere cuerpo.
Respuesta exitosa
{
"ok": true,
"data": {
"deliveryId": "del_ghi789",
"statusCode": 200,
"success": true,
"responseTime": 95
}
}Códigos de error
| Código | HTTP | Descripción |
|---|---|---|
NOT_FOUND | 404 | La entrega no existe |
ALREADY_SUCCESSFUL | 400 | La entrega ya fue exitosa y no necesita reintentarse |
Tipos de Evento
Auris emite los siguientes eventos de webhook. Suscríbete a los eventos relevantes para tu caso de uso.
Eventos de Autenticación
| Evento | Descripción |
|---|---|
login.success | El usuario se autenticó correctamente (cualquier método) |
login.failed | El intento de autenticación falló (credenciales incorrectas, cuenta bloqueada, etc.) |
signup.completed | Nueva cuenta de usuario registrada |
logout.completed | El usuario cerró sesión (sesión invalidada) |
token.refreshed | El access token fue renovado |
password.changed | El usuario cambió su contraseña |
password.reset_requested | Se envió el correo de restablecimiento de contraseña |
magic_link.sent | Se envió el correo con magic link |
magic_link.verified | Se utilizó el magic link para autenticarse |
Eventos de Gestión de Usuarios
| Evento | Descripción |
|---|---|
user.created | Nueva cuenta de usuario creada (mediante API de administración o registro) |
user.updated | Se modificaron campos del perfil del usuario |
user.deleted | La cuenta del usuario fue eliminada de forma lógica |
user.enabled | Un usuario previamente deshabilitado fue reactivado |
user.disabled | La cuenta del usuario fue deshabilitada |
user.email_verified | El usuario verificó su dirección de correo electrónico |
Eventos de Roles y Permisos
| Evento | Descripción |
|---|---|
role.created | Nuevo rol creado |
role.updated | Metadatos o permisos del rol modificados |
role.deleted | Rol eliminado |
role.assigned | Rol asignado a un usuario |
role.unassigned | Rol eliminado de un usuario |
Eventos de Seguridad
| Evento | Descripción |
|---|---|
mfa.enabled | El usuario activó un método 2FA (TOTP, SMS o WebAuthn) |
mfa.disabled | El usuario desactivó un método 2FA |
account.locked | La cuenta fue bloqueada por detección de fuerza bruta |
account.unlocked | El bloqueo de la cuenta fue eliminado |
suspicious_login.detected | Se detectó actividad de inicio de sesión sospechosa (nuevo dispositivo, viaje imposible, etc.) |
Eventos de Organización
| Evento | Descripción |
|---|---|
organization.created | Nueva organización creada |
organization.updated | Metadatos de la organización modificados |
organization.member_added | Usuario añadido a una organización |
organization.member_removed | Usuario eliminado de una organización |
organization.invitation_sent | Correo de invitación enviado |
Eventos del Sistema
| Evento | Descripción |
|---|---|
webhook.test | Entrega de prueba disparada desde la Consola o la API |
Formato del Payload del Webhook
Cada entrega de webhook envía una solicitud POST con un cuerpo JSON:
{
"event": "user.created",
"timestamp": "2025-02-18T10:00:00Z",
"tenant": "acme-corp",
"data": {
"userId": "usr_abc123",
"email": "[email protected]",
"firstName": "Alice",
"lastName": "Smith"
}
}| Campo | Tipo | Descripción |
|---|---|---|
event | string | El tipo de evento (p. ej., user.created) |
timestamp | string | Marca de tiempo ISO 8601 de cuándo ocurrió el evento |
tenant | string | Identificador del tenant donde ocurrió el evento |
data | object | Datos del payload específicos del evento |
La forma de data varía según el tipo de evento. Los eventos de usuario incluyen campos del usuario, los eventos de rol incluyen detalles del rol y los eventos de inicio de sesión incluyen el email y la dirección IP.
Verificación de Firma
Cada entrega incluye dos cabeceras para la verificación de firma:
| Cabecera | Descripción |
|---|---|
X-Webhook-Signature | Firma HMAC-SHA256 del cuerpo de la solicitud |
X-Webhook-Timestamp | Marca de tiempo Unix (segundos) de cuándo se generó la firma |
Algoritmo de Verificación
- Lee el cuerpo de la solicitud sin procesar como cadena UTF-8 (no analices el JSON primero).
- Lee la cabecera
X-Webhook-Timestamp. - Concatena:
timestamp + "." + body - Calcula el HMAC-SHA256 de la cadena concatenada usando el secreto de firma de tu webhook.
- Compara el resultado en hexadecimal con la cabecera
X-Webhook-Signatureusando una comparación en tiempo constante. - Opcionalmente, rechaza las entregas cuya marca de tiempo sea superior a 5 minutos (para prevenir ataques de repetición).
Ejemplo de Verificación en Node.js
import crypto from 'crypto';
function verifyWebhookSignature(rawBody, signature, timestamp, secret) {
// 1. Comprobar la frescura de la marca de tiempo (opcional pero recomendado)
const currentTime = Math.floor(Date.now() / 1000);
if (Math.abs(currentTime - parseInt(timestamp)) > 300) {
throw new Error('La marca de tiempo del webhook es demasiado antigua');
}
// 2. Calcular la firma esperada
const signedPayload = `${timestamp}.${rawBody}`;
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(signedPayload)
.digest('hex');
// 3. Comparación en tiempo constante
const expected = Buffer.from(expectedSignature, 'hex');
const received = Buffer.from(signature, 'hex');
if (expected.length !== received.length) {
return false;
}
return crypto.timingSafeEqual(expected, received);
}
// Manejador Express.js
app.post('/webhooks/auris', express.raw({ type: 'application/json' }), (req, res) => {
const signature = req.headers['x-webhook-signature'];
const timestamp = req.headers['x-webhook-timestamp'];
const rawBody = req.body.toString('utf-8');
if (!verifyWebhookSignature(rawBody, signature, timestamp, process.env.AURIS_WEBHOOK_SECRET)) {
return res.status(401).json({ error: 'Firma inválida' });
}
const event = JSON.parse(rawBody);
console.log(`Evento recibido ${event.event}:`, event.data);
// Procesar el evento...
res.status(200).json({ received: true });
});Ejemplo de Verificación en Python
import hmac
import hashlib
import time
def verify_webhook_signature(raw_body: bytes, signature: str, timestamp: str, secret: str) -> bool:
# Comprobar la frescura de la marca de tiempo
current_time = int(time.time())
if abs(current_time - int(timestamp)) > 300:
return False
# Calcular la firma esperada
signed_payload = f"{timestamp}.{raw_body.decode('utf-8')}"
expected = hmac.new(
secret.encode('utf-8'),
signed_payload.encode('utf-8'),
hashlib.sha256
).hexdigest()
# Comparación en tiempo constante
return hmac.compare_digest(expected, signature)El SDK @auris/js incluye una utilidad integrada de verificación de webhooks: import { verifyWebhookSignature } from '@auris/js'. Gestiona la validación de la marca de tiempo, el cálculo HMAC y la comparación en tiempo constante. Consulta la documentación del SDK para más detalles.
Comportamiento de Entrega
Tiempos de Espera
Auris espera hasta 30 segundos para recibir una respuesta de tu endpoint de webhook. Si tu endpoint no responde dentro de este período, la entrega se marca como fallida.
Política de Reintentos
Las entregas fallidas se reintentan con retroceso exponencial:
| Intento | Demora |
|---|---|
| 1.er reintento | 1 minuto |
| 2.º reintento | 5 minutos |
| 3.er reintento | 30 minutos |
Tras 3 intentos de reintento fallidos, la entrega se marca como permanentemente fallida. Aún puede reintentarse manualmente mediante la API o la Consola.
Desactivación Automática
Si un webhook acumula 10 fallos consecutivos (en cualquier entrega), se desactiva automáticamente (isActive: false). Deberás corregir el endpoint y reactivar manualmente el webhook mediante PATCH /api/webhooks/[id] con { "isActive": true }.
Idempotencia
Tu manejador de webhook debe ser idempotente. En casos raros (problemas de red, reintentos), el mismo evento puede entregarse más de una vez. Usa los campos timestamp y event para deduplicar si es necesario.
Buenas Prácticas
- Responde rápidamente: Devuelve un código de estado
2xxlo antes posible. Procesa el evento de forma asíncrona (p. ej., usando una cola) en lugar de hacer trabajo pesado en el manejador de la solicitud. - Verifica las firmas: Verifica siempre la firma HMAC-SHA256 antes de procesar el payload. Nunca confíes en un payload de webhook sin verificación.
- Usa HTTPS: Auris rechaza URLs de webhook sin HTTPS. Usa un certificado TLS válido.
- Gestiona los reintentos: Diseña tu manejador para ser idempotente. El mismo evento puede entregarse más de una vez.
- Rota los secretos periódicamente: Usa el endpoint de rotación de secretos para generar un nuevo secreto de firma con regularidad (p. ej., trimestralmente).
- Monitorea las entregas: Consulta el historial de entregas en la Consola o mediante la API para asegurarte de que tu endpoint está funcionando correctamente.
Relacionado
- Configurar Webhooks — Guía paso a paso para crear receptores de webhook
- Gestionar Webhooks — Configura webhooks desde la Consola
- API del Motor de Acciones — Hooks de lógica personalizada que complementan los webhooks
- SDK de JavaScript —
verifyWebhookSignature()para la verificación de firmas