通话管理
本页介绍你的服务端如何查询本应用的通话、强制结束通话、把成员移出通话,以及查看每天的通话统计。通话的流程、状态、结束原因和通话对象的字段见音视频通话。
典型用法:
- 在客服、问诊等业务中按用户或时间查询通话,结合通话的
ext(如订单号)核对业务记录; - 发现违规的通话时强制结束,或把某个成员移出群通话;
- 在发起业务操作前查询用户是否正在通话;
- 每天读取通话统计,与账单核对计费分钟数。
这些接口的说明:
- 不受运行策略
rtc_enabled限制:应用关闭音视频后,仍然可以查询和结束通话;应用处于只读状态时同样可用。 - 不能发起通话或代用户接听:通话需要用户的设备连接媒体服务,只能由客户端发起和接听。
- 通话数据在结束 180 天后删除,之后查询不到;需要长期保存的,请通过事件回调的
rtc.call_ended保存到你的业务系统。 - 控制台应用详情的“音视频”页面也可以查询通话、查看统计,
owner和admin可以在其中结束通话和移出成员。
查询通话列表
按条件查询本应用的通话,按发起时间从新到旧排列,包括进行中和已结束的通话。列表中的每一项不含成员,需要成员时查询通话详情。
/{org_name}/{app_name}/calls路径参数
org_name、app_name 见接入概述。
查询参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
username | String | 否 | 只返回这个用户参与过的通话,即他是成员的通话,包括他未接听、拒绝的。用户不存在时返回空列表 |
group_id | String | 否 | 只返回这个群的群通话 |
type | String | 否 | 通话类型:single 或 group |
status | String | 否 | 通话状态:ringing、active 或 ended。查询进行中的通话时分别查询 ringing 和 active |
start_time | String | 否 | 只返回发起时间不早于它的通话,RFC 3339 格式,如 2026-10-04T19:23:00Z |
end_time | String | 否 | 只返回发起时间早于它的通话,格式同上 |
limit | Number | 否 | 每页条数,1 到 100,默认 20 |
cursor | String | 否 | 下一页的游标,即上一页返回的 next_cursor,第一页不传 |
各条件可以组合使用。
请求示例
curl "$IM_API/$ORG/$APP/calls?username=alice&limit=2" \
-H "Authorization: Bearer $APP_TOKEN"响应
成功返回 200 OK:
| 字段 | 类型 | 说明 |
|---|---|---|
items | Array<Object> | 本页的通话,即不含 members 的通话对象 |
next_cursor | String | 下一页的游标。没有下一页时为空字符串 "" |
{
"items": [
{
"call_id": "100313814330769408",
"type": "single",
"group_id": null,
"media": "audio",
"status": "ended",
"initiator": "bob",
"max_participants": 2,
"joined_count": 0,
"ext": null,
"created_at": "2026-10-04T19:31:17.077Z",
"answered_at": "2026-10-04T19:31:21.115Z",
"ended_at": "2026-10-04T19:31:49.168Z",
"end_reason": "completed",
"ended_by": "bob",
"duration_seconds": 28,
"version": 3,
"ended_via": "client",
"member_count": 2,
"peak_joined": 2,
"billable_minutes": 2
},
{
"call_id": "100313666666102784",
"type": "group",
"group_id": "100311544667045888",
"media": "video",
"status": "ended",
"initiator": "alice",
"max_participants": 16,
"joined_count": 0,
"ext": {
"topic": "周会"
},
"created_at": "2026-10-04T19:30:41.871Z",
"answered_at": "2026-10-04T19:30:44.923Z",
"ended_at": "2026-10-04T19:31:17.049Z",
"end_reason": "completed",
"ended_by": "alice",
"duration_seconds": 32,
"version": 5,
"ended_via": "client",
"member_count": 3,
"peak_joined": 3,
"billable_minutes": 3
}
],
"next_cursor": "100313666666102784"
}以 next_cursor 判断是否还有下一页
一次请求最多检查 1000 个通话。条件很少命中时(如按 type 筛选、或同时按 status 和 username 筛选),一页可能不足 limit 条,甚至为空,但 next_cursor 不为空,请继续用它翻页,直到 next_cursor 为空字符串。不要以本页的条数判断是否已经查完。
错误
| HTTP 状态码 | code | 说明 |
|---|---|---|
| 400 | invalid_argument | 参数不正确,details.field 指出是哪个参数:type 不是 single 或 group,status 不是 ringing、active 或 ended,start_time、end_time 不是 RFC 3339 格式,group_id 不是群 ID,limit 不在 1 到 100 之间,cursor 不正确 |
查询通话详情
按通话 ID 查询一个通话,包括全部成员。通话 ID 可以从通话列表、会话中的通话记录消息或事件回调中取得。
/{org_name}/{app_name}/calls/{call_id}路径参数
org_name、app_name 见接入概述。
| 参数 | 类型 | 说明 |
|---|---|---|
call_id | String | 通话 ID |
请求示例
curl "$IM_API/$ORG/$APP/calls/100313814330769408" \
-H "Authorization: Bearer $APP_TOKEN"响应
成功返回 200 OK,响应体为通话对象。一个已结束的一对一语音通话:
{
"call_id": "100313814330769408",
"type": "single",
"group_id": null,
"media": "audio",
"status": "ended",
"initiator": "bob",
"max_participants": 2,
"joined_count": 0,
"ext": null,
"created_at": "2026-10-04T19:31:17.077Z",
"answered_at": "2026-10-04T19:31:21.115Z",
"ended_at": "2026-10-04T19:31:49.168Z",
"end_reason": "completed",
"ended_by": "bob",
"duration_seconds": 28,
"version": 3,
"members": [
{
"username": "bob",
"role": "initiator",
"state": "left",
"media_uid": 1,
"invited_by": null,
"ring_expires_at": null,
"joined_at": "2026-10-04T19:31:17.077Z",
"left_at": "2026-10-04T19:31:49.168Z",
"leave_reason": "hangup",
"invited_at": null,
"joined_platform": "android",
"join_count": 1,
"joined_ms": 28053,
"first_joined_at": "2026-10-04T19:31:17.077Z",
"media_joined_at": "2026-10-04T19:31:17.094Z",
"media_left_at": null
},
{
"username": "alice",
"role": "invitee",
"state": "left",
"media_uid": 2,
"invited_by": "bob",
"ring_expires_at": "2026-10-04T19:32:17.077Z",
"joined_at": "2026-10-04T19:31:21.115Z",
"left_at": "2026-10-04T19:31:49.168Z",
"leave_reason": "ended",
"invited_at": "2026-10-04T19:31:17.077Z",
"joined_platform": "ios",
"join_count": 1,
"joined_ms": 28053,
"first_joined_at": "2026-10-04T19:31:21.115Z",
"media_joined_at": "2026-10-04T19:31:21.131Z",
"media_left_at": null
}
],
"ended_via": "client",
"member_count": 2,
"peak_joined": 2,
"billable_minutes": 2
}错误
| HTTP 状态码 | code | 说明 |
|---|---|---|
| 404 | not_found | 通话不存在、不属于本应用,或已超过 180 天的保留期 |
结束通话
强制结束一个进行中的通话,例如发现违规内容、业务上的服务时间已到。结束后:
- 在通话中的成员全部离开(
leave_reason为ended),正在振铃的邀请取消,被叫设备上的来电通知被替换为“未接来电”; - 通话的
end_reason为server_ended,ended_via为openapi,ended_by为null; - 成员的在线设备收到通话结束的通知,媒体房间随即关闭,仍连着的设备被断开;
- 与其他原因结束的通话一样,会话中写入通话记录,并发送
rtc.call_ended事件回调。
通话已经结束的,返回 200 OK,changed 为 false,通话不变,可以放心重试。
/{org_name}/{app_name}/calls/{call_id}/end路径参数
org_name、app_name 见接入概述。
| 参数 | 类型 | 说明 |
|---|---|---|
call_id | String | 通话 ID |
请求体
请求体可以省略。
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
reason | String | 否 | 结束的原因,1 到 256 个字符,首尾的空白会被去掉。记入控制台的操作日志,不会告诉用户 |
请求示例
curl -X POST "$IM_API/$ORG/$APP/calls/100311615936659456/end" \
-H "Authorization: Bearer $APP_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"reason": "会议已结束"
}'响应
成功返回 200 OK:
| 字段 | 类型 | 说明 |
|---|---|---|
call | Object | 结束后的通话对象 |
changed | Boolean | 是否结束了通话。通话之前已经结束的为 false |
一个群语音通话被结束,其中 dave 之前已被移出通话:
{
"call": {
"call_id": "100311615936659456",
"type": "group",
"group_id": "100311544667045888",
"media": "audio",
"status": "ended",
"initiator": "alice",
"max_participants": 16,
"joined_count": 0,
"ext": {
"topic": "周会"
},
"created_at": "2026-10-04T19:22:32.940Z",
"answered_at": "2026-10-04T19:22:33.988Z",
"ended_at": "2026-10-04T19:22:38.122Z",
"end_reason": "server_ended",
"ended_by": null,
"duration_seconds": 4,
"version": 6,
"members": [
{
"username": "alice",
"role": "initiator",
"state": "left",
"media_uid": 1,
"invited_by": null,
"ring_expires_at": null,
"joined_at": "2026-10-04T19:22:32.940Z",
"left_at": "2026-10-04T19:22:38.122Z",
"leave_reason": "ended",
"invited_at": null,
"joined_platform": "ios",
"join_count": 1,
"joined_ms": 4134,
"first_joined_at": "2026-10-04T19:22:32.940Z",
"media_joined_at": "2026-10-04T19:22:34.045Z",
"media_left_at": null
},
{
"username": "bob",
"role": "invitee",
"state": "left",
"media_uid": 2,
"invited_by": "alice",
"ring_expires_at": "2026-10-04T19:23:32.940Z",
"joined_at": "2026-10-04T19:22:33.988Z",
"left_at": "2026-10-04T19:22:38.122Z",
"leave_reason": "ended",
"invited_at": "2026-10-04T19:22:32.940Z",
"joined_platform": "android",
"join_count": 1,
"joined_ms": 4134,
"first_joined_at": "2026-10-04T19:22:33.988Z",
"media_joined_at": "2026-10-04T19:22:34.056Z",
"media_left_at": null
},
{
"username": "carol",
"role": "invitee",
"state": "declined",
"media_uid": 3,
"invited_by": "alice",
"ring_expires_at": "2026-10-04T19:23:32.940Z",
"joined_at": null,
"left_at": null,
"leave_reason": null,
"invited_at": "2026-10-04T19:22:32.940Z",
"joined_platform": null,
"join_count": 0,
"joined_ms": 0,
"first_joined_at": null,
"media_joined_at": null,
"media_left_at": null
},
{
"username": "dave",
"role": "joiner",
"state": "left",
"media_uid": 4,
"invited_by": null,
"ring_expires_at": null,
"joined_at": "2026-10-04T19:22:34.030Z",
"left_at": "2026-10-04T19:22:37.089Z",
"leave_reason": "removed",
"invited_at": null,
"joined_platform": "ios",
"join_count": 1,
"joined_ms": 3059,
"first_joined_at": "2026-10-04T19:22:34.030Z",
"media_joined_at": "2026-10-04T19:22:34.067Z",
"media_left_at": null
}
],
"ended_via": "openapi",
"member_count": 4,
"peak_joined": 3,
"billable_minutes": 3
},
"changed": true
}错误
| HTTP 状态码 | code | 说明 |
|---|---|---|
| 400 | invalid_argument | reason 超过 256 个字符,details.field 为 reason |
| 404 | not_found | 通话不存在、不属于本应用,或已超过 180 天的保留期 |
把成员移出通话
把一个成员移出通话,用于群通话中处置个别成员:
- 在通话中的成员:离开通话(
leave_reason为removed),同时被移出媒体房间,不能凭手中的媒体凭据继续收听; - 正在振铃的成员:邀请被取消(
state为canceled),停止振铃; - 其他成员收到成员变化的通知,被移出的人收到本人离开或邀请取消的通知;
- 移出后在通话中的人数降为 0 的,通话随之结束:接通过的
end_reason为completed,没有接通过的为canceled;只剩一人且没有正在振铃的邀请的,60 秒后结束,见群通话; - 一对一通话:移出任何一方都等同结束通话,
end_reason为server_ended。
被移出的成员只要仍能在群里发言,就可以重新加入这个群通话。要阻止他再加入,请同时把他移出群或在群里禁言他。
成员已不在通话中、也不在振铃(如已经离开、拒绝),或者通话已经结束的,返回 200 OK,changed 为 false。
/{org_name}/{app_name}/calls/{call_id}/members/{username}/remove路径参数
org_name、app_name 见接入概述。
| 参数 | 类型 | 说明 |
|---|---|---|
call_id | String | 通话 ID |
username | String | 要移出的成员的用户名 |
请求体
请求体可以省略。
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
reason | String | 否 | 移出的原因,1 到 256 个字符,首尾的空白会被去掉。记入控制台的操作日志,不会告诉用户 |
请求示例
curl -X POST "$IM_API/$ORG/$APP/calls/100311615936659456/members/dave/remove" \
-H "Authorization: Bearer $APP_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"reason": "违规发言,移出通话"
}'响应
成功返回 200 OK,格式与结束通话相同:call 为移出后的通话对象,changed 表示是否有变化。
{
"call": {
"call_id": "100311615936659456",
"type": "group",
"group_id": "100311544667045888",
"media": "audio",
"status": "active",
"initiator": "alice",
"max_participants": 16,
"joined_count": 2,
"ext": {
"topic": "周会"
},
"created_at": "2026-10-04T19:22:32.940Z",
"answered_at": "2026-10-04T19:22:33.988Z",
"ended_at": null,
"end_reason": null,
"ended_by": null,
"duration_seconds": 3,
"version": 5,
"members": [
{
"username": "alice",
"role": "initiator",
"state": "joined",
"media_uid": 1,
"invited_by": null,
"ring_expires_at": null,
"joined_at": "2026-10-04T19:22:32.940Z",
"left_at": null,
"leave_reason": null,
"invited_at": null,
"joined_platform": "ios",
"join_count": 1,
"joined_ms": 0,
"first_joined_at": "2026-10-04T19:22:32.940Z",
"media_joined_at": "2026-10-04T19:22:34.045Z",
"media_left_at": null
},
{
"username": "bob",
"role": "invitee",
"state": "joined",
"media_uid": 2,
"invited_by": "alice",
"ring_expires_at": "2026-10-04T19:23:32.940Z",
"joined_at": "2026-10-04T19:22:33.988Z",
"left_at": null,
"leave_reason": null,
"invited_at": "2026-10-04T19:22:32.940Z",
"joined_platform": "android",
"join_count": 1,
"joined_ms": 0,
"first_joined_at": "2026-10-04T19:22:33.988Z",
"media_joined_at": "2026-10-04T19:22:34.056Z",
"media_left_at": null
},
{
"username": "carol",
"role": "invitee",
"state": "declined",
"media_uid": 3,
"invited_by": "alice",
"ring_expires_at": "2026-10-04T19:23:32.940Z",
"joined_at": null,
"left_at": null,
"leave_reason": null,
"invited_at": "2026-10-04T19:22:32.940Z",
"joined_platform": null,
"join_count": 0,
"joined_ms": 0,
"first_joined_at": null,
"media_joined_at": null,
"media_left_at": null
},
{
"username": "dave",
"role": "joiner",
"state": "left",
"media_uid": 4,
"invited_by": null,
"ring_expires_at": null,
"joined_at": "2026-10-04T19:22:34.030Z",
"left_at": "2026-10-04T19:22:37.089Z",
"leave_reason": "removed",
"invited_at": null,
"joined_platform": "ios",
"join_count": 1,
"joined_ms": 3059,
"first_joined_at": "2026-10-04T19:22:34.030Z",
"media_joined_at": "2026-10-04T19:22:34.067Z",
"media_left_at": null
}
],
"ended_via": null,
"member_count": 4,
"peak_joined": 3,
"billable_minutes": 0
},
"changed": true
}仍在通话中的成员(alice、bob)的 joined_ms 在他们离开时才累计,所以这里为 0。
错误
| HTTP 状态码 | code | 说明 |
|---|---|---|
| 400 | invalid_argument | reason 超过 256 个字符,details.field 为 reason |
| 404 | not_found | 通话不存在、不属于本应用,或已超过 180 天的保留期(“通话不存在”);用户不存在,或不是这个通话的成员(“用户不存在”) |
查询用户正在进行的通话
查询一个用户此刻正在进行的通话和正在振铃的来电,例如在客服系统中显示坐席是否正在通话,或在发起业务操作前确认用户不在通话中。
/{org_name}/{app_name}/users/{username}/calls/active路径参数
org_name、app_name 见接入概述。
| 参数 | 类型 | 说明 |
|---|---|---|
username | String | 用户名 |
请求示例
curl "$IM_API/$ORG/$APP/users/bob/calls/active" \
-H "Authorization: Bearer $APP_TOKEN"响应
成功返回 200 OK:
| 字段 | 类型 | 说明 |
|---|---|---|
current | Object | 用户正在进行的通话,即他在通话中、或他发起后正在等待对方接听的通话,为包括成员的通话对象;不在通话中为 null |
incoming | Array<Object> | 用户正在振铃、还没有超时的来电,最多 20 个,每项为包括成员的通话对象;没有为空数组 |
用户正在收到一个一对一视频来电:
{
"current": null,
"incoming": [
{
"call_id": "100311142651396096",
"type": "single",
"group_id": null,
"media": "video",
"status": "ringing",
"initiator": "alice",
"max_participants": 2,
"joined_count": 1,
"ext": {
"order_id": "8812"
},
"created_at": "2026-10-04T19:20:40.099Z",
"answered_at": null,
"ended_at": null,
"end_reason": null,
"ended_by": null,
"duration_seconds": 0,
"version": 1,
"members": [
{
"username": "alice",
"role": "initiator",
"state": "joined",
"media_uid": 1,
"invited_by": null,
"ring_expires_at": null,
"joined_at": "2026-10-04T19:20:40.099Z",
"left_at": null,
"leave_reason": null,
"invited_at": null,
"joined_platform": "ios",
"join_count": 1,
"joined_ms": 0,
"first_joined_at": "2026-10-04T19:20:40.099Z",
"media_joined_at": null,
"media_left_at": null
},
{
"username": "bob",
"role": "invitee",
"state": "ringing",
"media_uid": 2,
"invited_by": "alice",
"ring_expires_at": "2026-10-04T19:21:40.099Z",
"joined_at": null,
"left_at": null,
"leave_reason": null,
"invited_at": "2026-10-04T19:20:40.099Z",
"joined_platform": null,
"join_count": 0,
"joined_ms": 0,
"first_joined_at": null,
"media_joined_at": null,
"media_left_at": null
}
],
"ended_via": null,
"member_count": 2,
"peak_joined": 1,
"billable_minutes": 0
}
]
}用户既不在通话中、也没有来电时:
{
"current": null,
"incoming": []
}错误
| HTTP 状态码 | code | 说明 |
|---|---|---|
| 404 | not_found | 用户不存在或已删除 |
查询通话统计
查询本应用一段日期内每天的通话统计,与控制台“音视频”页面的统计相同。统计按北京时间划分自然日,通话计入它结束的那一天;当天的数据随通话结束随时更新。
/{org_name}/{app_name}/rtc/stats路径参数
org_name、app_name 见接入概述。
查询参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
from | String | 是 | 开始日期,YYYY-MM-DD |
to | String | 是 | 结束日期,YYYY-MM-DD,包含这一天。不能早于 from,一次最多查询 92 天 |
请求示例
curl "$IM_API/$ORG/$APP/rtc/stats?from=2026-10-01&to=2026-10-05" \
-H "Authorization: Bearer $APP_TOKEN"响应
成功返回 200 OK,items 为每天的统计,按日期升序。没有通话的日期不在其中。
| 字段 | 类型 | 说明 |
|---|---|---|
items[].date | String | 日期,YYYY-MM-DD |
items[].calls | Number | 当天结束的通话数,包括发起时被叫忙线、立即结束的 |
items[].group_calls | Number | 其中的群通话数 |
items[].answered_calls | Number | 其中接通过的通话数。接通率为 answered_calls / calls |
items[].ended_completed | Number | 结束原因为 completed 的通话数 |
items[].ended_canceled | Number | 结束原因为 canceled 的通话数 |
items[].ended_rejected | Number | 结束原因为 rejected 的通话数 |
items[].ended_busy | Number | 结束原因为 busy 的通话数 |
items[].ended_no_answer | Number | 结束原因为 no_answer 的通话数 |
items[].ended_connection_lost | Number | 结束原因为 connection_lost 的通话数 |
items[].ended_other | Number | 其他结束原因的通话数,如 server_ended、user_unavailable、app_unavailable |
items[].duration_ms | Number | 接通过的通话的时长(从接通到结束)合计,毫秒 |
items[].audio_participant_ms | Number | 语音通话中各成员在通话中的时长合计,即成员的 joined_ms 之和,毫秒 |
items[].video_participant_ms | Number | 视频通话中各成员在通话中的时长合计,毫秒 |
items[].billable_audio_minutes | Number | 语音通话的计费分钟数合计 |
items[].billable_video_minutes | Number | 视频通话的计费分钟数合计 |
items[].answer_wait_ms | Number | 接通过的通话从发起到接通的等待时间合计,毫秒。平均接通等待为 answer_wait_ms / answered_calls |
items[].joined_members | Number | 接通过的通话中加入过通话的人次 |
items[].media_missing_members | Number | 其中一直没有连上媒体服务的人次。占 joined_members 的比例明显偏高时,可能是部分用户的网络无法连接媒体服务,请联系我们排查 |
items[].peak_concurrent_calls | Number | 当天同时进行的通话数的峰值,每分钟采样一次,很短的通话可能没有采到 |
items[].peak_at | String | 峰值出现的时间,没有采到为 null |
{
"items": [
{
"date": "2026-10-05",
"calls": 18,
"group_calls": 3,
"answered_calls": 10,
"ended_completed": 5,
"ended_canceled": 4,
"ended_rejected": 1,
"ended_busy": 2,
"ended_no_answer": 1,
"ended_connection_lost": 1,
"ended_other": 4,
"duration_ms": 117736,
"audio_participant_ms": 69425,
"video_participant_ms": 184133,
"billable_audio_minutes": 13,
"billable_video_minutes": 9,
"answer_wait_ms": 11317,
"joined_members": 22,
"media_missing_members": 12,
"peak_concurrent_calls": 2,
"peak_at": "2026-10-04T19:23:22.358Z"
}
]
}date 按北京时间划分,peak_at 等时间是 UTC 时间,所以示例中 2026-10-05 的峰值时间显示为 2026-10-04T19:23:22.358Z(北京时间 10 月 5 日 03:23)。
错误
| HTTP 状态码 | code | 说明 |
|---|---|---|
| 400 | invalid_argument | details.reason 为 invalid_date_range:缺少 from 或 to、格式不是 YYYY-MM-DD、to 早于 from,或超过 92 天 |
独立频道接口
本页只管理 call_id 对应的原通话。业务 channel_id 对应的会议 / 语音房使用频道管理、会话与成员管理和频道统计,不能把频道 ID 传给通话接口。
