RWI Events 开发者参考

August 6, 2026 · View on GitHub

代码来源:src/rwi/proto.rs | 协议版本:1.0


1. 概述

RustPBX 通过 RWI(Real-time WebSocket Interface)实时推送呼叫、IVR、录音、队列、坐席、分机等事件。开发者可以通过以下两种方式接收事件:

接收方式协议适用场景
WebSocket 订阅ws(s)://<host>/rwi/v1实时双向交互(机器人、软电话、监控面板)
Webhook 回调HTTP POST异步通知(CRM、录音系统、数据分析平台)

事件分发模型

分发方式接收者含义
call_owner拥有该 call_id 的 WS 会话单呼叫精细控制
fan_out订阅了对应 context 的所有 WS 会话来电通知、IVR 事件
broadcast所有在线 WS 会话全局事件(坐席状态、分机注册等)
webhook配置的 HTTP 端点所有事件均转发(可配置过滤)

2. 连接与认证

WebSocket

GET /rwi/v1 HTTP/1.1
Upgrade: websocket
Authorization: Bearer <token>

或 URL 参数:GET /rwi/v1?token=<token>

Webhook 配置(rustpbx.toml)

[rwi_webhook]
url = "https://myapp.example.com/rwi-events"
timeout_ms = 5000
headers = { Authorization = "Bearer your-token" }
# 空 = 全部事件(推荐)。如需白名单过滤,请使用有效的事件类型。
# 注意:坐席状态是 "agent_state_changed"(旧的 "dn_state_changed" 已废弃移除);
# 录音数据(下载 URL、文件大小)通过 "recording_metadata_available" 和
# "record_end" 投递 —— 仅 "record_stopped" 不带录音 URL。
# 白名单示例:
# events = ["call_hangup", "record_stopped", "recording_metadata_available", "record_end", "agent_state_changed"]
events = []

3. 信封格式

WebSocket 事件

{
  /* 事件数据字段直接扁平化到顶层,无额外包裹 */
}

示例:

{
  "call_id": "call-abc123",
  "caller_name": "330909",
  "callee_name": "9242000001",
  "direction": "inbound"
}

WebSocket 事件以事件字段直接作为 JSON 顶层键值,不含 "rwi" 或事件类型名称包裹。客户端通过连接时协商的订阅规则识别事件类型。

Webhook 信封

{
  "rwi": "1.0",
  "sequence": 42,
  "timestamp": 1716212345,
  "call_id": "call-abc123",
  "event_type": "call_ringing",
  "event": {
    /* 与 WS 事件内容完全一致(无 event_type 包裹) */
  }
}
字段类型说明
rwistring协议版本 "1.0"
sequenceu64单调递增事件序号,用于去重和断线重连
timestampu64Unix 时间戳(秒)
call_idstring呼叫标识(广播事件为空字符串)
event_typestringsnake_case 事件类型名
eventobject事件载荷,字段直接扁平化(无 event_type 包裹)

4. 扁平化上下文(EventCallContext)

所有 call-scoped 事件通过 #[serde(flatten)] 将以下字段直接扁平化到事件 JSON 中(不产生嵌套对象)。None 值自动省略不出现在 JSON 里。

字段类型说明
callerOption<String>主叫 SIP URI
calleeOption<String>被叫 SIP URI
caller_nameOption<String>主叫号码(标准化纯号码)
callee_nameOption<String>被叫号码 / DNIS
directionOption<String>inbound / outbound / internal
trunkOption<String>SIP 中继名称
app_idOption<String>IVR 应用 ID
routing_targetOption<String>当前路由目标
agent_idOption<String>坐席 ID
agent_nameOption<String>坐席名称

说明

  • ani vs callerani 是纯号码(用于业务匹配),caller 是完整 SIP URI
  • dnis vs callee:同上
  • 上下文由 CallMetaStore 在 gateway 分发时自动注入,事件生产者无需手动填充

字段重复说明

部分事件(如 RecordStoppedIvrNodeEntered)自身也携带 ani/dnis 等字段。当事件自身字段值为 None 时,enrich() 会自动从上下文补充。Webhook 消费者最终收到的是合并后的完整值。


5. 订阅与断线重连

订阅 context

{
  "rwi": "1.0",
  "action_id": "sub-001",
  "action": "session.subscribe",
  "params": { "contexts": ["queue:support", "agent:*"] }
}
Context 格式说明
queue:<queue_id>订阅指定队列事件
agent:<agent_id>订阅指定坐席事件
*通配,接收所有广播事件

断线重连(Session Resume)

{
  "rwi": "1.0",
  "action_id": "resume-001",
  "action": "session.resume",
  "params": { "last_sequence": 42 }
}

服务端缓存最近 1000 条事件(保留 60 秒),重连后自动回放 last_sequence 之后的事件。

Webhook 去重

