Notifications
Get alerted when your services go down with Discord webhooks or generic HTTP webhooks.
Quick Setup
- Discord
- Generic Webhook
-
Create a Discord webhook:
- Go to your Discord server settings
- Navigate to Integrations > Webhooks
- Click New Webhook
- Choose a channel and name
- Copy the webhook URL
-
Add the secret to GitHub:
- Go to your repository's Settings > Secrets and variables > Actions
- Click New repository secret
- Name:
DISCORD_WEBHOOK - Value: Your webhook URL
- Click Add secret
-
Enable in config.json:
{
"notifications": {
"discord": {
"enabled": true
}
}
}
-
Get your webhook endpoint from your notification service (Slack, PagerDuty, custom endpoint, etc.)
-
Add the secret to GitHub:
- Go to your repository's Settings > Secrets and variables > Actions
- Click New repository secret
- Name:
WEBHOOK_URL - Value: Your webhook endpoint
- Click Add secret
-
Enable in config.json:
{
"notifications": {
"webhook": true
}
}
Configuration Options
{
"notifications": {
"defaultEnabled": true,
"numberOfDown": 1,
"discord": {
"enabled": true,
"defaultPingRole": "",
"pingForReturn": false
},
"webhook": false
}
}
| Property | Type | Default | Description |
|---|---|---|---|
defaultEnabled | boolean | true | Enable notifications by default for all monitors |
numberOfDown | number | 1 | Consecutive failures before sending notification |
discord.enabled | boolean | false | Enable Discord notifications |
discord.defaultPingRole | string | "" | Default role to ping (see below) |
discord.pingForReturn | boolean | false | Also ping when service recovers |
webhook | boolean | false | Enable generic webhook notifications |
Discord Configuration
Role Pings
Configure who gets notified when services go down:
| Value | Result |
|---|---|
"" | No ping |
"everyone" | @everyone |
"here" | @here |
"123456789" | @RoleID (specific role) |
To get a role ID:
- Enable Developer Mode in Discord (Settings > Advanced > Developer Mode)
- Right-click the role in Server Settings > Roles
- Click "Copy Role ID"
Per-Monitor Overrides
Override the default ping role for specific monitors:
{
"name": "Critical API",
"type": "http",
"target": "https://api.example.com",
"discordPingRole": "everyone"
}
Discord Message Format
When a service goes down, you'll receive an embed like this:
🔴 Monitor Down
━━━━━━━━━━━━━━━━━━━━━━━━
Critical API is experiencing issues
Status: Major Outage
Previous Status: Operational
Target: https://api.example.com
Details: Connection timeout
━━━━━━━━━━━━━━━━━━━━━━━━
My Status Page • Today at 2:45 PM
Recovery notifications (if pingForReturn is enabled):
🟢 Monitor Recovered
━━━━━━━━━━━━━━━━━━━━━━━━
Critical API is back online
Status: Operational
Previous Status: Major Outage
Response Time: 245ms
━━━━━━━━━━━━━━━━━━━━━━━━
My Status Page • Today at 3:12 PM
Generic Webhook
For services other than Discord, enable the generic webhook to receive JSON payloads.
Payload Format
Service Down:
{
"event": "monitor.down",
"monitor": {
"name": "Critical API",
"type": "http",
"target": "https://api.example.com"
},
"status": {
"current": "major",
"currentLabel": "Major Outage",
"previous": "operational",
"previousLabel": "Operational"
},
"message": "Connection timeout",
"responseTime": 10000,
"timestamp": "2024-02-15T14:45:00.000Z",
"siteName": "My Status Page"
}
Service Recovered:
{
"event": "monitor.up",
"monitor": {
"name": "Critical API",
"type": "http",
"target": "https://api.example.com"
},
"status": {
"current": "operational",
"currentLabel": "Operational",
"previous": "major",
"previousLabel": "Major Outage"
},
"message": null,
"responseTime": 245,
"timestamp": "2024-02-15T15:12:00.000Z",
"siteName": "My Status Page"
}
Integration Examples
- Slack
- PagerDuty
- Email (via SendGrid)
Use a Slack Incoming Webhook with a transformation service, or create a simple serverless function:
// Example AWS Lambda / Cloudflare Worker
export async function handler(event) {
const data = JSON.parse(event.body);
const slackMessage = {
text:
data.event === 'monitor.down'
? `🔴 ${data.monitor.name} is down!`
: `🟢 ${data.monitor.name} recovered`,
attachments: [
{
color: data.event === 'monitor.down' ? 'danger' : 'good',
fields: [
{ title: 'Status', value: data.status.currentLabel, short: true },
{ title: 'Target', value: data.monitor.target, short: true },
],
},
],
};
await fetch(process.env.SLACK_WEBHOOK, {
method: 'POST',
body: JSON.stringify(slackMessage),
});
return { statusCode: 200 };
}
Forward to PagerDuty Events API v2:
export async function handler(event) {
const data = JSON.parse(event.body);
const pdEvent = {
routing_key: process.env.PAGERDUTY_KEY,
event_action: data.event === 'monitor.down' ? 'trigger' : 'resolve',
dedup_key: `shadowstatus-${data.monitor.name}`,
payload: {
summary: `${data.monitor.name}: ${data.status.currentLabel}`,
source: data.siteName,
severity: data.event === 'monitor.down' ? 'critical' : 'info',
custom_details: {
target: data.monitor.target,
type: data.monitor.type,
message: data.message,
},
},
};
await fetch('https://events.pagerduty.com/v2/enqueue', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(pdEvent),
});
return { statusCode: 200 };
}
export async function handler(event) {
const data = JSON.parse(event.body);
const subject =
data.event === 'monitor.down'
? `🔴 Alert: ${data.monitor.name} is DOWN`
: `🟢 Resolved: ${data.monitor.name} is back up`;
await fetch('https://api.sendgrid.com/v3/mail/send', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.SENDGRID_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
personalizations: [{ to: [{ email: '[email protected]' }] }],
from: { email: '[email protected]' },
subject: subject,
content: [
{
type: 'text/plain',
value: `Monitor: ${data.monitor.name}\nStatus: ${data.status.currentLabel}\nTarget: ${data.monitor.target}\n\nMessage: ${data.message || 'N/A'}`,
},
],
}),
});
return { statusCode: 200 };
}
Notification Logic
When Notifications Are Sent
- First Failure: If
numberOfDownis 1, notify immediately on first failure - Consecutive Failures: If
numberOfDownis 2+, notify only after that many consecutive failures - Recovery: Notify when a monitor returns to operational (if enabled)
- No Duplicates: Same status won't trigger multiple notifications
Notification State
The system tracks notification state in data/notification-state.json:
{
"Critical API": {
"lastStatus": "major",
"consecutiveDown": 3,
"notified": true
}
}
This prevents duplicate notifications and tracks consecutive failure counts.
Per-Monitor Settings
Disable notifications for specific monitors:
{
"name": "Third-Party Service",
"type": "statuspage",
"target": "https://status.example.com",
"notify": false
}
This is useful for:
- Third-party status page monitors
- Non-critical services
- Services with known intermittent issues
Troubleshooting
Discord Notifications Not Sending
- Check the secret: Verify
DISCORD_WEBHOOKis set correctly in repository secrets - Verify webhook URL: Make sure it starts with
https://discord.com/api/webhooks/ - Check config: Ensure
notifications.discord.enabledistrue - Check monitor setting: Verify the monitor has
notify: true(or uses default) - Check workflow logs: Look at GitHub Actions logs for error messages
Webhook Not Receiving Events
- Check the secret: Verify
WEBHOOK_URLis set in repository secrets - Check config: Ensure
notifications.webhookistrue - Test your endpoint: Use a service like webhook.site to verify it receives requests
- Check for HTTPS: Your endpoint must support HTTPS
Getting Too Many Notifications
- Increase threshold: Set
numberOfDownto 2 or higher - Disable for flaky monitors: Set
notify: falseon monitors with known issues - Check for network issues: Ensure GitHub Actions can reliably reach your services
Not Getting Recovery Notifications
Make sure discord.pingForReturn is set to true if you want to be notified when services recover.