通话事件与录音 API

说明通话事件 Webhook 载荷、录音事件与录音下载地址重新签发接口

简述

通过 通话集成 → 事件订阅 配置规则后,OpenDesk 会把命中的通话事件以 Webhook 形式推送到客户服务器。若事件中包含录音临时下载地址,地址过期后,可使用 API Key 调用开放接口重新签发下载地址。

本文面向客户技术团队,说明事件接收、去重、常见事件与录音补拉方式。


接入前准备

准备项说明
事件订阅 Endpoint在 全局设置 → 事件订阅 中创建,保存 Webhook Secret
通话事件规则在 呼叫中心 → 通话集成 → 事件订阅 中配置方向、来源与事件类型
API Key在 全局设置 → API Key 中创建,用于录音地址过期后重新签发

Webhook 请求

OpenDesk 以 POST 方式向订阅 Endpoint 推送事件。接收方应尽快返回 2xx,耗时处理建议异步执行。

常见请求头:

Content-Type: application/json
User-Agent: OpenDesk-Webhook/1.0
Authorization: Bearer <Webhook Secret>
X-OpenDesk-Delivery: <本次投递标识>

建议接收方:

  1. 使用固定时间比较方式校验 Authorization 中的 Webhook Secret。
  2. 使用正文中的 id 去重;同一事件重试时 id 不变。
  3. 使用 subject 或 data.call.public_id 把同一通电话的事件归并。
  4. 使用 data.sequence 判断同一通电话内事件发生顺序。

Endpoint 返回 200~299 视为投递成功。超时、连接失败或返回其他状态码时,OpenDesk 会自动重试;测试请求不重试。


事件正文结构

事件正文遵循 CloudEvents 风格,示例:

{
  "specversion": "1.0",
  "id": "evt_稳定事件标识",
  "source": "opendesk://call-center",
  "type": "call.ended",
  "subject": "call/11111111-2222-3333-4444-555555555555",
  "time": "2026-10-23T04:34:38.912000+00:00",
  "datacontenttype": "application/json",
  "dataschema": "opendesk://schemas/events/call.ended/v1",
  "apiversion": "v1",
  "deliveryid": "本次投递标识",
  "test": false,
  "data": {
    "sequence": 10,
    "call": {},
    "event": {}
  }
}

事件信封字段

字段说明
specversion固定为 1.0
id稳定事件标识,用于去重;同一事件重试时保持不变
source通话事件固定为 opendesk://call-center
type事件类型,如 call.created、call.ended
subject通话公开标识,格式为 call/{call_public_id}
time事件事实发生时间,不是客户服务器收到时间
datacontenttype固定为 application/json
dataschema事件 Schema 标识,如 opendesk://schemas/events/call.ended/v1
apiversion固定为 v1
deliveryid本次 HTTP 投递标识,与请求头 X-OpenDesk-Delivery 相同;不要用它做业务去重
test是否为测试请求;正式通话事件为 false
data.sequence同一通电话内的事件序号;序号可能跳号,跳号不代表丢事件
data.call通话摘要,每条通话事件都会携带
data.event当前事件自己的字段

后台 发送测试请求 的事件类型为 opendesk.webhook.test,用于验证 Endpoint 连通性,不应按通话事件解析。


常见事件类型

事件何时出现一通电话中可能出现次数
call.created通话记录已创建,外呼刚开始一次
call.customer.connected客户已接通;外呼时表示客户已接听最多一次
call.queue.entered通话进入员工或员工组队列可多次
call.agent.connected坐席开始服务客户转接后可多次
call.agent.disconnected一段人工服务结束可多次
call.transfer.completed转给坐席、队列或外部号码成功可多次
call.ended通话结算完成,结果与时长已落库一次
call.recording.completed一段录音已完成或部分完成每段录音一次
call.recording.failed一段录音失败每段录音一次
call.satisfaction.completed满意度调查完成每次调查一次

没有发生的阶段不会推送。例如客户未接通的外呼通常不会有坐席接入、人工段录音或满意度事件。


通话摘要 data.call

不同事件会携带当前已发生的通话摘要字段。尚未发生或无法关联的信息通常为 null。

每条通话事件都会携带

