Saltar al contenido principal

Recibir webhooks

Descripción general

Remnawave puede enviar webhooks para muchos eventos.

Configuración

Configuración .env

WEBHOOK_ENABLED=true
WEBHOOK_URL=https://your-server.com/webhook
WEBHOOK_SECRET_HEADER=your-secret-header
VariableDescripción
WEBHOOK_ENABLEDHabilitar webhooks.
WEBHOOK_URLLa URL a la que se enviará el webhook. (debe comenzar con https:// o http://). Es posible especificar múltiples URLs separadas por comas (sin espacios)
WEBHOOK_SECRET_HEADEREste encabezado se utilizará para firmar la carga del webhook. (solo se permiten aA-zZ, 0-9)

Encabezados

Remnawave enviará los siguientes encabezados con la carga del webhook:

  • X-Remnawave-Signature - La firma de la carga del webhook. (firmada con WEBHOOK_SECRET_HEADER)
  • X-Remnawave-Timestamp - La marca de tiempo de la carga del webhook.

Carga

La carga será un objeto JSON.

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

Propiedades:

  • scope - El alcance de la carga del webhook. (Desde v2.5.0)

    • user - Eventos de usuario
    • user_hwid_devices - Eventos de dispositivos HWID de usuario
    • node - Eventos de nodo
    • service - Eventos de servicio
    • crm - Eventos de facturación de infraestructura
    • torrent_blocker - Eventos de bloqueador de torrents
    • errors - Eventos de errores
  • event - El evento que ocurrió.

  • timestamp - La marca de tiempo en formato ISO 8601.

  • data - Los datos asociados con el evento.

tip

El esquema de carga detallado para cada alcance está disponible en la documentación OpenAPI.

👉 Consulte Model Link en cada sección de alcance a continuación.

Alcance: user

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

Eventos disponibles (propiedad event):

  • user.created - Usuario creado
  • user.modified - Usuario modificado
  • user.deleted - Usuario eliminado
  • user.revoked - Usuario revocado
  • user.disabled - Usuario deshabilitado
  • user.enabled - Usuario habilitado
  • user.limited - Usuario limitado
  • user.expired - Usuario expirado
  • user.traffic_reset - Tráfico de usuario restablecido
  • user.expires_in_72_hours - Usuario expira en 72 horas (eliminado en v.2.8.0, usa user.expiration en su lugar)
  • user.expires_in_48_hours - Usuario expira en 48 horas (eliminado en v.2.8.0, usa user.expiration en su lugar)
  • user.expires_in_24_hours - Usuario expira en 24 horas (eliminado en v.2.8.0, usa user.expiration en su lugar)
  • user.expired_24_hours_ago - Usuario expiró hace 24 horas (eliminado en v.2.8.0, usa user.expiration en su lugar)
  • user.first_connected - Usuario conectado por primera vez
  • user.bandwidth_usage_threshold_reached - Umbral de uso de ancho de banda alcanzado
  • user.not_connected - Usuario no conectado (activo solo cuando NOT_CONNECTED_USERS_NOTIFICATIONS_ENABLED es true en .env)
  • user.expiration - Notificaciones de expiración de usuario (Activo solo cuando EXPIRATION_NOTIFICATIONS_ENABLED es true en .env.)

Tipos de Remnawave Typescript SDK:

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

Objeto meta

La mayoría de los eventos de usuario tienen meta: null. Este campo se rellena solo para eventos de tipo notificación, donde proporciona el contexto que desencadenó el webhook.

CampoTipoPresente paraDescripción
notConnectedAfterHoursnumber | nulluser.not_connectedHoras que el usuario ha estado desconectado. Coincide con el umbral de NOT_CONNECTED_USERS_NOTIFICATIONS_AFTER_HOURS que disparó el evento.
expirationnumber | nulluser.expirationDesfase en horas con signo relativo al vencimiento del usuario (expireAt). Coincide con un valor de EXPIRATION_NOTIFICATIONS.

Signo del campo expiration

El valor de expiration tiene signo y codifica la dirección relativa al momento de vencimiento:

  • Negativo — disparado antes del vencimiento: vence en |N| horas (p. ej., -72 → vence en 72 horas).
  • Positivo — disparado después del vencimiento: venció hace N horas (p. ej., 24 → venció hace 24 horas).
nota

Para todos los eventos excepto user.not_connected y user.expiration, meta es null. Cuando meta está presente, solo se establece el campo para el evento actual; el otro campo permanece null.

Alcance: user_hwid_devices

Eventos disponibles:

  • user_hwid_devices.added - Dispositivo HWID de usuario agregado
  • user_hwid_devices.deleted - Dispositivo HWID de usuario eliminado

Alcance: node

Eventos disponibles:

  • node.created - Nodo creado
  • node.modified - Nodo modificado
  • node.disabled - Nodo deshabilitado
  • node.enabled - Nodo habilitado
  • node.deleted - Nodo eliminado
  • node.connection_lost - Conexión de nodo perdida
  • node.connection_restored - Conexión de nodo restaurada
  • node.traffic_notify - Notificación de tráfico de nodo

Alcance: service

Eventos disponibles:

  • service.panel_started - Panel iniciado
  • service.login_attempt_failed - Intento de inicio de sesión fallido
  • service.login_attempt_success - Intento de inicio de sesión exitoso
  • service.subpage_config_changed - Configuración de subpágina cambiada

Alcance: crm

Eventos disponibles:

  • crm.infra_billing_node_payment_in_7_days - Pago de nodo de facturación de infraestructura en 7 días
  • crm.infra_billing_node_payment_in_48hrs - Pago de nodo en 48 horas
  • crm.infra_billing_node_payment_in_24hrs - Pago de nodo en 24 horas
  • crm.infra_billing_node_payment_due_today - Pago de nodo vence hoy
  • crm.infra_billing_node_payment_overdue_24hrs - Pago de nodo vencido 24 horas
  • crm.infra_billing_node_payment_overdue_48hrs - Pago de nodo vencido 48 horas
  • crm.infra_billing_node_payment_overdue_7_days - Pago de nodo vencido 7 días

Alcance: torrent_blocker

Desde Remnawave Panel v2.7.0.

  • torrent_blocker.report - Informe del bloqueador de torrents

Alcance: errors

  • errors.bandwidth_usage_threshold_reached_max_notifications - Umbral de uso de ancho de banda alcanzó máximo de notificaciones

Verificar webhook

Remnawave firmará la carga del webhook con el WEBHOOK_SECRET_HEADER y la enviará a la WEBHOOK_URL.

Verificación de 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']
}

Ejemplos para diferentes lenguajes

Python

Código de muestra 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

Código de muestra 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)
}