RWI 事件与 Webhook
RWI(Real-time WebSocket Interface)是 RustPBX 的实时事件总线。除了 WebSocket 实时推送,还支持 HTTP Webhook 将事件转发到外部系统。
1. RWI 架构
RustPBX 内部事件
│
▼
RWI Gateway
│
├── WebSocket 连接(实时控制)
│ ws://host:8080/rwi/ws
│ Sub-protocol: rwi-v1
│
└── HTTP Webhook(事件转发)
POST https://your-server/webhook
2. Webhook 配置
在 config.toml 中:
[rwi_webhook]
url = "https://your-server.example.com/api/rwi/events"
timeout_ms = 5000
[rwi_webhook.headers]
Authorization = "Bearer my-webhook-secret"
X-Source = "rustpbx"
# 可选:事件类型过滤(留空或不设则转发全部)
events = [
"call_hangup",
"call_answered",
"queue_joined",
"queue_agent_connected",
"agent_state_changed",
"dtmf",
"ivr_step_trace",
"record_stopped"
]
2.1 配置参数
| 参数 | 说明 | 默认值 |
url | Webhook 接收端点(必需) | - |
timeout_ms | HTTP 请求超时 | 5000 |
headers | 自定义 HTTP Headers | 无 |
events | 事件类型白名单(空=全部) | 全部 |
retries | 推送失败后的重试次数(传输错误、5xx 或 429)。指数退避——200ms 起步、逐次翻倍。重试期间幂等键保持一致,接收端可安全去重 | 0(单次尝试) |
track_queue_latency | 在 rwi_event_queue_latency_seconds 直方图中记录网关→处理器的排队延迟 | false |
2.2 投递运行时(0.5+)
Webhook 运行在与 SIP 热路径解耦的专用运行时上:
- 可配置的 worker 线程(
rwi_webhook_worker_threads)从有界队列取事件,慢速接收端不会拖垮呼叫处理。
- 失败投递(5xx/429/传输错误)按退避策略重试至多
retries 次;每次尝试携带相同的幂等键和逐字节一致的请求体。
- 每次请求都会记录 URL、状态码和延迟,用于结构化日志,并可选排队延迟指标。
[rwi_webhook] 段支持热更新——修改 URL、headers 或事件过滤无需重启即可生效。
3. Webhook Payload 格式
每次 POST 请求体:
{
"rwi": "1.0",
"sequence": 12345,
"timestamp": "2026-05-26T10:30:00Z",
"call_id": "abc123@10.0.0.1",
"event_type": "call_hangup",
"event": {
"call_id": "abc123@10.0.0.1",
"reason": "normal",
"duration": 120,
"sip_code": 200
}
}
| 字段 | 说明 |
rwi | 协议版本号 |
sequence | 单调递增序列号 |
timestamp | ISO 8601 时间戳 |
call_id | 关联的 SIP Call-ID |
event_type | 事件类型(snake_case 字符串) |
event | 事件详细数据(因类型而异) |
4. 去重机制
Webhook 内置去重:
- 环形缓存 4096 条
(call_id, sequence) 记录
- 相同
(call_id, sequence) 的事件不会重复发送
- 适用于同一事件从多个 call owner 转发的场景
5. 完整事件类型列表
5.1 呼叫生命周期
| event_type | 说明 |
call_incoming | 来电到达 |
call_ringing | 振铃 |
call_early_media | 早期媒体 |
call_answered | 应答 |
call_bridged | 桥接成功 |
call_unbridged | 桥接断开 |
call_hangup | 挂断 |
call_no_answer | 无应答 |
call_busy | 忙碌 |
call_transferred | 转接完成 |
call_transfer_accepted | 转接接受 |
call_transfer_failed | 转接失败 |
call_ownership_changed | 通话归属变更 |
call_metadata_updated | 通话元数据更新 |
5.2 媒体
| event_type | 说明 |
media_hold_started | 保持开始 |
media_hold_stopped | 保持结束 |
media_play_started | 播放开始 |
media_play_finished | 播放完成 |
media_stream_started | 流媒体开始 |
media_stream_stopped | 流媒体结束 |
dtmf | DTMF 按键 |
dtmf_collected | DTMF 收集完成 |
dtmf_collection_timeout | DTMF 收集超时 |
5.3 录音
| event_type | 说明 |
record_started | 录音开始 |
record_paused | 录音暂停 |
record_resumed | 录音恢复 |
record_stopped | 录音停止 |
record_failed | 录音失败 |
recording_metadata_available | 录音元数据就绪 |
5.4 队列/ACD
| event_type | 说明 |
queue_joined | 加入队列 |
queue_position_changed | 排队位置变化 |
queue_candidates_found | 找到候选坐席 |
queue_agent_offered | 向坐席分配 |
queue_agent_ringing | 坐席振铃 |
queue_agent_no_answer | 坐席未接 |
queue_agent_rejected | 坐席拒绝 |
queue_agent_connected | 坐席接通 |
queue_left | 离开队列 |
queue_wait_timeout | 等待超时 |
queue_overflowed | 队列溢出 |
queue_voicemail_redirected | 转语音信箱 |
queue_fallback_executed | 执行回退 |
queue_alert | 队列告警 |
5.5 坐席
| event_type | 说明 |
agent_state_changed | 坐席状态变化 |
5.6 分机
| event_type | 说明 |
dn_state_changed | 分机状态变化 |
dn_registered | 分机注册 |
dn_unregistered | 分机注销 |
5.7 IVR
| event_type | 说明 |
ivr_node_entered | 进入 IVR 节点 |
ivr_node_exited | 离开 IVR 节点 |
ivr_flow_transitioned | IVR 流程转换 |
ivr_flow_completed | IVR 流程完成 |
ivr_step_trace | Step IVR 跟踪 |
5.8 会议
| event_type | 说明 |
conference_created | 会议创建 |
conference_member_joined | 成员加入 |
conference_member_left | 成员离开 |
conference_member_muted | 成员静音 |
conference_member_unmuted | 成员取消静音 |
conference_destroyed | 会议销毁 |
conference_error | 会议错误 |
conference_consult_dialing | 咨询拨号中 |
conference_consult_connected | 咨询接通 |
conference_merge_requested | 合并请求 |
conference_merged | 合并完成 |
conference_merge_failed | 合并失败 |
conference_seat_replace_started | 席位替换开始 |
conference_seat_replace_succeeded | 席位替换成功 |
conference_seat_replace_failed | 席位替换失败 |
5.9 监控(Supervisor)
| event_type | 说明 |
supervisor_listen_started | 监听开始 |
supervisor_whisper_started | 密语开始 |
supervisor_barge_started | 强插开始 |
supervisor_takeover_started | 接管开始 |
supervisor_mode_stopped | 监控模式停止 |
5.10 并行外呼
| event_type | 说明 |
parallel_originate_started | 并行外呼开始 |
parallel_originate_leg_ringing | 外呼线路振铃 |
parallel_originate_winner | 获胜线路 |
parallel_originate_leg_cancelled | 线路取消 |
parallel_originate_completed | 并行外呼完成 |
parallel_originate_failed | 并行外呼失败 |
5.11 SIP 信令
| event_type | 说明 |
sip_message_received | 收到 SIP MESSAGE |
sip_notify_received | 收到 SIP NOTIFY |
5.12 会话恢复
| event_type | 说明 |
session_resumed | WebSocket 重连后恢复会话 |
6. Webhook 接收端示例
6.1 Python Flask
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route("/api/rwi/events", methods=["POST"])
def handle_rwi_event():
payload = request.json
event_type = payload.get("event_type")
call_id = payload.get("call_id")
event = payload.get("event", {})
if event_type == "call_hangup":
print(f"Call {call_id} ended, duration={event.get('duration')}s")
elif event_type == "queue_agent_connected":
print(f"Agent {event.get('agent_id')} connected to call {call_id}")
elif event_type == "agent_state_changed":
print(f"Agent {event.get('agent_id')} -> {event.get('state')}")
return jsonify({"status": "ok"})
6.2 Node.js Express
app.post("/api/rwi/events", express.json(), (req, res) => {
const { event_type, call_id, event, sequence } = req.body;
console.log(`[${sequence}] ${event_type} on ${call_id}`);
res.json({ status: "ok" });
});
7. WebSocket 实时连接(补充)
除了 Webhook,也可通过 WebSocket 实时订阅事件:
const ws = new WebSocket("ws://rustpbx:8080/rwi/ws", "rwi-v1");
ws.onmessage = (e) => {
const data = JSON.parse(e.data);
console.log(data.event_type, data.call_id);
};
7.1 会话恢复
WebSocket 断连后重连,可通过 last_sequence 参数恢复:
- Gateway 缓存最近 1000 条事件
- 60 秒保留期
- 单调递增 sequence 号
8. 典型应用场景
| 场景 | 订阅事件 | 说明 |
| CDR 实时推送 | call_hangup | 实时获取通话结束事件 |
| 呼叫中心大屏 | queue_*, agent_state_changed | 实时更新排队和坐席状态 |
| IVR 追踪 | ivr_step_trace | 记录每一步 IVR 决策 |
| 录音完成通知 | record_stopped + recording_metadata_available | 录音完成后立即处理 |
| 告警系统 | queue_alert, queue_overflowed | 队列异常告警 |
| CRM 集成 | call_answered | 弹屏显示客户信息 |