Avvisi email personalizzati ed escalation: routing avanzato degli incidenti
Progetta workflow di escalation che notificano la persona giusta al momento giusto. Guida al routing degli avvisi, on-call e policy di escalation.
Il problema dell'escalation#
Sono le 3 di mattina. Il tuo sito web va giù. Hai bisogno che qualcuno risponda subito.
Senza escalation:
- L'avviso scatta sul canale Slack on-call
- Nessuno lo vede (tutti dormono, notifiche Slack disattivate)
- 45 minuti dopo, le lamentele dei clienti svegliano qualcuno
- MTTR: 45 minuti
Con escalation intelligente:
- L'avviso scatta su Slack (priorità bassa)
- Se nessun riscontro entro 2 minuti → SMS al primario on-call
- Se nessuna risposta entro 5 minuti → chiamata al backup on-call
- MTTR: 5 minuti
Questa guida ti mostra come costruire workflow di escalation intelligenti.
Comprendere la severità dell'incidente#
Prima di tutto, classifica gli incidenti per severità:
Tier 1: Critico (notifica immediata)#
- Sito di produzione down (impatto sui ricavi)
- Errore del gateway di pagamento
- API che restituisce errori 5xx
- Replica del database giù
Azioni:
- Slack #critical-incidents (sempre monitorato)
- SMS al primario on-call
- Chiamata telefonica se nessuna risposta SMS
- Creazione automatica ticket Jira
- Aggiornamento status page
Tier 2: Avvertimento (notifica in orario lavorativo)#
- Degrado dei tempi di risposta
- Errori di servizi non critici
- Problemi di deliverability email
- Lentezza delle query del database
Azioni:
- Slack #alerts (controllato in orario lavorativo)
- Creazione ticket Jira
- Digest email a fine giornata
Tier 3: Info (solo log)#
- Scadenza dominio tra 30 giorni
- Scadenza SSL tra 90 giorni
- Report trend settimanale
- Soglia metrica non critica
Azioni:
- Digest email settimanale
- Notifica dashboard (nessuna notifica push)
Costruire la tua policy di escalation#
Step 1: Definisci le rotazioni on-call#
Crea una rotazione che mostri chi è on-call e quando:
Lun-Ven 9:00 - 17:00: Alice (primario), Bob (backup)
Lun-Ven 17:00 - 9:00: Charlie (primario), Diana (backup)
Sab-Dom 24h: Eve (primario), Frank (backup)
Festivi: George (on-call tutto il giorno)
Step 2: Definisci i tempi di escalation#
T+0: L'avviso scatta su Slack
T+2min: Nessun riscontro → SMS al primario
T+5min: Nessuna risposta → chiama il primario
T+10min: Nessuna risposta → SMS al backup
T+15min: Ancora nessuna risposta → notifica all'intero team
Step 3: Implementa la logica di escalation#
In Nova Uptime (se supportato):
- Impostazioni dominio → Avvisi
- Imposta severità: Critico
- Configura escalation:
- Step 1: Slack #critical-incidents
- Step 2 (2 min): SMS all'on-call
- Step 3 (5 min): chiamata telefonica
- Step 4 (10 min): tutto il team
Via Webhook + Sistema personalizzato:
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
}
Avanzato: routing intelligente degli avvisi#
Pattern 1: Routing per orario#
Persone diverse on-call in orari diversi.
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'
};
}
}
Pattern 2: Routing per dominio#
Team diversi possiedono domini diversi.
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);
}
}
Pattern 3: Routing per tipo di incidente#
Expertise diverse per guasti diversi.
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());
}
Pattern 4: Escalation condizionale#
Percorsi di escalation diversi in base alle proprietà dell'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' });
}
}
Integrazione con PagerDuty#
Per una gestione on-call sofisticata, integra 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}`);
}
Riscontro e handoff#
Pattern di riscontro#
L'on-call engineer deve dare riscontro dell'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
}
Handoff tra team#
Quando un on-call engineer deve passare il testimone a un altro:
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`
});
}
Misurare l'efficacia dell'escalation#
Monitora le metriche di escalation:
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`);
}
Errori comuni di escalation#
Errore 1: stanchezza da avvisi che porta a ignorarli#
Problema: troppi avvisi → il team smette di rispondere → si perdono i problemi reali
Soluzione: usa soglie rigorose. Notifica solo per problemi davvero critici.
Errore 2: escalation troppo aggressiva#
Problema: notifica all'intero team per ogni incidente → burnout → le persone se ne vanno
Soluzione: escalation graduale. Dai 5 minuti al primario prima di notificare il backup.
Errore 3: escalation troppo lenta#
Problema: un incidente critico passa inosservato per 30 minuti → impatto enorme
Soluzione: per incidenti critici, fai escalation entro 2-5 minuti.
Errore 4: nessun processo di handoff#
Problema: il primario on-call non sapeva che gli stavano passando il testimone → caos
Soluzione: comunica esplicitamente gli handoff via Slack/email.
Riepilogo: checklist di setup escalation#
- Definisci i tier di severità degli incidenti (Critico/Avvertimento/Info)
- Crea una pianificazione di rotazione on-call
- Definisci i tempi di escalation (2 min → 5 min → 10 min → tutti)
- Imposta i canali di escalation (Slack → SMS → telefono → tutti)
- Configura il routing per orario
- Routing per dominio/proprietà del team
- Routing per tipo di incidente (database vs API vs email)
- Integra con PagerDuty (se applicabile)
- Imposta meccanismi di riscontro (reazione Slack, risposta SMS)
- Definisci le procedure di handoff
- Traccia le metriche di escalation
- Revisione mensile dell'efficacia dell'escalation
Inizia oggi#
Inizia semplice: solo Slack + SMS. Aggiungi complessità (PagerDuty, routing condizionale) man mano che cresce il volume di incidenti.
Documenta la tua policy di escalation nella wiki del team. Condividila con tutti i membri del team. Testa trimestralmente per assicurarti che tutto funzioni ancora.
La tua policy di escalation è la differenza tra "incidente risolto in 5 minuti" e "interruzione durata 3 ore". Investi per fare bene questa parte.
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 FreeArticoli correlati
Webhook e integrazioni per il monitoraggio uptime: workflow personalizzati
Collega il monitoraggio uptime ai tuoi sistemi tramite webhook. Guida completa all'automazione degli incidenti, notifiche custom e integrazione workflow.
Caso Studio: Come il Monitoraggio Uptime Ha Salvato $500K di Ricavi Persi
Esempio reale di come il monitoraggio uptime proattivo ha evitato un impatto catastrofico. Impara dalla storia di risposta agli incidenti di una SaaS.
Come integrare il monitoraggio uptime con Slack: guida agli avvisi in tempo reale
Configura avvisi Slack per il downtime in 10 minuti. Instrada gli incidenti su #alerts e riduci il tempo di risposta da 30 minuti a 60 secondi.