Nova Uptime
Guideincident-responseescalationon-call

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.

SN
Sumit Nova Uptime
28 febbraio 2026 · 9 min read
Share:

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):

  1. Impostazioni dominio → Avvisi
  2. Imposta severità: Critico
  3. 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 Free

Articoli correlati