Webhook Setup
Overview
A webhook lets Haltless notify your systems the moment an alert fires. You stand up an HTTPS endpoint, register it as a webhook notification channel, and Haltless sends a JSON POST to it for each alert, so you can page an on-call engineer, open a ticket, or trigger downstream automation.
Webhooks are one of several notification channel types. This guide covers the webhook channel specifically. For the full channel API, see the Notifications API.
How it works
Requirements for your endpoint
- It must be reachable over HTTPS at a publicly resolvable address. Haltless validates the URL when you register the channel and rejects endpoints it cannot safely reach; plain
http://and private/internal addresses are not accepted. - It should respond with a
2xxstatus quickly. Do the minimum inline (acknowledge and enqueue), and process asynchronously.
Register a webhook channel
Creating a channel requires an admin or operator (JWT) session whose role can open the Notifications or Integrations settings tab. You can do it from the Client Portal (Settings → Integrations) or the API.
curl -X POST "https://api.haltless.io/api/v1/notification-channels" \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"channel_type": "webhook",
"name": "ops-pager",
"config": {
"url": "https://hooks.example.com/haltless",
"headers": {
"Authorization": "Bearer YOUR_SHARED_SECRET"
}
},
"notify_on_alert": true,
"is_active": true
}'
| Field | Description |
|---|---|
channel_type | Must be webhook |
name | A label for the channel |
config.url | Your HTTPS endpoint. Required |
config.headers | Optional map of custom headers Haltless sends with every delivery. Use this to authenticate deliveries (see below) |
notify_on_alert | Send this channel a delivery when an alert fires |
is_active | Whether the channel is enabled |
When you read a channel back, any secrets in config (such as an authorization header) are masked in the response.
Delivery payload
Haltless delivers a JSON body via POST. An alert delivery looks like this:
{
"type": "alert",
"message": "Temperature exceeded threshold",
"severity": "critical",
"metric_name": "temperature",
"trigger_value": 87.3,
"threshold_value": 85.0,
"machine_id": "9c8b7a6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d"
}
| Field | Description |
|---|---|
type | What the delivery is about (see below) |
message | Human-readable summary |
severity | info, warning, or critical |
metric_name | The metric that crossed its threshold |
trigger_value | The value that fired the alert |
threshold_value | The configured threshold that was crossed |
machine_id | The machine the alert is for |
type is one of:
type | Sent when |
|---|---|
alert | An alert is raised, or an open alert becomes more severe |
escalation | An alert is still unacknowledged after an escalation policy's wait time. Sent to the channel that policy names; message starts with [ESCALATED — unacknowledged for N min] |
prediction_critical | A machine's predicted health enters the critical band. metric_name is health_score, and the body adds machine_name, health_score, and days_to_maintenance |
test | You send a test delivery (see below) |
Design your handler to tolerate additional fields and type values: new ones may be added over time, and your parser should ignore anything it doesn't recognize.
Test your receiver
Send a test delivery through a channel to confirm your endpoint is wired up correctly, without waiting for a real alert:
curl -X POST "https://api.haltless.io/api/v1/notification-channels/CHANNEL_ID/test" \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
{ "success": true, "error": null }
The test endpoint answers 200 even when the delivery fails, so read success. On failure, error says why, for example the status your endpoint returned.
A test delivery carries a "type": "test" marker so your endpoint can distinguish it from a live alert:
{
"type": "test",
"message": "Test notification from Haltless — channel 'ops-pager'",
"severity": "info",
"metric_name": "test_metric",
"trigger_value": 0.0,
"threshold_value": 0.0
}
success: true means Haltless reached your endpoint and it responded successfully.
Secure your receiver
Because your endpoint is publicly reachable, verify that each request genuinely came from Haltless. Deliveries are not signed, so the shared secret you set in config.headers is what proves a request came from Haltless:
- Authenticate with a header. Put a shared secret in
config.headers(for example, anAuthorizationheader). Haltless sends those headers with every delivery. Reject any request that doesn't carry the expected value. - Require HTTPS. Haltless only delivers to validated HTTPS endpoints; terminate TLS at your edge and refuse plain HTTP.
- Rotate the secret periodically by updating the channel's
config.headers. - Scope the endpoint. Give the webhook URL a hard-to-guess path and treat it as sensitive.
Never act on an unauthenticated request. Validate the shared secret before doing any work.
Delivery behavior
- Haltless expects a
2xxwithin 10 seconds. Redirects are not followed, so a3xxcounts as a failure. - A delivery is tried up to 3 times in quick succession (about 0.5 seconds, then 2 seconds apart), but only when the failure looks temporary: a timeout, a dropped connection, or a
408,425,429,500,502,503, or504response. Any other non-2xxstatus fails the delivery at once. - Every delivery and its outcome is recorded; read the history with
GET /api/v1/notification-channels/{channel_id}/deliveries. - Make your handler idempotent: the same alert may reach you more than once, so key on the alert's fields (for example,
machine_id+metric_name+trigger_value) and de-duplicate on your side. - Webhooks are a notification mechanism, not a system of record. For critical workflows, reconcile against the Alerts API, polling it or querying it on reconnect, so a missed delivery never means a missed alert.
Managing channels
# List channels
curl "https://api.haltless.io/api/v1/notification-channels" \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
# Update a channel (for example, rotate the auth header or pause it)
curl -X PUT "https://api.haltless.io/api/v1/notification-channels/CHANNEL_ID" \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "is_active": false }'
# Delete a channel
curl -X DELETE "https://api.haltless.io/api/v1/notification-channels/CHANNEL_ID" \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
Next steps
- Notifications API: all channel types and their config
- Alerts API: query alert history and reconcile deliveries
- WebSocket Integration: receive alerts live in a dashboard instead of via webhook