Webhook Setup
Deliver alert notifications and escalations to any HTTP endpoint. This guide covers creating webhook notification channels, securing them, and handling payloads.
Overview
Haltless sends webhook notifications when:
- An alert is triggered (sensor threshold crossed)
- An escalation policy fires (alert not acknowledged within the configured window)
Webhooks are one of several notification channel types alongside email, Slack, and Microsoft Teams.
Creating a webhook channel
curl -X POST "https://api.haltless.io/api/v1/notification-channels" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "PagerDuty Integration",
"type": "webhook",
"config": {
"url": "https://your-server.com/haltless-webhook",
"headers": {
"X-Webhook-Secret": "your-shared-secret",
"Content-Type": "application/json"
}
},
"is_active": true
}'
Response:
{
"id": "channel-uuid",
"name": "PagerDuty Integration",
"type": "webhook",
"config": {
"url": "https://your-server.com/haltless-webhook",
"headers": {"X-Webhook-Secret": "your-shared-secret"}
},
"is_active": true,
"created_at": "2026-04-04T10:00:00Z"
}
Testing your webhook
Before relying on a webhook in production, send a test notification:
curl -X POST "https://api.haltless.io/api/v1/notification-channels/{channel_id}/test" \
-H "Authorization: Bearer YOUR_TOKEN"
This sends a sample payload to your endpoint. A successful response:
{"success": true}
Webhook payload format
When an alert triggers, Haltless sends a POST request to your URL with a JSON body:
{
"event": "alert.triggered",
"timestamp": "2026-04-04T10:30:05Z",
"tenant_id": "uuid",
"alert": {
"id": "alert-uuid",
"machine_id": "machine-uuid",
"machine_name": "CNC Mill #1",
"severity": "critical",
"message": "Temperature exceeded threshold",
"metric_name": "temperature",
"metric_value": 87.3,
"threshold_value": 85.0,
"rule_id": "rule-uuid",
"created_at": "2026-04-04T10:30:05Z"
}
}
For escalation events:
{
"event": "alert.escalated",
"timestamp": "2026-04-04T10:45:05Z",
"tenant_id": "uuid",
"alert": { ... },
"escalation": {
"policy_id": "policy-uuid",
"policy_name": "Critical alert , 15min escalation",
"wait_minutes": 15
}
}
Linking webhooks to escalation policies
Create an escalation policy that uses your webhook channel:
curl -X POST "https://api.haltless.io/api/v1/escalation-policies" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Critical , notify ops team in 15min",
"severity": "critical",
"wait_minutes": 15,
"channel_id": "WEBHOOK_CHANNEL_UUID",
"is_active": true
}'
Now if a critical alert is not acknowledged within 15 minutes, Haltless will POST to your webhook endpoint.
Receiving webhooks
Python (Flask)
from flask import Flask, request, abort
app = Flask(__name__)
WEBHOOK_SECRET = "your-shared-secret"
@app.route("/haltless-webhook", methods=["POST"])
def handle_webhook():
# Verify the shared secret
if request.headers.get("X-Webhook-Secret") != WEBHOOK_SECRET:
abort(401)
payload = request.json
event = payload["event"]
if event == "alert.triggered":
alert = payload["alert"]
print(f"[{alert['severity'].upper()}] {alert['machine_name']}: {alert['message']}")
# Forward to PagerDuty, Opsgenie, your ticketing system, etc.
elif event == "alert.escalated":
alert = payload["alert"]
policy = payload["escalation"]
print(f"ESCALATION: {alert['machine_name']} , {policy['policy_name']}")
return {"status": "ok"}, 200
Node.js (Express)
const express = require("express");
const app = express();
app.use(express.json());
const WEBHOOK_SECRET = "your-shared-secret";
app.post("/haltless-webhook", (req, res) => {
if (req.headers["x-webhook-secret"] !== WEBHOOK_SECRET) {
return res.status(401).json({ error: "Unauthorized" });
}
const { event, alert, escalation } = req.body;
if (event === "alert.triggered") {
console.log(`[${alert.severity}] ${alert.machine_name}: ${alert.message}`);
} else if (event === "alert.escalated") {
console.log(`ESCALATION: ${alert.machine_name} , ${escalation.policy_name}`);
}
res.json({ status: "ok" });
});
app.listen(3000);
Securing your webhook endpoint
Shared secret header
Include a secret in the headers config when creating the channel. Validate it on every incoming request (as shown in the examples above).
IP allowlisting
If your infrastructure supports it, restrict incoming traffic to Haltless's egress IPs. Contact support for the current IP ranges.
HTTPS only
Always use an https:// URL. Haltless will reject http:// URLs in production to prevent credentials from being sent in cleartext.
Respond quickly
Return a 2xx response within 5 seconds. If your processing takes longer, accept the webhook immediately and process it asynchronously (e.g., via a message queue).
Retry behavior
If your endpoint returns a non-2xx status or times out, Haltless retries with exponential backoff:
| Attempt | Delay |
|---|---|
| 1st retry | 30 seconds |
| 2nd retry | 2 minutes |
| 3rd retry | 10 minutes |
After 3 failed retries, the notification is marked as failed. Check the notification channel status in the dashboard for delivery failures.
Common integrations
Slack (via webhook)
Use a Slack Incoming Webhook URL directly as a slack channel type instead of a generic webhook:
{
"name": "Alerts Channel",
"type": "slack",
"config": {
"webhook_url": "https://hooks.slack.com/services/T.../B.../..."
},
"is_active": true
}
PagerDuty
Point a generic webhook at PagerDuty's Events API v2 and transform the payload in your middleware, or use the webhook handler examples above to forward alerts.
Microsoft Teams
Use the teams channel type with a Teams Incoming Webhook URL for native integration.
Next steps
- Notification Channels API , full API reference
- Alert Rules , configure what triggers alerts
- WebSocket Integration , real-time streaming alternative