Skip to main content

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:

AttemptDelay
1st retry30 seconds
2nd retry2 minutes
3rd retry10 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