Zum Inhalt

Webhooks

Generischer Output: bei einem Alert schickt Vesana einen JSON-POST an eine URL deiner Wahl. Damit lässt sich alles anflanschen — eigene Ticket-Systeme, Pager, IFTTT, n8n, Zapier.

Konfiguration

/notification-channelsNeuer Channel → Typ Webhook:

Feld Wert
Name „Pager-Webhook"
URL https://pager.example.com/incoming/...
Method POST (Default)
Auth None / Basic / Bearer
Headers Custom-Header für Auth o. ä.
HMAC-Secret optional, für Signatur-Validierung
Payload-Template Default oder 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-Signierung

Wenn webhook_secret gesetzt:

X-Vesana-Signature: sha256=<hex-digest>
X-Vesana-Timestamp: 1714039000

signature wird berechnet als HMAC-SHA256(secret, timestamp + "." + body). Empfänger soll selbst signieren und vergleichen:

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-Schutz: Timestamps älter als 5 min ablehnen.

Custom-Payload-Template

Wenn der Empfänger ein bestimmtes Format erwartet (z. B. 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 }}
    }
  }
}

Damit lässt sich Vesana direkt an PagerDuty, OpsGenie, VictorOps etc. anflanschen.

Retry

Bei Empfänger-Fehler (Timeout, 5xx):

  • 3 Retries mit Backoff: 5 s, 30 s, 5 min
  • Nach drittem Fehlversuch: failed-Eintrag im Notification-Log

4xx-Antworten werden nicht retried — die sieht der Server als „Empfänger sagt nein, akzeptier es".

Gruppen-Payloads

Bei aktiver Gruppierung in der Alert-Rule kommt ein Array:

{
  "version": 1,
  "group_id": "uuid",
  "rule_name": "Disk Full Critical",
  "alerts": [
    { "host": {...}, "service": {...} },
    { "host": {...}, "service": {...} },
    ...
  ],
  "alert_count": 12
}

Empfänger sollte beide Cases (single + group) handhaben.

Test

/notification-channels → Channel → Test:

  • Schickt Fake-Alert-Payload
  • UI zeigt HTTP-Status, Response-Body, Latenz
  • Wenn HMAC: Signature wird mitgeschickt, Empfänger kann seine Validierung testen

Audit

Jede Webhook-Notification (success oder fail) im Audit-Log mit action = notification.send.

Anschluss