Webhook 使用 (call_id, sequence) 元组去重,环形缓冲区容量 4096 条。重复事件自动丢弃。


6. 完整事件字典

下方各表 +ctx 表示该事件携带扁平化上下文字段。 ? 表示 Option<T> 字段,值为 null 时省略。

6.1 呼叫生命周期

call_incoming

分发:fan_out_to_context

新呼叫进入系统,是任何呼叫流程的第一个事件。

字段类型说明
call_idString呼叫唯一标识
contextString拨号计划 context
callerString主叫 SIP URI
calleeString被叫 SIP URI
dial_directionStringinbound / outbound / internal
trunkOption<String>SIP 中继名
sip_headersMap<String, String>白名单 SIP 头
root_call_idOption<String>根呼叫 ID(转接中不变)
caller_nameOption<String>主叫号码
callee_nameOption<String>被叫号码 / DNIS
called_phoneOption<String>实际被叫号码(外呼场景)
app_idOption<String>IVR 应用 ID
routing_targetOption<String>路由目标
uuidOption<String>全局 UUID(关联录音)
routing_pathOption<Vec<String>>路由路径序列

注意call_incoming 使用 dial_direction,其他事件的上下文使用 direction

{
  "rwi": "1.0",
  "call_incoming": {
    "call_id": "call-abc",
    "context": "inbound",
    "caller": "sip:13800138000@pbx.local",
    "callee": "sip:4000@pbx.local",
    "dial_direction": "inbound",
    "trunk": "trunk_sip",
    "sip_headers": { "X-Tenant": "corp_a" },
    "root_call_id": "call-root-42",
    "caller_name": "13800138000",
    "callee_name": "4000",
    "called_phone": null,
    "app_id": "ivr_sales",
    "routing_target": "queue:support",
    "uuid": "uuid-abc-123",
    "routing_path": ["menu:root", "queue:level1"]
  }
}

call_ringing / call_early_media / call_answered / call_unbridged / call_no_answer / call_busy

分发:call_owner

字段类型说明
call_idString呼叫标识
+ctx扁平化上下文
{
  "rwi": "1.0",
  "call_ringing": {
    "call_id": "call-abc",
    "caller": "sip:13800138000@pbx.local",
    "callee": "sip:4000@pbx.local",
    "caller_name": "13800138000",
    "callee_name": "4000",
    "direction": "inbound"
  }
}

call_bridged

分发:call_owner(两条 leg 均收到)

字段类型说明
leg_aStringA 腿 call_id
leg_bStringB 腿 call_id

call_hangup

分发:call_owner

字段类型说明
call_idString呼叫标识
reasonOption<String>挂机原因(见下表)
sip_statusOption<u16>SIP 响应码
+ctx扁平化上下文

reason 枚举值

说明
caller主叫挂机
callee被叫挂机
referREFER 转接挂机
system系统挂机
autohangup自动挂机(超时)
noAnswer无应答(408/480/487)
rejected拒接/忙(486/600/603)
canceled取消(487)
failed通用失败(其他 4xx)
serverUnavailable服务不可用(5xx)
rtpTimeoutRTP 超时
{
  "rwi": "1.0",
  "call_hangup": {
    "call_id": "call-abc",
    "reason": "caller",
    "sip_status": null,
    "caller": "sip:13800138000@pbx.local",
    "callee": "sip:4000@pbx.local",
    "caller_name": "13800138000",
    "callee_name": "4000",
    "direction": "inbound"
  }
}

6.2 转接事件

call_transferred / call_transfer_accepted

分发:call_owner

字段类型说明
call_idString呼叫标识
transfer_targetOption<String>原始转接目标字符串(如 queue:queue-name?target=skillgroup:tech-support_G)。在 SIP REFER Replaces 接管等场景下为 None
+ctx扁平化上下文

call_transfer_failed

分发:call_owner

字段类型说明
call_idString呼叫标识
sip_statusOption<u16>SIP 状态码
reasonOption<String>失败原因
transfer_targetOption<String>原始转接目标字符串(同上)
+ctx扁平化上下文

6.3 媒体事件

media_hold_started / media_hold_stopped / media_stream_started / media_stream_stopped

分发:call_owner

字段类型说明
call_idString呼叫标识
+ctx扁平化上下文

media_ringback_passthrough_started / media_ringback_passthrough_stopped

分发:call_owner

字段类型说明
sourceString源 leg call_id
targetString目标 leg call_id

media_play_started / media_play_finished

字段类型说明
call_idString呼叫标识
leg_idOption<String>目标 leg
track_idString播放 track ID
interruptedboolmedia_play_finished 专用:是否被 DTMF 中断
+ctx扁平化上下文

dtmf

分发:fan_out_to_context

