Nova Uptime
Guiasincident-responseescalationon-call

Alertas de e-mail personalizados e escalonamentos: roteamento avançado de incidentes

Crie workflows de escalonamento que acionam a pessoa certa no momento certo. Guia de roteamento de alertas, integração on-call e políticas de escalonamento.

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

O problema do escalonamento#

São 3 da manhã. Seu site cai. Você precisa que alguém responda imediatamente.

Sem escalonamento:

  • O alerta dispara no canal Slack do on-call
  • Ninguém vê (todo mundo dormindo, notificações do Slack desligadas)
  • 45 minutos depois, reclamações de clientes acordam alguém
  • MTTR: 45 minutos

Com escalonamento inteligente:

  • O alerta dispara no Slack (prioridade baixa)
  • Se ninguém confirmar em 2 minutos → SMS para o primário on-call
  • Se não houver resposta em 5 minutos → ligação para o backup on-call
  • MTTR: 5 minutos

Este guia mostra como construir workflows de escalonamento inteligentes.

Entendendo a severidade do incidente#

Primeiro, classifique os incidentes por severidade:

Tier 1: Crítico (notificação imediata)#

  • Site de produção fora do ar (impacto em receita)
  • Falha no gateway de pagamento
  • API retornando erros 5xx
  • Replicação do banco de dados caída

Ações:

  • Slack #critical-incidents (sempre monitorado)
  • SMS para o primário on-call
  • Ligação telefônica se não houver resposta ao SMS
  • Criação automática de ticket no Jira
  • Atualização da status page

Tier 2: Aviso (notificação em horário comercial)#

  • Degradação de tempo de resposta
  • Erros em serviços não críticos
  • Problemas de deliverability de e-mail
  • Lentidão em queries do banco de dados

Ações:

  • Slack #alerts (verificado em horário comercial)
  • Criação de ticket no Jira
  • Resumo por e-mail no fim do dia

Tier 3: Info (apenas log)#

  • Vencimento de domínio em 30 dias
  • Vencimento de SSL em 90 dias
  • Relatório semanal de tendências
  • Limiar de métrica não crítica

Ações:

  • Resumo semanal por e-mail
  • Notificação no dashboard (sem push)

Construindo sua política de escalonamento#

Passo 1: defina as rotações on-call#

Crie uma rotação que mostre quem está on-call e quando:

Seg-Sex 9h - 17h: Alice (primário), Bob (backup)
Seg-Sex 17h - 9h: Charlie (primário), Diana (backup)
Sáb-Dom 24h: Eve (primário), Frank (backup)
Feriados: George (on-call o dia todo)

Passo 2: defina os tempos de escalonamento#

T+0:    O alerta dispara no Slack
T+2min: Sem confirmação → SMS para o primário
T+5min: Sem resposta → ligação para o primário
T+10min: Sem resposta → SMS para o backup
T+15min: Ainda sem resposta → notifica o time inteiro

Passo 3: implemente a lógica de escalonamento#

No Nova Uptime (se suportado):

  1. Configurações do domínio → Alertas
  2. Defina severidade: Crítico
  3. Configure o escalonamento:
    • Passo 1: Slack #critical-incidents
    • Passo 2 (2 min): SMS para o on-call
    • Passo 3 (5 min): ligação telefônica
    • Passo 4 (10 min): time inteiro

Via Webhook + sistema customizado:

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
}

Avançado: roteamento inteligente de alertas#

Padrão 1: roteamento por horário do dia#

Pessoas diferentes on-call em horários diferentes.

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'
    };
  }
}

Padrão 2: roteamento por domínio#

Times diferentes são donos de domínios diferentes.

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);
  }
}

Padrão 3: roteamento por tipo de incidente#

Especialistas diferentes para falhas diferentes.

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());
}

Padrão 4: escalonamento condicional#

Caminhos de escalonamento diferentes com base nas propriedades do 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' });
  }
}

Integração com PagerDuty#

Para uma gestão on-call sofisticada, integre com 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}`);
}

Confirmação e handoff#

Padrões de confirmação#

O engenheiro on-call precisa confirmar o 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 entre times#

Quando um engenheiro on-call precisa passar o bastão para outro:

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`
  });
}

Medindo a eficácia do escalonamento#

Acompanhe as métricas de escalonamento:

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`);
}

Erros comuns de escalonamento#

Erro 1: fadiga de alertas levando ao descarte#

Problema: alertas demais → o time para de responder → problemas reais passam despercebidos

Solução: use limiares rigorosos. Acione apenas para problemas realmente críticos.

Erro 2: escalonamento agressivo demais#

Problema: acionar o time inteiro em todo incidente → burnout → as pessoas saem

Solução: escalone gradualmente. Dê 5 minutos ao primário antes de acionar o backup.

Erro 3: escalonamento lento demais#

Problema: incidente crítico passa despercebido por 30 minutos → impacto enorme

Solução: para incidentes críticos, escalone em até 2-5 minutos.

Erro 4: sem processo de handoff#

Problema: o primário on-call não sabia que estava recebendo o bastão → caos

Solução: comunique handoffs explicitamente via Slack/e-mail.

Resumo: checklist de configuração de escalonamento#

  • Defina os tiers de severidade dos incidentes (Crítico/Aviso/Info)
  • Crie um cronograma de rotação on-call
  • Defina os tempos de escalonamento (2 min → 5 min → 10 min → todos)
  • Configure os canais de escalonamento (Slack → SMS → telefone → time inteiro)
  • Configure roteamento por horário do dia
  • Roteamento por domínio/dono do time
  • Roteamento por tipo de incidente (banco de dados vs API vs e-mail)
  • Integre com PagerDuty (se aplicável)
  • Configure mecanismos de confirmação (reação no Slack, resposta de SMS)
  • Defina os procedimentos de handoff
  • Acompanhe as métricas de escalonamento
  • Revisão mensal da eficácia do escalonamento

Comece hoje#

Comece simples: só Slack + SMS. Adicione complexidade (PagerDuty, roteamento condicional) conforme o volume de incidentes cresce.

Documente sua política de escalonamento na wiki do time. Compartilhe com todos os membros do time. Teste trimestralmente para garantir que tudo continua funcionando.

Sua política de escalonamento é a diferença entre "incidente resolvido em 5 minutos" e "indisponibilidade durou 3 horas". Invista em acertar isso.

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

Artigos relacionados