| id |
title |
type |
status |
priority |
module |
version |
created_date |
updated_date |
| SAAS-010 |
Webhooks |
Module |
Published |
P2 |
webhooks |
1.0.0 |
2026-01-07 |
2026-01-10 |
SAAS-010: Webhooks
Metadata
- Codigo: SAAS-010
- Modulo: Webhooks
- Prioridad: P2
- Estado: Implementado
- Fase: 5 - Integraciones
Descripcion
Sistema de webhooks outbound: configuracion de endpoints por tenant, eventos suscribibles, firma de payloads, reintentos automaticos, y logs de entregas.
Objetivos
- Configuracion de webhooks por tenant
- Eventos suscribibles
- Firma HMAC de payloads
- Reintentos con backoff
- Dashboard de entregas
Implementacion
Estado de Entregables
| Entregable |
Estado |
Archivo |
| webhooks.module.ts |
Implementado |
apps/backend/src/modules/webhooks/webhooks.module.ts |
| webhooks.controller.ts |
Implementado |
apps/backend/src/modules/webhooks/webhooks.controller.ts |
| webhook.service.ts |
Implementado |
apps/backend/src/modules/webhooks/services/webhook.service.ts |
| webhook.processor.ts |
Implementado |
apps/backend/src/modules/webhooks/processors/webhook.processor.ts |
| DDL webhooks schema |
Implementado |
apps/database/ddl/schemas/webhooks/ |
Archivos Creados
Backend:
apps/backend/src/modules/webhooks/
├── webhooks.module.ts # NestJS module with BullMQ
├── webhooks.controller.ts # REST endpoints (10 endpoints)
├── index.ts # Module barrel export
├── entities/
│ ├── webhook.entity.ts # WebhookEntity, WebhookDeliveryEntity
│ └── index.ts
├── dto/
│ ├── webhook.dto.ts # Request/Response DTOs
│ └── index.ts
├── processors/
│ ├── webhook.processor.ts # BullMQ worker for deliveries
│ └── index.ts
└── services/
├── webhook.service.ts # Business logic
└── index.ts
Database:
apps/database/ddl/schemas/webhooks/tables/
├── 01-webhooks.sql # Main webhooks table
└── 02-deliveries.sql # Delivery logs, retry functions
Tablas de Base de Datos
| Tabla |
Descripcion |
| webhooks.webhooks |
Configuracion de webhooks por tenant |
| webhooks.deliveries |
Historial de entregas y reintentos |
Funciones de Base de Datos
| Funcion |
Descripcion |
| webhooks.calculate_next_retry() |
Calcula tiempo de siguiente reintento |
| webhooks.get_pending_retries() |
Obtiene entregas pendientes de retry |
| webhooks.get_webhook_stats() |
Estadisticas de un webhook |
Eventos Disponibles
| Evento |
Descripcion |
Payload |
| user.created |
Usuario creado |
User object |
| user.updated |
Usuario actualizado |
User + changes |
| user.deleted |
Usuario eliminado |
{ userId } |
| subscription.created |
Nueva suscripcion |
Subscription |
| subscription.updated |
Suscripcion cambiada |
Subscription |
| subscription.cancelled |
Suscripcion cancelada |
Subscription |
| invoice.paid |
Factura pagada |
Invoice |
| invoice.failed |
Pago fallido |
Invoice |
| file.uploaded |
Archivo subido |
File object |
| file.deleted |
Archivo eliminado |
{ fileId } |
| tenant.updated |
Tenant actualizado |
Tenant object |
Endpoints API
| Metodo |
Endpoint |
Descripcion |
Implementado |
| GET |
/webhooks/events |
Listar eventos disponibles |
✓ |
| GET |
/webhooks |
Listar webhooks |
✓ |
| GET |
/webhooks/:id |
Obtener webhook |
✓ |
| POST |
/webhooks |
Crear webhook |
✓ |
| PUT |
/webhooks/:id |
Actualizar webhook |
✓ |
| DELETE |
/webhooks/:id |
Eliminar webhook |
✓ |
| POST |
/webhooks/:id/regenerate-secret |
Regenerar secret |
✓ |
| POST |
/webhooks/:id/test |
Enviar test |
✓ |
| GET |
/webhooks/:id/deliveries |
Historial entregas |
✓ |
| POST |
/webhooks/:id/deliveries/:did/retry |
Reintentar |
✓ |
Firma de Payloads
Generacion
function signPayload(payload: object, secret: string): string {
const timestamp = Date.now();
const body = JSON.stringify(payload);
const signature = crypto
.createHmac('sha256', secret)
.update(`${timestamp}.${body}`)
.digest('hex');
return `t=${timestamp},v1=${signature}`;
}
X-Webhook-Signature: t=1704067200000,v1=abc123...
X-Webhook-Id: <webhook-uuid>
X-Webhook-Event: user.created
X-Webhook-Timestamp: 1704067200000
X-Webhook-Delivery: <delivery-uuid>
Verificacion (lado receptor)
function verifySignature(payload: string, signature: string, secret: string): boolean {
const [timestamp, hash] = parseSignature(signature);
// Verificar timestamp (5 min tolerance)
if (Date.now() - timestamp > 300000) return false;
const expected = crypto
.createHmac('sha256', secret)
.update(`${timestamp}.${payload}`)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(hash),
Buffer.from(expected)
);
}
Logica de Reintentos
Intento 1: Inmediato
Intento 2: +1 minuto
Intento 3: +5 minutos
Intento 4: +30 minutos
Intento 5: +2 horas
Intento 6: +6 horas
Despues: Marcar como fallido
Status de Entrega
| Status |
Descripcion |
| pending |
En cola |
| delivered |
Entregado (2xx) |
| failed |
Fallo permanente |
| retrying |
En reintento |
Limites por Plan
| Plan |
Webhooks |
Eventos/mes |
| Free |
0 |
0 |
| Starter |
0 |
0 |
| Pro |
5 |
10,000 |
| Enterprise |
20 |
100,000 |
Dependencias
Tecnologicas
- BullMQ para queue (Redis)
- NestJS @nestjs/bullmq
- ioredis
Depende de
- SAAS-002 (Tenants)
- SAAS-005 (Plans - feature flag)
Bloquea a
- Integraciones de terceros
- Automatizaciones externas
Criterios de Aceptacion
Configuracion
Variables de Entorno
# Redis for BullMQ
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD= # Optional
Seguridad
- Secrets generados aleatoriamente (whsec_...)
- HTTPS requerido para URLs
- Timeout de 30 segundos
- Rate limit por tenant
- No seguir redirects
Uso
Crear Webhook
POST /webhooks
{
"name": "My Webhook",
"url": "https://example.com/webhook",
"events": ["user.created", "subscription.created"],
"headers": {
"X-Custom-Header": "value"
}
}
Dispatch Event (desde otros servicios)
// Inyectar WebhookService
constructor(private webhookService: WebhookService) {}
// Disparar evento
await this.webhookService.dispatch(
tenantId,
'user.created',
{ id: user.id, email: user.email }
);
Ultima actualizacion: 2026-01-10