Webhook
Webhook 让面板主动把发生的事情告知外部系统:
创建了用户、节点宕机、流量耗尽。面板会向你
的地址发送带有 JSON 请求体的 POST 请求,因此接收方可以是脚本、CRM、
监控系统或聊天机器人。
启用
Webhook 默认关闭。设置位于面板的 .env 中:
| 变量 | 默认值 | 说明 |
|---|---|---|
WEBHOOK_ENABLED | false | 启用 Webhook 发送 |
WEBHOOK_URL | — | 接收方地址;多个地址用逗号分隔 |
WEBHOOK_SECRET_HEADER | — | payload 签名密钥,正好 64 个 [a-zA-Z0-9] 字符 |
WEBHOOK_ENABLED=true
WEBHOOK_URL=https://example.com/xlada/webhook
WEBHOOK_SECRET_HEADER=vsmu67Kmg6R8FjIOF1WUY8LWBHie4scdEqrfsKmyf4IAf8dY3nFS0wwYHkhh6ZvQ
当 WEBHOOK_ENABLED=true 时,两个变量都是必填的。WEBHOOK_URL 必须
以 http:// 或 https:// 开头,否则容器无法启动。签名
密钥只保存在 .env 中——接收方用它校验请求的
真实性。
发送哪些事件由容器内的
/var/lib/remnawave/configs/notifications/notifications-config.yml 文件决定。默认情况下,
镜像中两个通道的所有事件都已启用。
文件中没有的事件视为已关闭。为了让修改
得以保留,请把你的文件挂载到
/var/lib/remnawave/configs/notifications 目录之上。
events:
user.created:
telegram: true
webhook: true
user.expired:
telegram: false
webhook: true
# 仅用于该事件的附加地址
additionalWebhookUrls:
- https://example.com/xlada/critical
payload 签名
每个请求都会带上服务请求头:
| 请求头 | 值 |
|---|---|
X-XLADA-Signature | 请求体的 HMAC-SHA256,十六进制 |
X-XLADA-Timestamp | 事件的 ISO 8601 时间 |
Content-Type | application/json |
User-Agent | XLADA |
签名按 HMAC-SHA256(secret, request_body) 计算,并与
请求头比较。必须比较原始请求体,而不是解析后的 JSON。
import { createHmac, timingSafeEqual } from 'node:crypto';
function isValidSignature(rawBody, secret, signature) {
const expected = createHmac('sha256', secret).update(rawBody).digest('hex');
const a = Buffer.from(expected);
const b = Buffer.from(signature ?? '');
return a.length === b.length && timingSafeEqual(a, b);
}
payload 格式
请求体中始终有四个字段:scope、event、timestamp 和 data。
用户事件还额外带有 meta。
{
"scope": "service",
"event": "service.panel_started",
"timestamp": "2026-01-07T11:57:29.426Z",
"data": {
"panelVersion": "1.1.1"
}
}
meta 字段只在提醒类事件中填充:
| 字段 | 事件 | 值 |
|---|---|---|
notConnectedAfterHours | user.not_connected | 用户已有多少小时未连接 |
expiration | user.expiration | 相对于 expireAt 的小时数:负值表示到期前,正值表示到期后 |
其他所有事件的 meta 都为 null。
事件列表
范围(scope) | 事件 |
|---|---|
user | user.created, user.modified, user.deleted, user.revoked, user.disabled, user.enabled, user.limited, user.expired, user.traffic_reset, user.first_connected, user.bandwidth_usage_threshold_reached, user.not_connected, user.expiration |
user_hwid_devices | user_hwid_devices.added, user_hwid_devices.deleted |
node | node.created, node.modified, node.disabled, node.enabled, node.deleted, node.connection_lost, node.connection_restored, node.traffic_notify |
service | service.panel_started, service.login_attempt_failed, service.login_attempt_success, service.subpage_config_changed, service.api_token_created, service.api_token_deleted |
errors | errors.bandwidth_usage_threshold_reached_max_notifications |
crm | crm.infra_billing_node_payment_in_7_days, crm.infra_billing_node_payment_in_48hrs, crm.infra_billing_node_payment_in_24hrs, crm.infra_billing_node_payment_due_today, crm.infra_billing_node_payment_overdue_24hrs, crm.infra_billing_node_payment_overdue_48hrs, crm.infra_billing_node_payment_overdue_7_days |
torrent_blocker | torrent_blocker.report |
事件 user.not_connected 和 user.expiration 仅在启用
NOT_CONNECTED_USERS_NOTIFICATIONS_ENABLED 和 EXPIRATION_NOTIFICATIONS_ENABLED 时到达。
事件 user_hwid_devices.* 仅在启用设备限制时出现,
并且只发送到 Webhook——不会重复发送到 Telegram。
重试
投递通过队列进行:每个地址创建一个独立任务, 并发发送上限为 100。如果接收方未响应,请求会 重试 3 次,间隔 5 秒——总共最多四次尝试。之后 错误会写入面板日志,任务则留在队列中并受限于 有限的保留时间。
在 notifications-config.yml 中可以为具体事件指定附加地址
(additionalWebhookUrls)。它们会追加到 WEBHOOK_URL 中的地址,
而不是替换它们。
请立即向 Webhook 返回 2xx,把处理放到后台。在接收方
响应之前,面板会保持连接,而缓慢的响应会堆积队列。
接下来
- 通知 —— Telegram、流量阈值和提醒。
- 环境变量 —— 面板的所有开关。
- API Specification —— 每个事件的数据结构。
- 面板安全 —— 如何保护 Webhook 接收端。