Alertas de email personalizadas y escalados: enrutamiento avanzado de incidentes
Diseña flujos de escalado que avisen a la persona adecuada en el momento adecuado. Guía sobre enrutamiento de alertas, integración on-call y políticas de.
El problema del escalado#
Son las 3 de la mañana. Tu web se cae. Necesitas que alguien responda de inmediato.
Sin escalado:
- La alerta salta al canal de Slack on-call
- Nadie la ve (todo el mundo durmiendo, notificaciones de Slack apagadas)
- 45 minutos después, las quejas de los clientes despiertan a alguien
- MTTR: 45 minutos
Con escalado inteligente:
- La alerta salta en Slack (prioridad baja)
- Si no hay confirmación en 2 minutos → SMS al primario on-call
- Si no hay respuesta en 5 minutos → llamada al backup on-call
- MTTR: 5 minutos
Esta guía te muestra cómo construir flujos de escalado inteligentes.
Entender la severidad del incidente#
Lo primero, clasifica los incidentes por severidad:
Nivel 1: Crítico (avisar inmediatamente)#
- Web de producción caída (impacto en ingresos)
- Fallo en la pasarela de pago
- API devolviendo errores 5xx
- Replicación de base de datos caída
Acciones:
- Slack #critical-incidents (siempre monitorizado)
- SMS al primario on-call
- Llamada telefónica si no hay respuesta por SMS
- Creación automática de ticket en Jira
- Actualización de la status page
Nivel 2: Aviso (notificar en horario laboral)#
- Degradación de tiempos de respuesta
- Errores en servicios no críticos
- Problemas de deliverability de email
- Lentitud en queries de base de datos
Acciones:
- Slack #alerts (consultado en horario laboral)
- Creación de ticket en Jira
- Digest por email al final del día
Nivel 3: Información (solo log)#
- Caducidad de dominio en 30 días
- Caducidad de SSL en 90 días
- Informe de tendencias semanal
- Umbral de métrica no crítica
Acciones:
- Digest semanal por email
- Notificación en dashboard (sin avisar a nadie)
Construir tu política de escalado#
Paso 1: Define las rotaciones on-call#
Crea una rotación que muestre quién está on-call y cuándo:
Lunes-Viernes 9:00 - 17:00: Alice (primaria), Bob (backup)
Lunes-Viernes 17:00 - 9:00: Charlie (primario), Diana (backup)
Sábado-Domingo 24 h: Eve (primaria), Frank (backup)
Festivos: George (on-call todo el día)
Paso 2: Define los tiempos de escalado#
T+0: La alerta salta en Slack
T+2min: Sin confirmación → SMS al primario
T+5min: Sin respuesta → llamada al primario
T+10min: Sin respuesta → SMS al backup
T+15min: Todavía sin respuesta → avisar al equipo completo
Paso 3: Implementa la lógica de escalado#
En Nova Uptime (si está soportado):
- Configuración del dominio → Alertas
- Establece severidad: Crítica
- Configura el escalado:
- Paso 1: Slack #critical-incidents
- Paso 2 (2 min): SMS al on-call
- Paso 3 (5 min): llamada telefónica
- Paso 4 (10 min): todo el equipo
Vía Webhook + sistema personalizado:
async function handleCriticalIncident({ domain, detectedAt }) {
const oncall = await getOnCallEngineer(new Date());
// Step 1: Immediate Slack alert
const slackMessage = await postToSlack({
channel: '#critical-incidents',
text: `🚨 CRITICAL: ${domain} is down`,
blocks: [
{
type: 'section',
text: {
type: 'mrkdwn',
text: `@${oncall.slackHandle}: ${domain} is down. Acknowledge with reaction.`
}
}
]
});
// Step 2: Wait 2 minutes for acknowledgment
const acknowledged = await waitForAcknowledgment(slackMessage, 2 * 60 * 1000);
if (!acknowledged) {
// Step 2: Send SMS
await sendSMS({
to: oncall.phone,
message: `CRITICAL: ${domain} down. Reply OK to acknowledge.`
});
}
// Step 3: Wait 5 minutes total
const smsAcknowledged = await waitForSMS(oncall.phone, 5 * 60 * 1000);
if (!smsAcknowledged) {
// Step 3: Phone call
await makePhoneCall({
to: oncall.phone,
message: `Critical incident. Website ${domain} is down. Press 1 to acknowledge.`
});
}
// ... Continue escalation chain
}
Avanzado: enrutamiento inteligente de alertas#
Patrón 1: Enrutar por hora del día#
Distintas personas on-call en distintos momentos.
async function getOnCallEngineer(timestamp) {
const hour = new Date(timestamp).getHours();
const dayOfWeek = new Date(timestamp).getDay();
// Business hours (9 AM - 5 PM weekdays)
if (dayOfWeek >= 1 && dayOfWeek <= 5 && hour >= 9 && hour < 17) {
return {
name: 'Alice',
slackHandle: 'alice',
phone: '+1-555-0100',
email: 'alice@company.com'
};
}
// After hours (weekdays)
if (dayOfWeek >= 1 && dayOfWeek <= 5 && (hour < 9 || hour >= 17)) {
return {
name: 'Charlie',
slackHandle: 'charlie',
phone: '+1-555-0102',
email: 'charlie@company.com'
};
}
// Weekend
if (dayOfWeek === 0 || dayOfWeek === 6) {
return {
name: 'Eve',
slackHandle: 'eve',
phone: '+1-555-0104',
email: 'eve@company.com'
};
}
}
Patrón 2: Enrutar por dominio#
Cada equipo es dueño de dominios distintos.
async function getTeamForDomain(domain) {
// Engineering team owns api.*, backend.*
if (domain.startsWith('api.') || domain.startsWith('backend.')) {
return 'engineering';
}
// Infrastructure team owns server, monitoring, infra
if (domain.includes('server') || domain.includes('monitor')) {
return 'infrastructure';
}
// Support team owns customer-facing domains
if (domain.startsWith('support.') || domain.startsWith('customer.')) {
return 'support';
}
// Default: DevOps
return 'devops';
}
async function handleIncident({ domain, severity }) {
const team = await getTeamForDomain(domain);
const oncall = await getOnCallEngineer(team, new Date());
if (severity === 'critical') {
await escalate(oncall);
}
}
Patrón 3: Enrutar por tipo de incidente#
Distinta especialidad para distintos tipos de fallo.
async function getExpertForIncident(domain, incidentType) {
if (incidentType === 'database_down') {
// Page database expert
return await getOnCallExpert('database');
} else if (incidentType === 'api_errors') {
// Page API lead
return await getOnCallExpert('backend');
} else if (incidentType === 'email_delivery_failing') {
// Page email ops
return await getOnCallExpert('email');
} else if (incidentType === 'ssl_expired') {
// Page security
return await getOnCallExpert('security');
}
// Default: on-call engineer
return await getOnCallEngineer(new Date());
}
Patrón 4: Escalado condicional#
Distintas rutas de escalado según las propiedades del incidente.
async function escalateIncident({ domain, severity, duration }) {
const oncall = await getOnCallEngineer(new Date());
if (severity === 'critical' && duration > 5 * 60 * 1000) {
// Critical for >5 minutes: Aggressive escalation
await Promise.all([
postSlack({ channel: '#critical-incidents', text: 'CRITICAL ESCALATION' }),
sendSMS(oncall.phone),
makePhoneCall(oncall.phone),
pageBackup(oncall.backup)
]);
} else if (severity === 'critical') {
// Critical but recent: Gentle escalation
await Promise.all([
postSlack({ channel: '#critical-incidents' }),
sendSMS(oncall.phone)
]);
} else if (severity === 'warning') {
// Just log
await postSlack({ channel: '#alerts' });
}
}
Integración con PagerDuty#
Para una gestión sofisticada del on-call, intégrate con PagerDuty:
const PagerDutyClient = require('pagerduty');
async function pageOnCallVia PagerDuty(domain, severity) {
const client = new PagerDutyClient({
token: process.env.PAGERDUTY_TOKEN
});
// Create incident
const incident = await client.incidents.create({
type: 'incident_reference',
incident: {
type: 'incident',
title: `${domain} is down`,
body: {
type: 'incident_body',
description: `Website ${domain} is down. Response: Down. Severity: ${severity}`
},
urgency: severity === 'critical' ? 'high' : 'low',
service: {
id: process.env.PAGERDUTY_SERVICE_ID,
type: 'service_reference'
}
}
});
console.log(`Created PagerDuty incident: ${incident.id}`);
}
Confirmación y traspaso#
Patrones de confirmación#
El ingeniero on-call tiene que confirmar el incidente:
// Via Slack reaction
async function waitForSlackAcknowledgment(messageId, maxWait) {
const startTime = Date.now();
while (Date.now() - startTime < maxWait) {
// Poll for reactions
const reactions = await getSlackMessageReactions(messageId);
if (reactions.includes('white_check_mark')) {
return true; // Acknowledged
}
await sleep(10 * 1000); // Check every 10 seconds
}
return false; // Not acknowledged within max wait
}
// Via SMS
async function waitForSmsAcknowledgment(phone, maxWait) {
const startTime = Date.now();
while (Date.now() - startTime < maxWait) {
// Poll SMS responses
const responses = await getSmsResponses(phone);
if (responses.some(m => m.text.toUpperCase().includes('OK'))) {
return true; // Acknowledged
}
await sleep(5 * 1000); // Check every 5 seconds
}
return false; // Not acknowledged
}
Traspaso entre equipos#
Cuando un ingeniero on-call necesita pasar el testigo a otro:
async function handoffIncident(incident, fromEngineer, toEngineer) {
// Update incident
incident.assignedTo = toEngineer;
incident.handoffAt = new Date();
await incident.save();
// Notify both
await sendMessage({
to: fromEngineer.slack,
text: `Handing off ${incident.domain} to ${toEngineer.name}`
});
await sendMessage({
to: toEngineer.slack,
text: `Taking over incident: ${incident.domain}. See: ${incident.dashboard}`
});
// Update status page
await updateStatusPage({
message: `Working with ${toEngineer.name}'s team on investigation`
});
}
Medir la eficacia del escalado#
Mide las métricas de escalado:
async function analyzeEscalationMetrics() {
const incidents = await Incident.find({
createdAfter: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000) // Last 30 days
});
const metrics = {
totalIncidents: incidents.length,
avgTimeToAcknowledgment: calculateAvg(
incidents.map(i => i.acknowledgedAt - i.alertedAt)
),
avgTimeToEscalation: calculateAvg(
incidents.map(i => i.escalatedAt - i.alertedAt)
),
escalationRate: incidents.filter(i => i.escalatedAt).length / incidents.length,
avgMTTR: calculateAvg(
incidents.map(i => i.resolvedAt - i.alertedAt)
)
};
console.log('Escalation Metrics (Last 30 days):');
console.log(`Total Incidents: ${metrics.totalIncidents}`);
console.log(`Avg Time to Acknowledgment: ${metrics.avgTimeToAcknowledgment / 60}m`);
console.log(`Escalation Rate: ${(metrics.escalationRate * 100).toFixed(1)}%`);
console.log(`Avg MTTR: ${metrics.avgMTTR / 60}m`);
}
Errores comunes en el escalado#
Error 1: fatiga de alertas que lleva a ignorarlas#
Problema: demasiadas alertas → el equipo deja de responder → se pierden los problemas reales
Solución: usa umbrales estrictos. Avisa solo en incidentes realmente críticos.
Error 2: escalar de forma demasiado agresiva#
Problema: avisar a todo el equipo en cada incidente → burnout → la gente se va
Solución: escala de forma gradual. Dale al primario 5 minutos antes de avisar al backup.
Error 3: escalar demasiado despacio#
Problema: un incidente crítico pasa desapercibido durante 30 minutos → impacto enorme
Solución: para incidentes críticos, escala en 2-5 minutos.
Error 4: sin proceso de traspaso#
Problema: el on-call primario no sabía que le estaban pasando el incidente → caos
Solución: comunica los traspasos de forma explícita por Slack/email.
Resumen: checklist de configuración del escalado#
- ✅ Define los niveles de severidad de incidente (Crítico/Aviso/Info)
- ✅ Crea un calendario de rotación on-call
- ✅ Define los tiempos de escalado (2 min → 5 min → 10 min → todos)
- ✅ Configura los canales de escalado (Slack → SMS → llamada → todo el equipo)
- ✅ Configura el enrutamiento por hora del día
- ✅ Enruta por dominio/equipo responsable
- ✅ Enruta por tipo de incidente (base de datos vs API vs email)
- ✅ Intégrate con PagerDuty (si aplica)
- ✅ Configura los mecanismos de confirmación (reacción en Slack, respuesta SMS)
- ✅ Define los procedimientos de traspaso
- ✅ Mide las métricas de escalado
- ✅ Revisión mensual de la eficacia del escalado
Empieza hoy#
Empieza simple: solo Slack + SMS. Añade complejidad (PagerDuty, enrutamiento condicional) según vaya creciendo el volumen de incidentes.
Documenta tu política de escalado en la wiki del equipo. Compártela con todo el equipo. Pruébala cada trimestre para asegurarte de que todo sigue funcionando.
Tu política de escalado es la diferencia entre "incidente resuelto en 5 minutos" y "caída de 3 horas". Invierte en hacerlo bien.
Monitor Your Website Before It Goes Down
Get uptime monitoring, SSL tracking, domain expiry alerts, and email health checks. Free plan — no credit card required.
Start Monitoring FreeArtículos relacionados
Webhooks e integraciones de monitoring de uptime: crea flujos personalizados
Conecta el monitoring de uptime a tus sistemas mediante webhooks. Guía completa de automatización de incidentes, notificaciones personalizadas y patrones.
Caso práctico: cómo el monitoring de uptime ahorró 500.000 $ en ingresos perdidos
Ejemplo real de cómo el monitoring proactivo de uptime evitó un impacto catastrófico en el negocio. Aprende de la historia de respuesta ante incidentes de.
Cómo integrar la monitorización de uptime con Slack: guía de alertas en tiempo real
Configura alertas de Slack para caídas de sitios web en 10 minutos. Enruta incidentes a #alerts y reduce el tiempo de respuesta de 30 minutos a 60 segundos.