Aller au contenu principal

Réception de webhooks

Présentation

Remnawave peut envoyer des webhooks pour de nombreux événements.

Configuration

Configuration .env

WEBHOOK_ENABLED=true
WEBHOOK_URL=https://your-server.com/webhook
WEBHOOK_SECRET_HEADER=your-secret-header
VariableDescription
WEBHOOK_ENABLEDActiver les webhooks.
WEBHOOK_URLL'URL à laquelle envoyer le webhook. (doit commencer par https:// ou http://). Il est possible de spécifier plusieurs URLs séparées par des virgules (sans espaces)
WEBHOOK_SECRET_HEADERCet en-tête sera utilisé pour signer la charge utile du webhook. (seuls aA-zZ, 0-9 sont autorisés)

En-têtes

Remnawave enverra les en-têtes suivants avec la charge utile du webhook :

  • X-Remnawave-Signature - La signature de la charge utile du webhook. (signée avec WEBHOOK_SECRET_HEADER)
  • X-Remnawave-Timestamp - L'horodatage de la charge utile du webhook.

Charge utile

La charge utile sera un objet JSON.

Exemple de charge utile de webhook
{
"scope": "service",
"event": "service.panel_started",
"timestamp": "2026-01-07T11:57:29.426Z",
"data": {
"panelVersion": "2.5.0"
}
}

Propriétés :

  • scope - La portée de la charge utile du webhook. (Depuis v2.5.0)

    • user - Événements utilisateur
    • user_hwid_devices - Événements appareils HWID utilisateur
    • node - Événements nœud
    • service - Événements service
    • crm - Événements facturation infrastructure
    • torrent_blocker - Événements bloqueur de torrents
    • errors - Événements erreurs
  • event - L'événement survenu.

  • timestamp - L'horodatage en format ISO 8601.

  • data - Les données associées à l'événement.

astuce

Le schéma de charge utile détaillé pour chaque portée est disponible dans la documentation OpenAPI.

👉 Consultez Model Link dans chaque section de portée ci-dessous.

Portée : user

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

Événements disponibles (propriété event) :

  • user.created - Utilisateur créé
  • user.modified - Utilisateur modifié
  • user.deleted - Utilisateur supprimé
  • user.revoked - Utilisateur révoqué
  • user.disabled - Utilisateur désactivé
  • user.enabled - Utilisateur activé
  • user.limited - Utilisateur limité
  • user.expired - Utilisateur expiré
  • user.traffic_reset - Trafic utilisateur réinitialisé
  • user.expires_in_72_hours - Utilisateur expire dans 72 heures (supprimé dans v.2.8.0, utilisez user.expiration à la place)
  • user.expires_in_48_hours - Utilisateur expire dans 48 heures (supprimé dans v.2.8.0, utilisez user.expiration à la place)
  • user.expires_in_24_hours - Utilisateur expire dans 24 heures (supprimé dans v.2.8.0, utilisez user.expiration à la place)
  • user.expired_24_hours_ago - Utilisateur expiré il y a 24 heures (supprimé dans v.2.8.0, utilisez user.expiration à la place)
  • user.first_connected - Utilisateur connecté pour la première fois
  • user.bandwidth_usage_threshold_reached - Seuil d'utilisation de bande passante atteint
  • user.not_connected - Utilisateur non connecté (actif uniquement quand NOT_CONNECTED_USERS_NOTIFICATIONS_ENABLED est true dans .env)
  • user.expiration - Notifications d'expiration d'utilisateur (Actif uniquement lorsque EXPIRATION_NOTIFICATIONS_ENABLED est vrai dans .env.)
import { TRemnawaveWebhookUserEvent, RemnawaveWebhookUserEvents } from '@remnawave/backend-contract'

Objet meta

La plupart des événements utilisateur ont meta: null. Ce champ est renseigné uniquement pour les événements de type notification, où il fournit le contexte ayant déclenché le webhook.

ChampTypePrésent pourDescription
notConnectedAfterHoursnumber | nulluser.not_connectedHeures de déconnexion de l'utilisateur. Correspond au seuil de NOT_CONNECTED_USERS_NOTIFICATIONS_AFTER_HOURS ayant déclenché l'événement.
expirationnumber | nulluser.expirationDécalage signé en heures par rapport à l'expiration de l'utilisateur (expireAt). Correspond à une valeur de EXPIRATION_NOTIFICATIONS.

Signe du champ expiration

La valeur expiration est signée et encode la direction par rapport au moment d'expiration :

  • Négatif — déclenché avant l'expiration : expire dans |N| heures (ex. -72 → expire dans 72 heures).
  • Positif — déclenché après l'expiration : expiré il y a N heures (ex. 24 → expiré il y a 24 heures).
remarque

Pour tous les événements autres que user.not_connected et user.expiration, meta est null. Lorsque meta est présent, seul le champ de l'événement actuel est défini — l'autre champ reste null.

Portée : user_hwid_devices

Événements disponibles :

  • user_hwid_devices.added - Appareil HWID utilisateur ajouté
  • user_hwid_devices.deleted - Appareil HWID utilisateur supprimé

Portée : node

Événements disponibles :

  • node.created - Nœud créé
  • node.modified - Nœud modifié
  • node.disabled - Nœud désactivé
  • node.enabled - Nœud activé
  • node.deleted - Nœud supprimé
  • node.connection_lost - Connexion nœud perdue
  • node.connection_restored - Connexion nœud restaurée
  • node.traffic_notify - Notification trafic nœud

Portée : service

Événements disponibles :

  • service.panel_started - Panel démarré
  • service.login_attempt_failed - Tentative de connexion échouée
  • service.login_attempt_success - Tentative de connexion réussie
  • service.subpage_config_changed - Configuration sous-page modifiée

Portée : crm

Événements disponibles :

  • crm.infra_billing_node_payment_in_7_days - Paiement nœud facturation infra dans 7 jours
  • crm.infra_billing_node_payment_in_48hrs - Paiement nœud dans 48 heures
  • crm.infra_billing_node_payment_in_24hrs - Paiement nœud dans 24 heures
  • crm.infra_billing_node_payment_due_today - Paiement nœud dû aujourd'hui
  • crm.infra_billing_node_payment_overdue_24hrs - Paiement nœud en retard 24 heures
  • crm.infra_billing_node_payment_overdue_48hrs - Paiement nœud en retard 48 heures
  • crm.infra_billing_node_payment_overdue_7_days - Paiement nœud en retard 7 jours

Portée : torrent_blocker

Depuis Remnawave Panel v2.7.0.

  • torrent_blocker.report - Rapport bloqueur de torrents

Portée : errors

  • errors.bandwidth_usage_threshold_reached_max_notifications - Seuil de bande passante atteint notifications maximales

Vérifier le webhook

Remnawave signera la charge utile du webhook avec WEBHOOK_SECRET_HEADER et l'enverra à WEBHOOK_URL.

Vérification du webhook
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']
}

Exemples pour différents langages

Python

Exemple de code Python
def validate_webhook(body, signature):
webhook_secret_panel = "your_secret_token"
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

Exemple de code Go
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, _ := ioutil.ReadAll(r.Body)
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)
}