Custom e-mailalerts en escalaties: geavanceerde incident-routing
Ontwerp escalatie-workflows die de juiste persoon op het juiste moment paginen. Gids voor alert-routing, on-call integratie en escalatiebeleid.
Het escalatieprobleem#
Het is 3 uur 's nachts. Je website gaat down. Iemand moet meteen reageren.
Zonder escalatie:
- Alert gaat naar het on-call Slack-kanaal
- Niemand ziet het (iedereen slaapt, Slack-notificaties uit)
- 45 minuten later wekken klantklachten iemand
- MTTR: 45 minuten
Met slimme escalatie:
- Alert gaat naar Slack (lage prioriteit)
- Geen acknowledgment binnen 2 minuten → SMS naar primaire on-call
- Geen reactie binnen 5 minuten → Bel de on-call backup
- MTTR: 5 minuten
Deze gids laat zien hoe je intelligente escalatie-workflows bouwt.
Incident-severity begrijpen#
Classificeer incidents eerst op severity:
Tier 1: Critical (direct paginen)#
- Productie-website down (raakt omzet)
- Payment gateway-failure
- API geeft 5xx-errors
- Database-replicatie down
Acties:
- Slack #critical-incidents (altijd in de gaten gehouden)
- SMS naar primaire on-call
- Telefoontje als er geen SMS-reactie komt
- Automatische Jira-ticket-aanmaak
- Statuspagina-update
Tier 2: Warning (paginen tijdens kantooruren)#
- Verslechterde response time
- Niet-kritieke service-errors
- E-maildeliverability-issues
- Trage database-queries
Acties:
- Slack #alerts (gecheckt tijdens kantooruren)
- Jira-ticket-aanmaak
- E-maildigest aan einde van de dag
Tier 3: Info (alleen loggen)#
- Domein verloopt over 30 dagen
- SSL verloopt over 90 dagen
- Wekelijks trendrapport
- Niet-kritieke metric-threshold
Acties:
- Wekelijkse e-maildigest
- Dashboard-notificatie (geen page)
Je escalatiebeleid bouwen#
Stap 1: Definieer on-call rotaties#
Maak een rotatie die laat zien wie wanneer on-call is:
Maandag-vrijdag 9:00 - 17:00: Alice (primair), Bob (backup)
Maandag-vrijdag 17:00 - 9:00: Charlie (primair), Diana (backup)
Zaterdag-zondag 24 uur: Eve (primair), Frank (backup)
Feestdagen: George (de hele dag on-call)
Stap 2: Definieer escalatietijden#
T+0: Alert gaat naar Slack
T+2min: Geen acknowledgment → SMS naar primaire
T+5min: Geen reactie → Bel primaire
T+10min: Geen reactie → SMS naar backup
T+15min: Nog steeds geen reactie → Het hele team paginen
Stap 3: Implementeer de escalatielogica#
In Nova Uptime (indien ondersteund):
- Domein-instellingen → Alerting
- Severity instellen: Critical
- Escalatie configureren:
- Stap 1: Slack #critical-incidents
- Stap 2 (2 min): SMS naar on-call
- Stap 3 (5 min): Telefoontje
- Stap 4 (10 min): Hele team
Via webhook + custom systeem:
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
}
Geavanceerd: smart alert routing#
Patroon 1: Routeer op tijd van de dag#
Verschillende mensen on-call op verschillende momenten.
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'
};
}
}
Patroon 2: Routeer op domein#
Verschillende teams zijn eigenaar van verschillende domeinen.
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);
}
}
Patroon 3: Routeer op incident-type#
Verschillende expertise voor verschillende failures.
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());
}
Patroon 4: Voorwaardelijke escalatie#
Verschillende escalatiepaden op basis van incident-eigenschappen.
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' });
}
}
Integratie met PagerDuty#
Voor geavanceerd on-call beheer integreer je met 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}`);
}
Acknowledgment en handoff#
Acknowledgment-patronen#
De on-call engineer moet het incident bevestigen:
// 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 tussen teams#
Als de ene on-call engineer moet overdragen aan een andere:
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`
});
}
Effectiviteit van escalatie meten#
Track escalatie-metrics:
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`);
}
Veelgemaakte escalatiefouten#
Fout 1: Alert fatigue leidt tot wegklikken#
Probleem: Te veel alerts → team reageert niet meer → echte issues gemist
Oplossing: Gebruik strikte thresholds. Page alleen voor echt kritieke issues.
Fout 2: Te agressief escaleren#
Probleem: Het hele team paginen bij elk incident → burn-out → mensen vertrekken
Oplossing: Escaleer geleidelijk. Geef de primaire 5 minuten voordat je de backup paginet.
Fout 3: Te traag escaleren#
Probleem: Critical incident blijft 30 minuten onopgemerkt → enorme impact
Oplossing: Voor critical incidents, escaleer binnen 2-5 minuten.
Fout 4: Geen handoff-proces#
Probleem: Primaire on-call wist niet dat er werd overgedragen → chaos
Oplossing: Communiceer handoffs expliciet via Slack/e-mail.
Samenvatting: escalatie-setup checklist#
- ✅ Definieer incident-severity tiers (Critical/Warning/Info)
- ✅ Maak een on-call rotatieschema
- ✅ Definieer escalatie-timing (2 min → 5 min → 10 min → iedereen)
- ✅ Zet escalatiekanalen op (Slack → SMS → Telefoon → hele team)
- ✅ Configureer routing op tijd van de dag
- ✅ Routeer op domein/team-eigenaarschap
- ✅ Routeer op incident-type (database vs API vs e-mail)
- ✅ Integreer met PagerDuty (indien van toepassing)
- ✅ Zet acknowledgment-mechanismen op (Slack-reactie, SMS-reactie)
- ✅ Definieer handoff-procedures
- ✅ Track escalatie-metrics
- ✅ Maandelijkse review van escalatie-effectiviteit
Begin vandaag#
Begin simpel: alleen Slack + SMS. Voeg complexiteit toe (PagerDuty, conditional routing) naarmate je incident-volume groeit.
Documenteer je escalatiebeleid in je team-wiki. Deel het met alle teamleden. Test elk kwartaal of alles nog werkt.
Je escalatiebeleid is het verschil tussen "incident opgelost in 5 minuten" en "storing duurde 3 uur." Investeer in het goed neerzetten hiervan.
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 FreeGerelateerde artikelen
Uptime-monitoring webhooks en integraties: bouw custom workflows
Verbind uptime-monitoring met je systemen via webhooks. Complete gids voor incident-automatisering, custom notificaties en workflow-integratiepatronen.
Case Study: Hoe Uptime Monitoring $500K aan Verloren Omzet Bespaarde
Praktijkvoorbeeld van hoe proactieve uptime monitoring catastrofale bedrijfsimpact voorkwam. Leer van het incident response verhaal van een SaaS-bedrijf.
Uptime-monitoring integreren met Slack: gids voor real-time alerts
Stel in 10 minuten Slack-alerts in voor website-downtime. Routeer incidenten naar #alerts en verlaag de responstijd van 30 minuten naar 60 seconden.