| id |
type |
title |
provider |
status |
integration_type |
created_at |
updated_at |
simco_version |
tags |
| INT-014 |
Integration |
Webhooks Outbound |
BullMQ |
Planificado |
events |
2026-01-10 |
2026-01-10 |
4.0.1 |
| webhooks |
| events |
| bullmq |
| integration |
| outbound |
|
INT-014: Webhooks Outbound
Metadata
| Campo |
Valor |
| Codigo |
INT-014 |
| Proveedor |
BullMQ (interno) |
| Tipo |
Eventos |
| Estado |
Planificado |
| Multi-tenant |
Si |
| Epic Relacionada |
MCH-029 |
| Owner |
Backend Team |
1. Descripcion
Sistema de webhooks outbound que permite a tenants recibir notificaciones HTTP cuando ocurren eventos en el sistema. Incluye firma de payloads, reintentos con backoff y logs de entrega.
Casos de uso principales:
- Notificar sistemas externos de nuevos pedidos
- Sincronizar inventario con ERP
- Integraciones con Zapier/Make
- Alertas personalizadas
2. Eventos Disponibles
Eventos por Categoria
| Categoria |
Evento |
Payload |
| Orders |
order.created |
Order completa |
|
order.updated |
Order con cambios |
|
order.completed |
Order finalizada |
|
order.cancelled |
Order cancelada |
| Products |
product.created |
Producto nuevo |
|
product.updated |
Producto modificado |
|
product.deleted |
Producto eliminado |
|
product.low_stock |
Stock bajo minimo |
| Payments |
payment.received |
Pago recibido |
|
payment.failed |
Pago fallido |
|
payment.refunded |
Pago reembolsado |
| Customers |
customer.created |
Cliente nuevo |
|
customer.updated |
Cliente modificado |
3. Configuracion de Endpoints
API de Configuracion
// POST /api/webhooks/endpoints
{
"url": "https://example.com/webhook",
"events": ["order.created", "payment.received"],
"secret": "whsec_xxxxxxxx", // Generado si no se provee
"description": "Mi integracion",
"is_active": true
}
// Response
{
"id": "wh_123abc",
"url": "https://example.com/webhook",
"events": ["order.created", "payment.received"],
"secret": "whsec_xxxxxxxx",
"is_active": true,
"created_at": "2026-01-10T10:00:00Z"
}
Endpoints CRUD
| Metodo |
Ruta |
Descripcion |
| GET |
/api/webhooks/endpoints |
Listar endpoints |
| POST |
/api/webhooks/endpoints |
Crear endpoint |
| GET |
/api/webhooks/endpoints/:id |
Obtener endpoint |
| PATCH |
/api/webhooks/endpoints/:id |
Actualizar endpoint |
| DELETE |
/api/webhooks/endpoints/:id |
Eliminar endpoint |
| GET |
/api/webhooks/deliveries |
Listar entregas |
| POST |
/api/webhooks/endpoints/:id/test |
Enviar test |
4. Payload de Webhook
Estructura
{
"id": "evt_123abc",
"type": "order.created",
"created_at": "2026-01-10T10:00:00Z",
"data": {
"id": "ord_456def",
"total": 150.00,
"items": [...],
"customer": {...}
},
"tenant_id": "550e8400-e29b-41d4-a716-446655440000"
}
| Header |
Valor |
Descripcion |
Content-Type |
application/json |
Tipo de contenido |
X-Webhook-Id |
evt_123abc |
ID del evento |
X-Webhook-Timestamp |
1704880800 |
Unix timestamp |
X-Webhook-Signature |
sha256=xxx |
Firma HMAC |
User-Agent |
MiChangarrito/1.0 |
Identificador |
5. Firma de Payloads
Generacion de Firma
function signPayload(
payload: string,
secret: string,
timestamp: number,
): string {
const signedPayload = `${timestamp}.${payload}`;
const signature = crypto
.createHmac('sha256', secret)
.update(signedPayload)
.digest('hex');
return `sha256=${signature}`;
}
Verificacion en Cliente
// Ejemplo para el receptor del webhook
function verifySignature(
payload: string,
signature: string,
secret: string,
timestamp: number,
tolerance: number = 300, // 5 minutos
): boolean {
const currentTime = Math.floor(Date.now() / 1000);
// Verificar que no sea muy viejo
if (currentTime - timestamp > tolerance) {
throw new Error('Timestamp too old');
}
const expected = signPayload(payload, secret, timestamp);
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected),
);
}
6. Estrategia de Reintentos
Exponential Backoff
Intento 1: Inmediato
Intento 2: 1 segundo
Intento 3: 2 segundos
Intento 4: 4 segundos
Intento 5: 8 segundos
Intento 6: 16 segundos (maximo)
Configuracion BullMQ
await this.webhookQueue.add('deliver', {
endpointId: endpoint.id,
eventId: event.id,
payload: event.data,
}, {
attempts: 6,
backoff: {
type: 'exponential',
delay: 1000,
},
removeOnComplete: true,
removeOnFail: false, // Mantener para logs
});
Codigos de Respuesta
| Codigo |
Accion |
Descripcion |
| 2xx |
Exito |
Entrega exitosa |
| 3xx |
Retry |
Seguir redirecciones |
| 4xx |
Fallo |
No reintentar (excepto 429) |
| 429 |
Retry |
Rate limited, esperar |
| 5xx |
Retry |
Error del servidor |
| Timeout |
Retry |
Esperar siguiente intento |
7. Tabla de BD
Schema webhooks
CREATE SCHEMA IF NOT EXISTS webhooks;
CREATE TABLE webhooks.endpoints (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL,
url VARCHAR(2000) NOT NULL,
description VARCHAR(255),
events TEXT[] NOT NULL,
secret VARCHAR(255) NOT NULL,
is_active BOOLEAN DEFAULT true,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
CREATE TABLE webhooks.deliveries (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
endpoint_id UUID REFERENCES webhooks.endpoints(id),
event_type VARCHAR(100) NOT NULL,
event_id VARCHAR(100) NOT NULL,
payload JSONB NOT NULL,
status VARCHAR(20) NOT NULL, -- pending, success, failed
attempts INTEGER DEFAULT 0,
last_attempt_at TIMESTAMP WITH TIME ZONE,
response_status INTEGER,
response_body TEXT,
error_message TEXT,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
completed_at TIMESTAMP WITH TIME ZONE
);
CREATE INDEX idx_deliveries_endpoint ON webhooks.deliveries(endpoint_id);
CREATE INDEX idx_deliveries_status ON webhooks.deliveries(status);
8. Procesador de Webhooks
@Processor('webhooks')
export class WebhookProcessor {
constructor(
private readonly httpService: HttpService,
private readonly webhookService: WebhookService,
) {}
@Process('deliver')
async handleDelivery(job: Job<WebhookJobData>) {
const { endpointId, eventId, payload } = job.data;
const endpoint = await this.webhookService.getEndpoint(endpointId);
if (!endpoint.is_active) return;
const timestamp = Math.floor(Date.now() / 1000);
const payloadString = JSON.stringify(payload);
const signature = this.signPayload(payloadString, endpoint.secret, timestamp);
try {
const response = await this.httpService.axiosRef.post(
endpoint.url,
payload,
{
headers: {
'Content-Type': 'application/json',
'X-Webhook-Id': eventId,
'X-Webhook-Timestamp': timestamp.toString(),
'X-Webhook-Signature': signature,
'User-Agent': 'MiChangarrito/1.0',
},
timeout: 30000, // 30 segundos
},
);
await this.webhookService.logDelivery(endpointId, eventId, {
status: 'success',
responseStatus: response.status,
attempts: job.attemptsMade + 1,
});
} catch (error) {
const shouldRetry = this.shouldRetry(error);
await this.webhookService.logDelivery(endpointId, eventId, {
status: shouldRetry ? 'pending' : 'failed',
responseStatus: error.response?.status,
errorMessage: error.message,
attempts: job.attemptsMade + 1,
});
if (shouldRetry) {
throw error; // BullMQ reintentara
}
}
}
private shouldRetry(error: any): boolean {
if (!error.response) return true; // Timeout o network error
const status = error.response.status;
return status === 429 || status >= 500;
}
}
9. UI de Administracion
Funcionalidades
- Lista de endpoints configurados
- Crear/editar/eliminar endpoints
- Ver historial de entregas
- Reintentar entregas fallidas
- Enviar evento de prueba
- Rotar secret
Ejemplo de Vista
+--------------------------------------------------+
| Webhooks [+ Nuevo] |
+--------------------------------------------------+
| URL | Eventos | Estado |
|------------------------|--------------|----------|
| https://example.com/wh | order.* | Activo |
| https://zapier.com/... | payment.* | Activo |
| https://erp.local/api | product.* | Inactivo |
+--------------------------------------------------+
Entregas Recientes:
+--------------------------------------------------+
| Evento | Destino | Estado | Fecha |
|-----------------|--------------|--------|--------|
| order.created | example.com | OK | 10:00 |
| payment.failed | zapier.com | OK | 09:55 |
| product.updated | erp.local | Failed | 09:50 |
+--------------------------------------------------+
10. Testing
Enviar Evento de Prueba
// POST /api/webhooks/endpoints/:id/test
{
"event_type": "test.webhook"
}
// Payload enviado
{
"id": "evt_test_123",
"type": "test.webhook",
"created_at": "2026-01-10T10:00:00Z",
"data": {
"message": "This is a test webhook"
}
}
Herramientas de Debug
11. Monitoreo
Metricas
| Metrica |
Descripcion |
Alerta |
| webhook_deliveries_total |
Total entregas |
- |
| webhook_deliveries_success |
Entregas exitosas |
- |
| webhook_deliveries_failed |
Entregas fallidas |
> 10% |
| webhook_delivery_latency |
Latencia de entrega |
> 5s |
| webhook_queue_length |
Jobs pendientes |
> 100 |
12. Referencias
Ultima actualizacion: 2026-01-10
Autor: Backend Team