Webhooks¶
Generic output: on alert Vesana POSTs JSON to a URL of your choice. Lets you connect anything — own ticketing, pager, IFTTT, n8n, Zapier.
Configuration¶
/notification-channels → New channel → type Webhook:
| Field | Value |
|---|---|
| Name | „Pager Webhook" |
| URL | https://pager.example.com/incoming/... |
| Method | POST (default) |
| Auth | None / Basic / Bearer |
| Headers | Custom headers for auth etc. |
| HMAC secret | optional for signature validation |
| Payload template | default or custom Jinja |
Default payload¶
{
"version": 1,
"alert_id": "uuid",
"rule_name": "Disk Full Critical",
"rule_id": "uuid",
"status": "CRITICAL",
"trigger_at": "2026-04-25T10:15:30Z",
"is_recovery": false,
"host": {
"id": "uuid",
"name": "web01.acme.local",
"ip_address": "10.10.5.13",
"tenant": { "id": "uuid", "name": "Acme GmbH" },
"tags": ["production","web"],
"url": "https://vesana.example.com/hosts/uuid"
},
"service": {
"id": "uuid",
"display_name": "Disk /var",
"check_type": "agent_disk",
"value": 96.4,
"message": "CRITICAL - /var at 96%",
"perfdata": { "used_percent": 96.4 }
},
"previous_status": "WARNING",
"duration_seconds": 320
}
HMAC signing¶
When webhook_secret is set:
Signature: HMAC-SHA256(secret, timestamp + "." + body). Receiver should sign and compare:
import hmac, hashlib
def valid(body: bytes, ts: str, sig: str, secret: str) -> bool:
expected = "sha256=" + hmac.new(
secret.encode(),
f"{ts}.".encode() + body,
hashlib.sha256,
).hexdigest()
return hmac.compare_digest(sig, expected)
Replay protection: reject timestamps older than 5 min.
Custom payload template¶
When the receiver expects a specific format (e.g. PagerDuty events):
{
"routing_key": "{{ env.PAGERDUTY_KEY }}",
"event_action": "{% if alert.is_recovery %}resolve{% else %}trigger{% endif %}",
"dedup_key": "vesana:{{ alert.service.id }}",
"payload": {
"summary": "{{ alert.host.name }} — {{ alert.service.display_name }}: {{ alert.message }}",
"severity": "{{ alert.status | lower }}",
"source": "{{ alert.host.name }}",
"custom_details": {
"tenant": "{{ alert.host.tenant.name }}",
"value": {{ alert.service.value }}
}
}
}
So you can connect Vesana to PagerDuty, OpsGenie, VictorOps, etc.
Retry¶
On receiver error (timeout, 5xx):
- 3 retries with backoff: 5 s, 30 s, 5 min
- After third failure:
failedentry in notification log
4xx responses are not retried — server takes the receiver's „no" at face value.
Group payloads¶
With grouping in alert rule, an array arrives:
{
"version": 1,
"group_id": "uuid",
"rule_name": "Disk Full Critical",
"alerts": [
{ "host": {...}, "service": {...} },
{ "host": {...}, "service": {...} },
...
],
"alert_count": 12
}
Receiver should handle both cases (single + group).
Test¶
/notification-channels → Channel → Test:
- Sends fake alert payload
- UI shows HTTP status, response body, latency
- With HMAC: signature is included, receiver can test validation
Audit¶
Every webhook notification (success or fail) appears in audit log with action = notification.send.