Zum Hauptinhalt springen

Webhooks empfangen

Übersicht

Remnawave kann Webhooks für viele Ereignisse senden.

Konfiguration

.env-Konfiguration

WEBHOOK_ENABLED=true
WEBHOOK_URL=https://your-server.com/webhook
WEBHOOK_SECRET_HEADER=your-secret-header
VariableBeschreibung
WEBHOOK_ENABLEDWebhooks aktivieren.
WEBHOOK_URLDie URL, an die der Webhook gesendet wird. (muss mit https:// oder http:// beginnen). Mehrere URLs können durch Kommas getrennt angegeben werden (ohne Leerzeichen)
WEBHOOK_SECRET_HEADERDieser Header wird verwendet, um die Webhook-Nutzlast zu signieren. (nur aA-zZ, 0-9 erlaubt)

Remnawave sendet folgende Header mit der Webhook-Nutzlast:

  • X-Remnawave-Signature - Die Signatur der Webhook-Nutzlast. (signiert mit WEBHOOK_SECRET_HEADER)
  • X-Remnawave-Timestamp - Der Zeitstempel der Webhook-Nutzlast.

Nutzlast

Die Nutzlast ist ein JSON-Objekt.

Beispiel-Webhook-Nutzlast
{
"scope": "service",
"event": "service.panel_started",
"timestamp": "2026-01-07T11:57:29.426Z",
"data": {
"panelVersion": "2.5.0"
}
}

Eigenschaften:

  • scope - Der Geltungsbereich der Webhook-Nutzlast. (Seit v2.5.0)

    • user - Benutzerereignisse
    • user_hwid_devices - Benutzer-HWID-Geräteereignisse
    • node - Knotenereignisse
    • service - Dienstereignisse
    • crm - Infrastruktur-Abrechnungsereignisse
    • torrent_blocker - Torrent-Blocker-Ereignisse
    • errors - Fehlerereignisse
  • event - Das eingetretene Ereignis.

  • timestamp - Der Zeitstempel im ISO 8601-Format.

  • data - Die mit dem Ereignis verbundenen Daten.

tipp

Das detaillierte Nutzlastschema für jeden Bereich ist in der OpenAPI-Dokumentation verfügbar.

👉 Beachten Sie Model Link in jedem Bereichsabschnitt unten.

Bereich: user

OpenAPI Model: RemnawaveWebhookUserEventsDto
Model Link: https://rw.gy/api/#model/RemnawaveWebhookUserEventsDto

Verfügbare Ereignisse (Eigenschaft event):

  • user.created - Benutzer erstellt
  • user.modified - Benutzer geändert
  • user.deleted - Benutzer gelöscht
  • user.revoked - Benutzer widerrufen
  • user.disabled - Benutzer deaktiviert
  • user.enabled - Benutzer aktiviert
  • user.limited - Benutzer limitiert
  • user.expired - Benutzer abgelaufen
  • user.traffic_reset - Benutzertraffic zurückgesetzt
  • user.expires_in_72_hours - Benutzer läuft in 72 Stunden ab (entfernt in v.2.8.0, verwende stattdessen user.expiration)
  • user.expires_in_48_hours - Benutzer läuft in 48 Stunden ab (entfernt in v.2.8.0, verwende stattdessen user.expiration)
  • user.expires_in_24_hours - Benutzer läuft in 24 Stunden ab (entfernt in v.2.8.0, verwende stattdessen user.expiration)
  • user.expired_24_hours_ago - Benutzer ist vor 24 Stunden abgelaufen (entfernt in v.2.8.0, verwende stattdessen user.expiration)
  • user.first_connected - Benutzer zum ersten Mal verbunden
  • user.bandwidth_usage_threshold_reached - Bandbreitennutzungsschwelle erreicht
  • user.not_connected - Benutzer nicht verbunden (aktiv nur wenn NOT_CONNECTED_USERS_NOTIFICATIONS_ENABLED in .env true ist)
  • user.expiration - Benutzerablauf-Benachrichtigungen (Aktiv nur wenn EXPIRATION_NOTIFICATIONS_ENABLED in .env true ist.)

Remnawave Typescript SDK-Typen:

import { TRemnawaveWebhookUserEvent, RemnawaveWebhookUserEvents } from '@remnawave/backend-contract'

meta-Objekt

Die meisten Benutzerereignisse haben meta: null. Das Feld wird nur bei Benachrichtigungs-Ereignissen befüllt und liefert den Kontext, der den Webhook ausgelöst hat.

FeldTypVorhanden fürBeschreibung
notConnectedAfterHoursnumber | nulluser.not_connectedStunden, die der Benutzer offline war. Entspricht dem Schwellenwert aus NOT_CONNECTED_USERS_NOTIFICATIONS_AFTER_HOURS, der das Ereignis auslöste.
expirationnumber | nulluser.expirationVorzeichenbehafteter Stunden-Offset relativ zum Ablauf des Benutzers (expireAt). Entspricht einem Wert aus EXPIRATION_NOTIFICATIONS.

Vorzeichen des expiration-Felds

Der Wert expiration ist vorzeichenbehaftet und kodiert die Richtung relativ zum Ablaufzeitpunkt:

  • Negativ — ausgelöst vor Ablauf: läuft in |N| Stunden ab (z.B. -72 → läuft in 72 Stunden ab).
  • Positiv — ausgelöst nach Ablauf: ist seit N Stunden abgelaufen (z.B. 24 → ist seit 24 Stunden abgelaufen).
hinweis

Für alle anderen Ereignisse außer user.not_connected und user.expiration ist meta gleich null. Wenn meta vorhanden ist, ist nur das Feld für das aktuelle Ereignis gesetzt — das andere Feld bleibt null.

Bereich: user_hwid_devices

OpenAPI Model: RemnawaveWebhookUserHwidDevicesEventsDto​
Model Link: https://rw.gy/api/#model/RemnawaveWebhookUserHwidDevicesEventsDto

Verfügbare Ereignisse:

  • user_hwid_devices.added - Benutzer-HWID-Gerät hinzugefügt
  • user_hwid_devices.deleted - Benutzer-HWID-Gerät gelöscht
import {
TRemnawaveWebhookUserHwidDevicesEvent,
RemnawaveWebhookUserHwidDevicesEvents
} from '@remnawave/backend-contract'

Bereich: node

OpenAPI Model: RemnawaveWebhookNodeEventsDto​
Model Link: https://rw.gy/api/#model/RemnawaveWebhookNodeEventsDto

Verfügbare Ereignisse:

  • node.created - Knoten erstellt
  • node.modified - Knoten geändert
  • node.disabled - Knoten deaktiviert
  • node.enabled - Knoten aktiviert
  • node.deleted - Knoten gelöscht
  • node.connection_lost - Knotenverbindung verloren
  • node.connection_restored - Knotenverbindung wiederhergestellt
  • node.traffic_notify - Knotentraffic-Benachrichtigung
import { TRemnawaveWebhookNodeEvent, RemnawaveWebhookNodeEvents } from '@remnawave/backend-contract'

Bereich: service

OpenAPI Model: RemnawaveWebhookServiceEventsDto
Model Link: https://rw.gy/api/#model/RemnawaveWebhookServiceEventsDto

Verfügbare Ereignisse:

  • service.panel_started - Panel gestartet
  • service.login_attempt_failed - Anmeldeversuch fehlgeschlagen
  • service.login_attempt_success - Anmeldeversuch erfolgreich
  • service.subpage_config_changed - Unterseiten-Konfiguration geändert
import {
TRemnawaveWebhookServiceEvent,
RemnawaveWebhookServiceEvents
} from '@remnawave/backend-contract'

Bereich: crm

OpenAPI Model: RemnawaveWebhookCrmEventsDto
Model Link: https://rw.gy/api/#model/RemnawaveWebhookCrmEventsDto

Verfügbare Ereignisse:

  • crm.infra_billing_node_payment_in_7_days - Infrastruktur-Abrechnungsknoten Zahlung in 7 Tagen
  • crm.infra_billing_node_payment_in_48hrs - Infrastruktur-Abrechnungsknoten Zahlung in 48 Stunden
  • crm.infra_billing_node_payment_in_24hrs - Infrastruktur-Abrechnungsknoten Zahlung in 24 Stunden
  • crm.infra_billing_node_payment_due_today - Infrastruktur-Abrechnungsknoten Zahlung heute fällig
  • crm.infra_billing_node_payment_overdue_24hrs - Infrastruktur-Abrechnungsknoten Zahlung 24 Stunden überfällig
  • crm.infra_billing_node_payment_overdue_48hrs - Infrastruktur-Abrechnungsknoten Zahlung 48 Stunden überfällig
  • crm.infra_billing_node_payment_overdue_7_days - Infrastruktur-Abrechnungsknoten Zahlung 7 Tage überfällig
import { TRemnawaveWebhookCrmEvent, RemnawaveWebhookCrmEvents } from '@remnawave/backend-contract'

Bereich: torrent_blocker

Ab Remnawave Panel v2.7.0.

OpenAPI Model: RemnawaveWebhookTorrentBlockerEventsDto Model Link: https://rw.gy/api/#model/RemnawaveWebhookTorrentBlockerEventsDto

Verfügbare Ereignisse:

  • torrent_blocker.report - Torrent-Blocker-Bericht
import {
TRemnawaveWebhookTorrentBlockerEvent,
RemnawaveWebhookTorrentBlockerEvents
} from '@remnawave/backend-contract'

Bereich: errors

OpenAPI Model: RemnawaveWebhookErrorsEventsDto Model Link: https://rw.gy/api/#model/RemnawaveWebhookErrorsEventsDto

Verfügbare Ereignisse:

  • errors.bandwidth_usage_threshold_reached_max_notifications - Maximale Benachrichtigungen für Bandbreitenschwelle erreicht

Webhook verifizieren

Remnawave signiert die Webhook-Nutzlast mit dem WEBHOOK_SECRET_HEADER und sendet sie an die WEBHOOK_URL.

Sie können die Webhook-Nutzlast durch Überprüfung der Signatur validieren.

Webhook-Verifizierung
export interface WebhookHeaders {
'x-remnawave-signature': string
'x-remnawave-timestamp': string
}

validateWebhook(data: {
body: unknown
headers: WebhookHeaders
}): boolean {
if (!this.webhookSecret) return false

const signature = createHmac('sha256', this.webhookSecret)
.update(JSON.stringify(data.body))
.digest('hex')

return signature === data.headers['x-remnawave-signature']
}

Beispiele für verschiedene Sprachen

Python

Python-Beispielcode
def validate_webhook(body, signature):
webhook_secret_panel = "your_secret_token"
"""Validate webhook signature"""
if isinstance(body, str):
original_body = body
try:
parsed_body = json.loads(body)
except json.JSONDecodeError as e:
return False
else:
original_body = json.dumps(body, separators=(',', ':'))
parsed_body = body

computed_signature = hmac.new(
webhook_secret_panel.encode('utf-8'),
original_body.encode('utf-8'),
hashlib.sha256
).hexdigest()

return hmac.compare_digest(computed_signature, signature)

Go

Go-Beispielcode
package main

import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"fmt"
"io/ioutil"
"net/http"
)

var webhookSecret = "your-secret-header"

func validateWebhook(body []byte, signature string) bool {
mac := hmac.New(sha256.New, []byte(webhookSecret))
mac.Write(body)
expectedMAC := hex.EncodeToString(mac.Sum(nil))
return hmac.Equal([]byte(signature), []byte(expectedMAC))
}

func webhookHandler(w http.ResponseWriter, r *http.Request) {
body, err := ioutil.ReadAll(r.Body)
if err != nil {
http.Error(w, "Error reading request body", http.StatusBadRequest)
return
}
signature := r.Header.Get("X-Remnawave-Signature")
if !validateWebhook(body, signature) {
http.Error(w, "Invalid signature", http.StatusUnauthorized)
return
}
w.WriteHeader(http.StatusOK)
w.Write([]byte("Webhook received"))
}

func main() {
http.HandleFunc("/webhook", webhookHandler)
fmt.Println("Server running at http://localhost:3000")
http.ListenAndServe(":3000", nil)
}