Pular para o conteúdo principal

Recebendo webhooks

Visão geral

O Remnawave pode enviar webhooks para muitos eventos.

Configuração

Configuração .env

WEBHOOK_ENABLED=true
WEBHOOK_URL=https://your-server.com/webhook
WEBHOOK_SECRET_HEADER=your-secret-header
VariávelDescrição
WEBHOOK_ENABLEDHabilitar webhooks.
WEBHOOK_URLA URL para enviar o webhook. (deve começar com https:// ou http://). É possível especificar múltiplas URLs separadas por vírgulas (sem espaços)
WEBHOOK_SECRET_HEADEREste cabeçalho será usado para assinar o payload do webhook. (apenas aA-zZ, 0-9 são permitidos)

Cabeçalhos

O Remnawave enviará os seguintes cabeçalhos com o payload do webhook:

  • X-Remnawave-Signature - A assinatura do payload do webhook. (assinado com WEBHOOK_SECRET_HEADER)
  • X-Remnawave-Timestamp - O timestamp do payload do webhook.

Payload

O payload será um objeto JSON.

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

Propriedades:

  • scope - O escopo do payload do webhook. (Desde v2.5.0)

    • user - Eventos de usuário
    • user_hwid_devices - Eventos de dispositivos HWID do usuário
    • node - Eventos de nó
    • service - Eventos de serviço
    • crm - Eventos de faturamento de infraestrutura
    • torrent_blocker - Eventos de bloqueador de torrents
    • errors - Eventos de erros
  • event - O evento que ocorreu.

  • timestamp - O timestamp no formato ISO 8601.

  • data - Os dados associados ao evento.

dica

O esquema de payload detalhado para cada escopo está disponível na documentação OpenAPI.

👉 Consulte Model Link em cada seção de escopo abaixo.

Escopo: user

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

Eventos disponíveis (propriedade event):

  • user.created - Usuário criado
  • user.modified - Usuário modificado
  • user.deleted - Usuário deletado
  • user.revoked - Usuário revogado
  • user.disabled - Usuário desabilitado
  • user.enabled - Usuário habilitado
  • user.limited - Usuário limitado
  • user.expired - Usuário expirado
  • user.traffic_reset - Tráfego do usuário resetado
  • user.expires_in_72_hours - Usuário expira em 72 horas (removido na v.2.8.0, use user.expiration em vez disso)
  • user.expires_in_48_hours - Usuário expira em 48 horas (removido na v.2.8.0, use user.expiration em vez disso)
  • user.expires_in_24_hours - Usuário expira em 24 horas (removido na v.2.8.0, use user.expiration em vez disso)
  • user.expired_24_hours_ago - Usuário expirou há 24 horas (removido na v.2.8.0, use user.expiration em vez disso)
  • user.first_connected - Usuário conectado pela primeira vez
  • user.bandwidth_usage_threshold_reached - Limite de uso de largura de banda atingido
  • user.not_connected - Usuário não conectado (ativo apenas quando NOT_CONNECTED_USERS_NOTIFICATIONS_ENABLED é true no .env)
  • user.expiration - Notificações de expiração de usuário (Ativo somente quando EXPIRATION_NOTIFICATIONS_ENABLED é true em .env.)
import { TRemnawaveWebhookUserEvent, RemnawaveWebhookUserEvents } from '@remnawave/backend-contract'

Objeto meta

A maioria dos eventos de usuário tem meta: null. Este campo é preenchido apenas para eventos de notificação, fornecendo o contexto que acionou o webhook.

CampoTipoPresente paraDescrição
notConnectedAfterHoursnumber | nulluser.not_connectedHoras que o usuário ficou offline. Corresponde ao limite de NOT_CONNECTED_USERS_NOTIFICATIONS_AFTER_HOURS que disparou o evento.
expirationnumber | nulluser.expirationDeslocamento em horas com sinal em relação à expiração do usuário (expireAt). Corresponde a um valor de EXPIRATION_NOTIFICATIONS.

Sinal do campo expiration

O valor de expiration tem sinal e codifica a direção em relação ao momento de expiração:

  • Negativo — disparado antes da expiração: expira em |N| horas (ex.: -72 → expira em 72 horas).
  • Positivo — disparado após a expiração: expirou há N horas (ex.: 24 → expirou há 24 horas).
observação

Para todos os eventos exceto user.not_connected e user.expiration, meta é null. Quando meta está presente, apenas o campo do evento atual é definido — o outro campo permanece null.

Escopo: user_hwid_devices

Eventos disponíveis:

  • user_hwid_devices.added - Dispositivo HWID do usuário adicionado
  • user_hwid_devices.deleted - Dispositivo HWID do usuário deletado

Escopo: node

Eventos disponíveis:

  • node.created - Nó criado
  • node.modified - Nó modificado
  • node.disabled - Nó desabilitado
  • node.enabled - Nó habilitado
  • node.deleted - Nó deletado
  • node.connection_lost - Conexão do nó perdida
  • node.connection_restored - Conexão do nó restaurada
  • node.traffic_notify - Notificação de tráfego do nó

Escopo: service

Eventos disponíveis:

  • service.panel_started - Painel iniciado
  • service.login_attempt_failed - Tentativa de login falhou
  • service.login_attempt_success - Tentativa de login bem-sucedida
  • service.subpage_config_changed - Configuração de subpágina alterada

Escopo: crm

Eventos disponíveis:

  • crm.infra_billing_node_payment_in_7_days - Pagamento de nó de faturamento de infra em 7 dias
  • crm.infra_billing_node_payment_in_48hrs - Pagamento de nó em 48 horas
  • crm.infra_billing_node_payment_in_24hrs - Pagamento de nó em 24 horas
  • crm.infra_billing_node_payment_due_today - Pagamento de nó vence hoje
  • crm.infra_billing_node_payment_overdue_24hrs - Pagamento de nó atrasado 24 horas
  • crm.infra_billing_node_payment_overdue_48hrs - Pagamento de nó atrasado 48 horas
  • crm.infra_billing_node_payment_overdue_7_days - Pagamento de nó atrasado 7 dias

Escopo: torrent_blocker

A partir do Remnawave Panel v2.7.0.

  • torrent_blocker.report - Relatório do bloqueador de torrents

Escopo: errors

  • errors.bandwidth_usage_threshold_reached_max_notifications - Limiar de largura de banda atingiu notificações máximas

Verificar webhook

O Remnawave assinará o payload do webhook com WEBHOOK_SECRET_HEADER e o enviará para WEBHOOK_URL.

Verificação 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']
}

Exemplos para diferentes linguagens

Python

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