Webhook Configuration
Route Statusly events to Slack, Discord, your CRM, or any HTTP endpoint in real time.
1. Create a Webhook Endpoint
Navigate to Settings → Integrations → Webhooks in your Statusly dashboard. Click New Webhook and provide a publicly reachable HTTPS URL. Statusly sends a POST request every time a selected event occurs — your endpoint must respond with a 2xx status within 5 seconds.
For Slack, use an Incoming Webhook URL from Slack → Apps → Incoming Webhooks (e.g., https://hooks.slack.com/services/T04ABC123/B08XYZ456/wKj9mNpQrStUvWxYz). For Discord, use a channel webhook URL from Channel Settings → Integrations → Webhooks (e.g., https://discord.com/api/webhooks/1098765432109876543/aBcDeFgHiJkLmNoPqRsTuVwXyZ).
Event Selection
Choose which events trigger your webhook. Available types: incident.created, incident.resolved, incident.updating, check.failed, check.recovered, maintenance.scheduled, maintenance.completed. Select all or pick individually per endpoint.
Retry Policy
If your endpoint returns 4xx or 5xx, Statusly retries up to 5 times with exponential backoff: 10s, 30s, 2m, 10m, 30m. After the final failure, the event is logged under Webhooks → Failed Deliveries for manual replay.
Payload Formatting
All payloads are sent as application/json with UTF-8 encoding. You can enable the Slack Block Kit toggle to wrap the JSON in a Slack-compatible message block automatically — no custom formatting needed.
2. Event Payload Structure
Every webhook delivery contains a single JSON object. Below is a real incident.created event as it would arrive at your endpoint.
Example: incident.created
{
"id": "evt_9f8e7d6c5b4a3210",
"type": "incident.created",
"timestamp": "2025-06-14T09:32:17Z",
"account": {
"id": "acc_7721904",
"name": "CloudRetail Ltd",
"plan": "Business"
},
"incident": {
"id": "inc_4421a8b3",
"title": "Checkout API returning 502",
"status": "investigating",
"severity": "major",
"affected_checks": [
{
"id": "chk_88f2e1d0",
"name": "POST /api/v2/checkout",
"url": "https://api.cloudretail.ru/api/v2/checkout",
"last_response_time_ms": 12450,
"last_status_code": 502,
"location": "Moscow"
},
{
"id": "chk_99a3f2e1",
"name": "POST /api/v2/checkout",
"url": "https://api.cloudretail.ru/api/v2/checkout",
"last_response_time_ms": 11890,
"last_status_code": 502,
"location": "Frankfurt"
}
],
"created_by": {
"id": "usr_551022",
"name": "Statusly Bot",
"type": "automated"
}
},
"webhook": {
"id": "wh_33c1b2a4",
"name": "Slack #ops-incidents"
}
}
check.failed
Sent when a monitored endpoint returns a non-2xx response or exceeds the timeout threshold (default 10s). Includes check.last_response_time_ms, check.last_status_code, and check.location.
incident.resolved
Sent when an incident transitions to resolved. Payload mirrors incident.created but adds incident.duration_seconds and incident.affected_checks_count.
3. Request Signing & Verification
Every webhook request includes an HMAC-SHA256 signature so you can verify it originated from Statusly and was not tampled with in transit.
Signature Headers
Each POST request carries two custom headers:
Statusly-Signature— HMAC-SHA256 hex digest of the raw request body, keyed with your webhook secret (e.g.,whsec_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6)Statusly-Timestamp— Unix epoch milliseconds when the request was signed (e.g.,1718360537000)
Verification Steps
- Read the raw request body and the
Statusly-Signatureheader. - Compute
HMAC_SHA256(webhook_secret, raw_body)on your server. - Compare the computed digest with the header value using a constant-time string comparison.
- Reject the request if the signature does not match or if
Statusly-Timestampis more than 5 minutes old (prevents replay attacks).
Node.js Verification Example
const crypto = require('crypto');
function verifySignature(payload, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(payload, 'utf8')
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature, 'utf8'),
Buffer.from(expected, 'utf8')
);
}
// Usage in Express
app.post('/webhooks/statusly', (req, res) => {
const sig = req.headers['statusly-signature'];
const secret = process.env.STATUSLY_WEBHOOK_SECRET;
if (!verifySignature(JSON.stringify(req.body), sig, secret)) {
return res.status(401).send('Invalid signature');
}
// Process the event
console.log('Event:', req.body.type);
res.status(200).send('OK');
});
Managing Your Secret
Your webhook secret is generated automatically when you create a webhook. You can rotate it at any time from the webhook settings — Statusly will honor both the old and new secret for 24 hours to avoid missed deliveries. If you lose the secret, generate a new one; existing deliveries will fail until your server is updated.
Webhook Configuration
Route Statusly events to Slack, Discord, your CRM, or any HTTP endpoint in real time.
1. Create a Webhook Endpoint
Navigate to Settings → Integrations → Webhooks in your Statusly dashboard. Click New Webhook and provide a publicly reachable HTTPS URL. Statusly sends a POST request every time a selected event occurs — your endpoint must respond with a 2xx status within 5 seconds.
For Slack, use an Incoming Webhook URL from Slack → Apps → Incoming Webhooks (e.g., https://hooks.slack.com/services/T04ABC123/B08XYZ456/wKj9mNpQrStUvWxYz). For Discord, use a channel webhook URL from Channel Settings → Integrations → Webhooks (e.g., https://discord.com/api/webhooks/1098765432109876543/aBcDeFgHiJkLmNoPqRsTuVwXyZ).
Event Selection
Choose which events trigger your webhook. Available types: incident.created, incident.resolved, incident.updating, check.failed, check.recovered, maintenance.scheduled, maintenance.completed. Select all or pick individually per endpoint.
Retry Policy
If your endpoint returns 4xx or 5xx, Statusly retries up to 5 times with exponential backoff: 10s, 30s, 2m, 10m, 30m. After the final failure, the event is logged under Webhooks → Failed Deliveries for manual replay.
Payload Formatting
All payloads are sent as application/json with UTF-8 encoding. You can enable the Slack Block Kit toggle to wrap the JSON in a Slack-compatible message block automatically — no custom formatting needed.
2. Event Payload Structure
Every webhook delivery contains a single JSON object. Below is a real incident.created event as it would arrive at your endpoint.
Example: incident.created
{
"id": "evt_9f8e7d6c5b4a3210",
"type": "incident.created",
"timestamp": "2025-06-14T09:32:17Z",
"account": {
"id": "acc_7721904",
"name": "CloudRetail Ltd",
"plan": "Business"
},
"incident": {
"id": "inc_4421a8b3",
"title": "Checkout API returning 502",
"status": "investigating",
"severity": "major",
"affected_checks": [
{
"id": "chk_88f2e1d0",
"name": "POST /api/v2/checkout",
"url": "https://api.cloudretail.ru/api/v2/checkout",
"last_response_time_ms": 12450,
"last_status_code": 502,
"location": "Moscow"
},
{
"id": "chk_99a3f2e1",
"name": "POST /api/v2/checkout",
"url": "https://api.cloudretail.ru/api/v2/checkout",
"last_response_time_ms": 11890,
"last_status_code": 502,
"location": "Frankfurt"
}
],
"created_by": {
"id": "usr_551022",
"name": "Statusly Bot",
"type": "automated"
}
},
"webhook": {
"id": "wh_33c1b2a4",
"name": "Slack #ops-incidents"
}
}
check.failed
Sent when a monitored endpoint returns a non-2xx response or exceeds the timeout threshold (default 10s). Includes check.last_response_time_ms, check.last_status_code, and check.location.
incident.resolved
Sent when an incident transitions to resolved. Payload mirrors incident.created but adds incident.duration_seconds and incident.affected_checks_count.
3. Request Signing & Verification
Every webhook request includes an HMAC-SHA256 signature so you can verify it originated from Statusly and was not tampered with in transit.
Signature Headers
Each POST request carries two custom headers:
Statusly-Signature— HMAC-SHA256 hex digest of the raw request body, keyed with your webhook secret (e.g.,whsec_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6)Statusly-Timestamp— Unix epoch milliseconds when the request was signed (e.g.,1718360537000)
Verification Steps
- Read the raw request body and the
Statusly-Signatureheader. - Compute
HMAC_SHA256(webhook_secret, raw_body)on your server. - Compare the computed digest with the header value using a constant-time string comparison.
- Reject the request if the signature does not match or if
Statusly-Timestampis more than 5 minutes old (prevents replay attacks).
Node.js Verification Example
const crypto = require('crypto');
function verifySignature(payload, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(payload, 'utf8')
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature, 'utf8'),
Buffer.from(expected, 'utf8')
);
}
// Usage in Express
app.post('/webhooks/statusly', (req, res) => {
const sig = req.headers['statusly-signature'];
const secret = process.env.STATUSLY_WEBHOOK_SECRET;
if (!verifySignature(JSON.stringify(req.body), sig, secret)) {
return res.status(401).send('Invalid signature');
}
// Process the event
console.log('Event:', req.body.type);
res.status(200).send('OK');
});
Managing Your Secret
Your webhook secret is generated automatically when you create a webhook. You can rotate it at any time from the webhook settings — Statusly will honor both the old and new secret for 24 hours to avoid missed deliveries. If you lose the secret, generate a new one; existing deliveries will fail until your server is updated.