字段类型说明
call_idString呼叫标识
digitStringDTMF 按键(0-9*#
leg_idOption<String>产生 DTMF 的 leg
extraOption<Object>附加数据(扩展字段,默认 null
+ctx扁平化上下文

dtmf_collected / dtmf_collection_timeout

分发:call_owner

字段类型说明
call_idString呼叫标识
leg_idStringDTMF 来源 leg
digitsStringdtmf_collected 专用:收集到的按键串
+ctx扁平化上下文

6.4 录音事件

record_started / record_paused / record_resumed / record_failed

分发:call_owner

触发方式:通过 RecordStart / RecordPause / RecordResume / RecordStop RWI 命令触发,非自动。录音不会在通话接通后自动开始。

字段类型说明
call_idString呼叫标识
errorStringrecord_failed 专用:错误信息
+ctx扁平化上下文

record_stopped(增强版)

分发:call_owner

触发方式:通过 RecordStop RWI 命令触发,非自动

字段类型说明
call_idString呼叫标识
duration_secsOption<u64>录音时长(秒)
filenameOption<String>录音文件名
unique_idOption<String>录音 UUID
file_sizeOption<u64>文件大小(字节)
download_urlOption<String>下载地址
caller_nameOption<String>主叫号码
callee_nameOption<String>被叫号码
called_phoneOption<String>实际被叫号码
call_typeOption<String>inbound/outbound/internal/consult
agent_idOption<String>坐席 ID
agent_nameOption<String>坐席名称
call_start_timeOption<String>通话开始时间(ISO 8601)
call_end_timeOption<String>通话结束时间
upload_timeOption<String>上传完成时间
switch_flagOption<String>站点标识(如 ksbj
root_call_idOption<String>根呼叫 ID

注意:record_stopped 不携带扁平化上下文,但自身已包含 ani/dnis 等字段,enrich() 会从上下文补充 None 字段。

{
  "rwi": "1.0",
  "record_stopped": {
    "call_id": "call-abc",
    "duration_secs": 51,
    "filename": "uuid_2026-05-14_08-11-49.mp3",
    "unique_id": "uuid-abc-123",
    "file_size": 149517,
    "download_url": "https://storage.example.com/rec.mp3",
    "caller_name": "330909",
    "callee_name": "9242000001",
    "called_phone": "018659727661",
    "call_type": "outbound",
    "agent_id": "451447",
    "agent_name": "luoxiaofeng90_v",
    "call_start_time": "2026-05-14T08:11:35Z",
    "call_end_time": "2026-05-14T08:12:26Z",
    "upload_time": "2026-05-14T16:14:46Z",
    "switch_flag": "ks",
    "root_call_id": "call-root-42"
  }
}

recording_metadata_available

分发:call_owner

录音文件上传完成后触发,包含完整元数据。

字段类型说明
call_idString呼叫标识
metadataRecordingMetadata录音元数据(见下表)

RecordingMetadata 字段

字段类型说明
filenameString录音文件名(必填)
unique_idString录音 UUID(必填)
file_sizeu64文件大小字节(必填)
download_urlOption<String>下载地址
caller_nameOption<String>主叫号码
callee_nameOption<String>被叫号码
called_phoneOption<String>实际被叫号码
call_typeString呼叫类型(必填)
agent_idOption<String>坐席 ID
agent_nameOption<String>坐席名称
call_start_timeOption<String>通话开始时间
call_end_timeOption<String>通话结束时间
upload_timeOption<String>上传完成时间
switch_flagOption<String>站点标识
process_flagOption<String>处理进程标识(如 ks_22_normal
root_call_idOption<String>根呼叫 ID
{
  "rwi": "1.0",
  "recording_metadata_available": {
    "call_id": "call-abc",
    "metadata": {
      "filename": "uuid_2026-05-14.mp3",
      "unique_id": "uuid-abc-123",
      "file_size": 149517,
      "download_url": "https://storage.example.com/rec.mp3",
      "caller_name": "330909",
      "callee_name": "9242000001",
      "called_phone": null,
      "call_type": "inbound",
      "agent_id": "451447",
      "agent_name": "luoxiaofeng90_v",
      "call_start_time": "2026-05-14T08:11:35Z",
      "call_end_time": "2026-05-14T08:12:26Z",
      "upload_time": "2026-05-14T16:14:46Z",
      "switch_flag": "ks",
      "process_flag": "ks_22_normal",
      "root_call_id": "call-root-42"
    }
  }
}

record_end

分发:call_owner

录音终结事件。在录音上传完成后触发;若无上传配置则在录音文件就绪后触发(使用本地文件路径)。SipFlow 媒体上传完成后也会触发。

触发条件

  • 普通录音:CallRecordManager 处理完录音记录后,RecordingUploadHook 自动触发
  • SipFlow 录音:SipFlow 媒体文件上传到 S3/HTTP 完成后自动触发
  • 需要通过 RecordStop 命令触发,与 record_started/record_stopped 由 command 触发的模式不同
字段类型说明
call_idString呼叫标识
urlOption<String>上传 URL(有上传时)或本地文件路径(无上传时),SipFlow 场景为媒体文件 URL
duration_secsu64录音时长(秒)
file_sizeu64文件大小(字节)

6.5 IVR 事件

所有 IVR 事件携带扁平化上下文。

ivr_node_entered

分发:fan_out_to_context

呼叫进入 IVR 节点(菜单、播放提示音等)。

字段类型说明
call_idString呼叫标识
node_idString节点 ID
node_nameString节点名称
node_typeString节点类型(menuprompttransfer 等)
app_idStringIVR 应用 ID
entry_timeString进入时间(ISO 8601)
caller_nameOption<String>主叫号码
callee_nameOption<String>被叫号码
routing_targetOption<String>路由目标
previous_node_idOption<String>上一个节点 ID
+ctx扁平化上下文

ivr_node_exited

分发:fan_out_to_context

呼叫退出 IVR 节点。

会话终止时也会触发:当 sip_session 在执行中被终止(主叫挂机、系统取消等),内置(tree 模式)IVR 也会 emit 本事件记录主叫当时所在的节点,此时 hangup_reason 被填充(取值:cancelledremote_hanguphanguperror 等),call_result"hangup"

字段类型说明
call_idString呼叫标识
node_idString节点 ID
node_nameString节点名称
result_valueOption<String>用户按键或分支结果
duration_msu32节点停留时长(毫秒)
exit_timeString退出时间
next_node_idOption<String>下一个节点 ID
hangup_reasonOption<String>挂机原因(会话终止时取值:cancelled/remote_hangup/hangup 等)
call_resultOption<String>通话结果
+ctx扁平化上下文

ivr_flow_transitioned

分发:fan_out_to_context

呼叫在 IVR 应用之间跳转。

字段类型说明
call_idString呼叫标识
from_app_idString源应用 ID
to_app_idString目标应用 ID
from_node_idString源节点 ID
to_node_idString目标节点 ID
transition_reasonString跳转原因(menu_choicetransferoverflow 等)
transition_timeString跳转时间
next_routing_targetOption<String>下一个路由目标
+ctx扁平化上下文

ivr_flow_completed

分发:fan_out_to_context

IVR 流程完成(执行了终止动作:转接、排队、留言、挂机)。

会话终止时也会触发:内置(tree 模式)IVR 在 sip_session 执行中被终止(主叫挂机 remote_hangup、系统取消 cancelled 等)时会以 final_result 记录终止原因,并携带 total_nodes_traversed 统计信息。final_result 取值:transferredqueuevoicemailhangupabandonedcancelledremote_hanguperror 等。

字段类型说明
call_idString呼叫标识
app_idStringIVR 应用 ID
total_nodes_traversedu32经过的节点总数
total_duration_msu32IVR 总耗时(毫秒)
final_resultString最终结果(transferredvoicemailabandonedcancelledremote_hangup 等)
completion_timeString完成时间
final_routing_targetOption<String>最终路由目标
+ctx扁平化上下文
{
  "rwi": "1.0",
  "ivr_flow_completed": {
    "call_id": "call-abc",
    "app_id": "ivr-sales",
    "total_nodes_traversed": 3,
    "total_duration_ms": 15200,
    "final_result": "transferred",
    "completion_time": "2026-05-14T17:55:00Z",
    "final_routing_target": "queue:support",
    "caller": "13800138000",
    "direction": "inbound"
  }
}

ivr_step_trace

分发:fan_out_to_context

Step-Mode IVR 跟踪事件。每一步 provider 往返或动作执行完成时产生。

会话终止条目(session_end:当 IVR 会话结束(含主叫挂机 RemoteHangup、系统取消 Cancelled)时,会额外 emit 一条 trigger.type="session_end" 的跟踪事件,action_type/step_id/step_name 记录最后执行的节点,并填充 end_reason/end_detail 表示整个会话的结束原因。外部 provider 的 /end webhook 在 RemoteHangup/Cancelled 时不会被调用(本地跟踪事件照常发出)。

字段类型说明
call_idString呼叫标识
session_idString会话 ID
callerString主叫
calleeString被叫
step_indexu32步骤序号
triggerObject触发该步骤的结构化信息,见下方说明
action_typeString动作类型(如 TransferPromptDtmfMenu
action_jsonOption<String>动作详情 JSON
result_kindString结果类型(terminalcontinueerror
duration_msu64步骤执行耗时(毫秒),始终有值
errorOption<String>错误信息
step_idOption<String>当前节点 ID,由 Provider 通过 ActionNode.step_id 返回
step_nameOption<String>当前节点名称,由 Provider 通过 ActionNode.step_name 返回
step_start_timeOption<String>当前步骤开始时间(ISO UTC)
step_end_timeOption<String>当前步骤结束时间(ISO UTC)。仅步骤执行完成(terminal/error)时有值;等待用户输入(WaitFor)时为 null
extraOption<JSON Object>Provider 透传的额外数据。Provider 在每次响应的 ActionNode.extra 中返回完整对象,RustPBX 透传存储并原样输出
end_reasonOption<String>仅会话终止(session_end)条目有值,标识整个 IVR 会话如何结束(normaltransfertransfer_to_queuehangupuser_hanguptimeouterror 等)
end_detailOption<String>end_reason 配套的详情(如转接目标、错误信息)

trigger 字段说明

描述触发当前步骤执行的原因,是一个对象:

{ "type": "dtmf", "detail": { "digit": "2" } }
子字段类型说明
typeString触发源类型:session_startsession_enddtmfdtmf_menudtmf_menu_timeoutaudio_completeaction_executechainedapi_responsephone_collectedrecording_completeinput_voiceerrordtmf_menu_invalidunknown
detailOption<JSON Object>触发详情对象,无详情时省略。常见取值:DTMF → {"digit":"2"};API 响应 → {"status":200};号码收集 → {"number":"13800138000"}

时间字段说明

  • step_start_time — 当前步骤的开始时间(上一步结束或 session 开始)
  • step_end_time — 步骤结束时间(仅完成时)

耗时字段说明

  • duration_ms — 步骤执行耗时(毫秒),始终有值,包含 provider 往返和动作执行时间

6.6 队列 / ACD 事件

事件来源说明:队列相关事件分两个家族,由不同子系统产生,可同时出现:

  • queue_*(队列生命周期):由 Queue 应用(src/call/app/queue.rs)产生,无论是否启用 CC addon 都会发。覆盖入队、振铃、接通、放弃、超时、回退等通用生命周期。
  • skill_group_*(技能组调度决策):由 CC addon 的 ACD 适配器(src/addons/cc/agent_registry_adapter.rs)在队列向 ACD 询问坐席、ACD 产出调度结果时产生,仅在启用 CC addon 且使用技能路由时发

一通走技能组的呼叫,典型事件序列: queue_joinedskill_group_candidates_foundskill_group_agent_assignedqueue_agent_offeredqueue_agent_connected

所有队列事件携带扁平化上下文。

queue_joined

分发:call_owner / broadcast

字段类型说明
call_idString呼叫标识
queue_idString队列 ID
+ctx扁平化上下文

queue_position_changed

字段类型说明
call_idString呼叫标识
queue_idString队列 ID
positionu32当前排队位置
+ctx扁平化上下文

queue_agent_offered / queue_agent_connected

字段类型说明
call_idString呼叫标识
queue_idString队列 ID
agent_idString坐席 ID
+ctx扁平化上下文

queue_left

字段类型说明
call_idString呼叫标识
queue_idString队列 ID
reasonOption<String>离开原因
+ctx扁平化上下文

queue_wait_timeout

字段类型说明
call_idString呼叫标识
queue_idString队列 ID
+ctx扁平化上下文

queue_overflowed

字段类型说明
call_idString呼叫标识
original_queue_idString原队列 ID
overflow_queue_idString溢出目标队列 ID
reasonString溢出原因
+ctx扁平化上下文

queue_voicemail_redirected

字段类型说明
call_idString呼叫标识
queue_idString队列 ID
reasonString原因
+ctx扁平化上下文

queue_candidates_found

字段类型说明
call_idString呼叫标识
queue_idString队列 ID
candidatesVec<String>候选坐席列表
trace_idStringACD 跟踪 ID
+ctx扁平化上下文

queue_agent_ringing / queue_agent_no_answer / queue_agent_rejected

字段类型说明
call_idString呼叫标识
queue_idString队列 ID
agent_idString坐席 ID
attemptu32no_answer/rejected 专用:尝试次数
trace_idStringACD 跟踪 ID
+ctx扁平化上下文

queue_fallback_executed

字段类型说明
call_idString呼叫标识
queue_idString队列 ID
actionString执行的回退动作
reasonString原因
trace_idStringACD 跟踪 ID
+ctx扁平化上下文

queue_alert

分发:broadcast(无 call_id)

字段类型说明
queue_idString队列 ID
alert_typeString告警类型
messageString告警消息

skill_group_candidates_found

分发:broadcast

ACD 调度器为技能组找到候选坐席时触发。

字段类型说明
call_idString呼叫标识
skill_group_idOption<String>技能组 ID(显式 skill-group:{id} 路径为 Some;自主技能路由为 None
candidatesVec<String>候选坐席 ID 列表
trace_idString跟踪 ID

skill_group_agent_assigned

分发:broadcast

ACD 调度器决定将某坐席分配给该呼叫时触发(ACD Assign 决策或策略选中的首位坐席)。

字段类型说明
call_idString呼叫标识
skill_group_idOption<String>技能组 ID
agent_idString被分配的坐席 ID
trace_idString跟踪 ID

skill_group_no_agent

分发:broadcast

ACD 调度器无法为技能组提供坐席时触发。

字段类型说明
call_idString呼叫标识
skill_group_idOption<String>技能组 ID
reasonString原因(no_candidates 无匹配坐席 / acd_blocked 被 ACD 策略拦截 / no_strategy_match 策略未选中)

6.7 坐席状态事件

agent_state_changed

分发:broadcast

坐席状态机转换。

字段类型说明
agent_idString坐席 ID
from_statusString原状态
to_statusString新状态
call_idOption<String>关联呼叫 ID
agent_nameOption<String>坐席名称
agent_extensionOption<String>坐席分机号
callerOption<String>主叫 / 分机号
team_idOption<String>团队 ID
duration_secsOption<u32>上一状态持续时长
reason_codeOption<String>原因码(如 CALLBREAKTRAINING

坐席状态枚举

状态说明可转到
offline离线idleawaydnd
idle空闲(可接听)ringingawaydndoffline
away离开(小休)idledndoffline
dnd勿扰idleawayoffline
ringing振铃中(含 call_id)busy(接听)、idle(未接)
busy通话中(含 call_id)wrapup
wrapup话后处理idleawaydnd
custom:<name>自定义状态idleawaydndoffline
{
  "rwi": "1.0",
  "agent_state_changed": {
    "agent_id": "agent-001",
    "from_status": "idle",
    "to_status": "busy",
    "call_id": "call-abc",
    "agent_name": "Alice",
    "agent_extension": "8001",
    "caller": "8001",
    "team_id": "sales",
    "duration_secs": 300,
    "reason_code": "CALL"
  }
}

6.8 分机(DN)事件

dn_state_changed

分发:broadcast

分机级别的细粒度信令事件。

字段类型说明
callerString分机号 / 主叫
event_nameString事件名称(见下表)
system_timeString系统时间
call_idOption<String>关联呼叫 ID
agent_idOption<String>坐席 ID
caller_nameOption<String>主叫名称/号码
callee_nameOption<String>被叫名称/号码
reason_codeOption<String>原因码
agent_work_modeOption<String>坐席工作模式
releasing_partyOption<String>释放方("1 Local" / "2 Remote"
vq_nameOption<String>虚拟队列名
routing_targetOption<String>路由目标
skill_groupOption<String>技能组
extraOption<Map<String, Value>>扩展字段(省略时不出现在 JSON 中)

event_name 枚举值

event_name说明触发场景
REGISTERED分机注册SIP REGISTER 成功
DIALING外呼拨号坐席外呼或手工拨号
RINGING振铃坐席侧振铃
ESTABLISHED接通通话建立,坐席接起电话
RELEASED释放挂机或转接成功后
ABANDONED挂断振铃阶段用户放弃
HELD保持坐席保持,用户听音乐
RETRIEVED取回将 held 的用户取回
PARTYCHANGED多方通话状态变更多方通话状态变化
PARTYADDED多方通话新增多方通话新增一方
PARTYDELETED多方通话删除多方通话减少一方
AGENTLOGIN坐席登录坐席从离线变为在线(CC addon)
AGENTLOGOUT坐席登出坐席从在线变为离线(CC addon)
AGENTREADY坐席就绪坐席进入空闲状态(CC addon)
AGENTNOTREADY坐席未就绪坐席进入忙碌/振铃/话后处理等状态(CC addon)
ONHOOK摘机软电话摘机

注意:使用 event_name 做事件路由和匹配。

{
  "rwi": "1.0",
  "dn_state_changed": {
    "caller": "80001",
    "event_name": "ESTABLISHED",
    "system_time": "2026-05-14T17:54:49.003Z",
    "call_id": "call-abc",
    "agent_id": "10001",
    "caller_name": "19534519769",
    "callee_name": "39989",
    "extra": {
      "source": "KS",
      "kz_conn_id": "kc-12345",
      "user_data": { "kz_target": "39299", "kz_flowname": "CTC400Customer" }
    }
  }
}

dn_registered / dn_unregistered

分发:broadcast

字段类型说明
callerString分机号
agent_idOption<String>坐席 ID
register_time / unregister_timeString注册/注销时间

6.9 呼叫元数据事件

call_metadata_updated

呼叫建立后元数据更新时触发。

字段类型说明
call_idString呼叫标识
metadataCallMetadata元数据(见下表)

CallMetadata 字段

字段类型说明
root_call_idOption<String>根呼叫 ID
caller_nameOption<String>主叫号码
callee_nameOption<String>被叫号码
called_phoneOption<String>实际被叫号码
dial_directionOption<String>呼叫方向
uuidOption<String>全局 UUID
routing_pathOption<Vec<String>>路由路径
app_idOption<String>IVR 应用 ID
routing_targetOption<String>路由目标
switch_nameOption<String>交换机名
{
  "rwi": "1.0",
  "call_metadata_updated": {
    "call_id": "call-abc",
    "metadata": {
      "root_call_id": "call-root-42",
      "caller_name": "330909",
      "callee_name": "9242000001",
      "called_phone": "018659727661",
      "dial_direction": "inbound",
      "uuid": "uuid-abc-123",
      "routing_path": ["menu:root", "queue:level1"],
      "app_id": "ivr-support",
      "routing_target": "queue:support",
      "switch_name": "SIP_Switch_KS"
    }
  }
}

6.10 会议事件

conference_created / conference_destroyed

分发:broadcast

字段类型说明
conf_idString会议房间 ID

conference_member_joined / conference_member_left / conference_member_muted / conference_member_unmuted

分发:broadcast

字段类型说明
conf_idString会议 ID
call_idString成员呼叫 ID
+ctx扁平化上下文

conference_ended_by_host

字段类型说明
conf_idString会议 ID
host_call_idString主持人呼叫 ID
removed_call_idsVec<String>被移除的成员
+ctx扁平化上下文

conference_auto_ended

字段类型说明
conf_idString会议 ID
reasonString结束原因
+ctx扁平化上下文

conference_error

字段类型说明
conf_idString会议 ID
errorString错误信息

conference_consult_dialing / conference_consult_connected

字段类型说明
call_idString咨询呼叫 ID
targetString咨询目标
+ctx扁平化上下文

conference_merge_requested / conference_merged / conference_merge_failed

字段类型说明
call_idString呼叫 ID(merge_requestedconsultation_call_id
conf_idString会议 ID(merged/merge_failed
consultation_call_idStringmerge_requested 专用:咨询呼叫 ID
reasonStringmerge_failed 专用:失败原因
+ctx扁平化上下文

conference_seat_replace_started / ...succeeded / ...failed / ...rollback_failed

字段类型说明
conf_idString会议 ID
old_call_idString原成员呼叫 ID
new_call_idString新成员呼叫 ID
reasonStringfailed/rollback_failed 专用:失败原因

座位替换事件序列(成功路径)

  1. conference_seat_replace_started
  2. conference_member_left(旧成员离开)
  3. conference_member_joined(新成员加入)
  4. conference_seat_replace_succeeded

6.11 管理监控事件

supervisor_listen_started / supervisor_whisper_started / supervisor_barge_started / supervisor_takeover_started

字段类型说明
supervisor_call_idString管理员呼叫 ID
target_call_idString被监控呼叫 ID

supervisor_mode_stopped

字段类型说明
supervisor_call_idString管理员呼叫 ID
target_call_idString被监控呼叫 ID

6.12 并行外呼事件

parallel_originate_started

字段类型说明
operation_idString操作 ID
leg_countu32并发 leg 数量

parallel_originate_leg_ringing / parallel_originate_winner / parallel_originate_leg_cancelled

字段类型说明
operation_idString操作 ID
call_idStringLeg 呼叫 ID
destinationString拨打目标
reasonStringleg_cancelled 专用:取消原因
+ctx扁平化上下文

parallel_originate_completed

字段类型说明
operation_idString操作 ID
winning_call_idString中选呼叫 ID

parallel_originate_failed

字段类型说明
operation_idString操作 ID
reasonString失败原因

6.13 SIP 信令事件

sip_message_received / sip_notify_received

字段类型说明
call_idString呼叫标识
content_typeString内容类型
bodyString消息内容
eventStringsip_notify_received 专用:SIP Event 头
+ctx扁平化上下文

6.14 会话系统事件

call_ownership_changed

字段类型说明
call_idString呼叫标识
session_idString接管会话 ID
modeString模式(control/listen/whisper/barge
+ctx扁平化上下文

session_resumed

字段类型说明
session_idString恢复的会话 ID
last_sequenceu64客户端上报的最后序号

7. 事件类型速查表

事件类型分发call_id上下文
call_incomingfan_out自有字段
call_ringingowner+ctx
call_early_mediaowner+ctx
call_answeredowner+ctx
call_bridgedownerleg_a
call_unbridgedowner+ctx
call_transferredowner+ctx
call_transfer_acceptedowner+ctx
call_transfer_failedowner+ctx
call_hangupowner+ctx
call_no_answerowner+ctx
call_busyowner+ctx
media_hold_startedowner+ctx
media_hold_stoppedowner+ctx
media_ringback_passthrough_startedowner
media_ringback_passthrough_stoppedowner
media_play_startedowner+ctx
media_play_finishedowner+ctx
media_stream_startedowner+ctx
media_stream_stoppedowner+ctx
record_startedowner+ctx
record_pausedowner+ctx
record_resumedowner+ctx
record_stoppedowner自有字段+enrich
record_failedowner+ctx
recording_metadata_availableowner
dtmffan_out+ctx
dtmf_collectedowner+ctx
dtmf_collection_timeoutowner+ctx
ivr_node_enteredfan_out+ctx
ivr_node_exitedfan_out+ctx
ivr_flow_transitionedfan_out+ctx
ivr_flow_completedfan_out+ctx
ivr_step_tracefan_out
queue_joinedowner/broadcast+ctx
queue_position_changedowner+ctx
queue_agent_offeredbroadcast+ctx
queue_agent_connectedowner+ctx
queue_leftbroadcast+ctx
queue_wait_timeoutowner+ctx
queue_overflowedowner+ctx
queue_voicemail_redirectedowner+ctx
queue_candidates_foundowner+ctx
queue_agent_ringingowner+ctx
queue_agent_no_answerowner+ctx
queue_agent_rejectedowner+ctx
queue_fallback_executedowner+ctx
queue_alertbroadcast
skill_group_candidates_foundbroadcast
skill_group_agent_assignedbroadcast
skill_group_no_agentbroadcast
agent_state_changedbroadcast可选
dn_state_changedbroadcast可选
dn_registeredbroadcast
dn_unregisteredbroadcast
call_metadata_updatedowner
conference_createdbroadcast
conference_member_joinedbroadcast+ctx
conference_member_leftbroadcast+ctx
conference_member_mutedbroadcast+ctx
conference_member_unmutedbroadcast+ctx
conference_destroyedbroadcast
conference_ended_by_hostbroadcast+ctx
conference_auto_endedbroadcast+ctx
conference_errorbroadcast
conference_consult_dialingowner+ctx
conference_consult_connectedowner+ctx
conference_merge_requestedfan_out+ctx
conference_mergedfan_out+ctx
conference_merge_failedfan_out+ctx
conference_seat_replace_startedfan_out
conference_seat_replace_succeededfan_out
conference_seat_replace_failedfan_out
conference_seat_replace_rollback_failedfan_out
supervisor_listen_startedowner
supervisor_whisper_startedowner
supervisor_barge_startedowner
supervisor_takeover_startedowner
supervisor_mode_stoppedowner
parallel_originate_startedowner
parallel_originate_leg_ringingowner+ctx
parallel_originate_winnerowner+ctx
parallel_originate_leg_cancelledowner+ctx
parallel_originate_completedowner
parallel_originate_failedowner
sip_message_receivedowner+ctx
sip_notify_receivedowner+ctx
call_ownership_changedowner+ctx
session_resumedowner

8. 开发者示例

Python Webhook 接收

from http.server import HTTPServer, BaseHTTPRequestHandler
import json

class Handler(BaseHTTPRequestHandler):
    def do_POST(self):
        length = int(self.headers.get("Content-Length", 0))
        body = json.loads(self.rfile.read(length))

        event_type = body["event_type"]
        call_id = body["call_id"]

        print(f"[{event_type}] call_id={call_id}")

        if event_type == "recording_metadata_available":
            meta = body["event"]["recording_metadata_available"]["metadata"]
            print(f"  download: {meta['download_url']}")
            print(f"  file_size: {meta['file_size']}")

        self.send_response(200)
        self.send_header("Content-Type", "application/json")
        self.end_headers()
        self.wfile.write(b'{"status":"ok"}')

HTTPServer(("0.0.0.0", 8080), Handler).serve_forever()

Python WebSocket 实时监听

import asyncio, json
from websockets import connect

async def main():
    async with connect(
        "ws://pbx.example.com/rwi/v1",
        additional_headers={"Authorization": "Bearer your-token"},
        subprotocols=["rwi-v1"],
    ) as ws:
        await ws.send(json.dumps({
            "rwi": "1.0",
            "action_id": "sub-001",
            "action": "session.subscribe",
            "params": {"contexts": ["*"]}
        }))

        async for msg in ws:
            payload = json.loads(msg)
            for key, data in payload.items():
                if key == "rwi":
                    continue
                print(f"[{key}] {json.dumps(data, ensure_ascii=False)}")

asyncio.run(main())

9. 辅助结构体

以下结构体供嵌套引用,不独立作为事件发出。

IvrNodeInfo

字段类型说明
node_idString节点 ID
node_nameString节点名称
node_typeString节点类型
routing_targetOption<String>路由目标
previous_node_idOption<String>上一节点 ID
next_node_idOption<String>下一节点 ID
duration_msOption<u32>停留时长
result_valueOption<String>按键/结果

IvrFlowContext

字段类型说明
app_idStringIVR 应用 ID
routing_pathVec<String>路由路径
service_typeOption<String>业务类型
customer_typeOption<String>客户类型

文档版本:v1.0
最后更新:2026-06-23
代码来源src/rwi/proto.rs