字段说明
public_id通话公开标识,与 subject 中 / 后面的值相同
direction通话方向:inbound 呼入,outbound 外呼
source通话来源,如 embedded_call、agent_dial、auto_outbound、manual_outbound_task、personal_auto_outbound
lifecycle_status通话生命周期状态,常见为 active、ended
customer_number客户号码
service_number服务号码 / 外显号码
user已关联用户时为 {"public_id":"usr_..."},否则为 null
initiator_agent发起坐席,结构为 {"username":"登录名","name":"显示名"}
last_serving_agent最近服务坐席,结构同 initiator_agent
last_queue最近进入的队列,未进过队列时为 null
started_at通话开始时间
customer_connected_at客户接通时间
ended_at通话结束时间
external_context嵌入式通话拨号时传入的外部上下文,便于客户系统关联订单、工单或会员

仅 call.ended 携带

字段说明
finalization_status结算状态,常见为 completed
call_result通话结果,如 agent_answered、customer_busy、customer_no_answer、failed
hangup_party挂断方,如 customer、agent、system、carrier、unknown
hangup_reason_code挂断原因代码,如 agent_hangup、customer_busy、customer_no_answer、provider_rejected
actual_reception_type实际接待类型,如 agent、bot、bot_agent、ivr;未接通时通常为 null
total_duration_ms从开始到结束的总时长,单位毫秒
customer_ring_duration_ms客户侧振铃时长,单位毫秒
agent_wait_duration_ms等待坐席时长,单位毫秒
customer_agent_talk_duration_ms客户与坐席实际通话时长,单位毫秒
recording_status整通电话的录音汇总状态,如 none、recording、completed、partial、failed
custom_business_info嵌入式通话拨号时传入的业务文本;只在 call.ended 返回

常见 call_result:

值含义
agent_answered坐席曾经接起,形成了人工服务
customer_busy客户忙线或对方按拒接
customer_no_answer客户未接听,振铃超时
customer_unreachable客户不可达或关机
invalid_number号码无效
agent_no_answer已经进队列或振铃给坐席,但坐席未接
bot_completed机器人服务完成
canceled坐席或系统在接通前取消
failed其他失败,包括线路拒绝、发起失败、媒体失败等

客户拒接时通常表现为 call_result=customer_busy、hangup_reason_code=customer_busy、hangup_party=carrier。线路直接拒绝时通常表现为 call_result=failed、hangup_reason_code=provider_rejected。


当前事件内容 data.event

data.event 表示当前这一条事件自己的字段。通话最终结果、总时长和挂断原因仍以 call.ended 的 data.call 为准。

事件data.event 主要字段
call.createdcreated_at:通话记录创建时间
call.customer.connectedconnected_at:客户接通时间
call.queue.enteredqueue.type、queue.name:进入的队列;entered_at:进入时间
call.agent.connectedagent:接入坐席;connected_at:这一段人工服务开始时间
call.agent.disconnectedagent:离开坐席;disconnected_at:这一段结束时间;duration_ms:这一段人工服务时长
call.transfer.completedtransfer_type:agent、queue 或 external;from_agent:转出坐席;target:转接目标;completed_at:转接完成时间
call.endedended_at:通话结束时间
call.recording.completed / call.recording.failedrecording:录音对象,见下一节
call.satisfaction.completedstatus:满意度状态;result:首题答案;trigger_source:触发来源;completed_at:完成时间

录音事件

录音事件中的 data.event.recording 表示一段录音。示例:

{
  "recording": {
    "public_id": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
    "phase": "human",
    "status": "completed",
    "started_at": "2026-10-23T04:34:31.650000+00:00",
    "ended_at": "2026-10-23T04:34:38.912000+00:00",
    "duration_ms": 7340,
    "download_url": "https://临时下载地址",
    "download_url_expires_at": "2026-10-23T05:34:39.260000+00:00",
    "failure_reason": null
  }
}
字段说明
public_id录音公开标识,地址过期后用于重新签发
phase录音阶段:bot 机器人段、human 人工段、other 其他阶段
status录音状态:completed 完整、partial 部分完成、failed 失败
started_at / ended_at录音开始 / 结束时间
duration_ms录音时长,单位毫秒
download_url临时下载地址,completed 和 partial 时返回
download_url_expires_at下载地址失效时间
failure_reason失败原因;失败录音没有下载地址

收到下载地址后建议尽快转存到客户自己的存储。请勿将临时地址暴露到前端页面或长期日志中。


回调案例

案例一:客户接听,坐席挂断

坐席从嵌入式软电话拨出,客户接听后通话 42 秒,最后坐席挂断。规则如果勾选了完整过程,通常会依次收到 call.created、call.customer.connected、call.agent.connected、call.agent.disconnected、call.ended;开启录音时,结束后还可能收到 call.recording.completed。

这类通话的 call.ended 关键字段通常如下:

{
  "type": "call.ended",
  "subject": "call/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
  "data": {
    "sequence": 8,
    "call": {
      "public_id": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
      "direction": "outbound",
      "source": "embedded_call",
      "customer_number": "13800138000",
      "service_number": "02158000000",
      "initiator_agent": { "username": "agent01", "name": "张敏" },
      "last_serving_agent": { "username": "agent01", "name": "张敏" },
      "customer_connected_at": "2026-10-26T03:25:09.880000+00:00",
      "ended_at": "2026-10-26T03:25:52.184000+00:00",
      "external_context": { "record_type": "ticket", "record_id": "T-1001" },
      "finalization_status": "completed",
      "call_result": "agent_answered",
      "hangup_party": "agent",
      "hangup_reason_code": "agent_hangup",
      "total_duration_ms": 48082,
      "customer_agent_talk_duration_ms": 42304,
      "recording_status": "none",
      "custom_business_info": "订单 SO-10086"
    },
    "event": {
      "ended_at": "2026-10-26T03:25:52.184000+00:00"
    }
  }
}

recording_status 在结束事件中为 none,只表示结束这一刻录音还没有完成;后续如果收到 call.recording.completed,应按同一个 subject 归到这通电话。

案例二:客户拒接或未接

客户拒接、忙线或一直未接时,通常只会收到 call.created 和 call.ended。不会有 call.customer.connected、坐席接入、人工录音或满意度事件。

{
  "type": "call.ended",
  "subject": "call/bbbbbbbb-cccc-dddd-eeee-ffffffffffff",
  "data": {
    "sequence": 7,
    "call": {
      "public_id": "bbbbbbbb-cccc-dddd-eeee-ffffffffffff",
      "direction": "outbound",
      "source": "embedded_call",
      "customer_number": "13800138001",
      "customer_connected_at": null,
      "last_serving_agent": null,
      "finalization_status": "completed",
      "call_result": "customer_busy",
      "hangup_party": "carrier",
      "hangup_reason_code": "customer_busy",
      "total_duration_ms": 4903,
      "customer_ring_duration_ms": 4903,
      "customer_agent_talk_duration_ms": 0,
      "recording_status": "none"
    }
  }
}

如果客户一直未接,常见结果为 call_result=customer_no_answer、hangup_reason_code=customer_no_answer。如果是线路直接拒绝,常见结果为 call_result=failed、hangup_reason_code=provider_rejected。

案例三:录音完成并转存

录音完成后会收到 call.recording.completed。一通电话可能有多段录音,每段录音都会有自己的 recording.public_id。

{
  "type": "call.recording.completed",
  "subject": "call/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
  "data": {
    "sequence": 9,
    "call": {
      "public_id": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
      "source": "embedded_call",
      "recording_status": "completed"
    },
    "event": {
      "recording": {
        "public_id": "rrrrrrrr-1111-2222-3333-444444444444",
        "phase": "human",
        "status": "completed",
        "duration_ms": 42304,
        "download_url": "https://临时下载地址",
        "download_url_expires_at": "2026-10-26T04:26:10.000000+00:00",
        "failure_reason": null
      }
    }
  }
}

客户服务器收到后应尽快下载并转存。若临时地址已过期,可使用 recording.public_id 重新签发下载地址。


重新签发录音下载地址

使用 API Key 重新签发地址。接口只允许查询当前 Key 所属租户的录音。

按通话查询录音列表

GET {OpenDesk地址}/api/v1/open/call-center/calls/{call_public_id}/recordings
Authorization: Bearer sk-odk-您的密钥

返回该通电话的录音列表,并为可下载录音重新签发约 1 小时有效的下载地址。

按录音重新签发单段地址

GET {OpenDesk地址}/api/v1/open/call-center/recordings/{recording_public_id}/download-url
Authorization: Bearer sk-odk-您的密钥

失败录音或没有文件的录音不会返回下载地址。


错误与限制

状态常见原因
401API Key 缺失、无效或已停用
404通话或录音不存在,或不属于当前租户
409录音失败或没有可下载文件
429重新签发过于频繁

单个 API Key 的录音地址签发有频率限制。批量补拉时建议控制并发与重试节奏。


排查建议

  • 回调没到:先确认 事件订阅 测试请求成功,再确认 通话集成 → 事件订阅 规则已启用且匹配方向、来源和事件类型。
  • 事件不完整:没有发生的通话阶段不会推送;例如客户未接通时,不会有坐席接入事件。
  • 录音地址过期:使用本文的录音开放接口重新签发。
  • 业务对不上:使用 subject 中的通话公开标识,在 服务运营 → 通话记录 中搜索核对。