@3cx/call-control-sdk

中文文档
0.1.10发布于 2026/08/04MITTypeScriptES Module近 30 天下载 1992

与 3CX 调用控制 API 交互的 SDK

中文使用文档
文档目录16 节
  1. 安装
  2. 快速启动
  3. WebSocket 重新连接
  4. 延期参与者监测
  5. 为什么没有关于扩展参与者的音频?
  6. 事件周期说明
  7. 外出通话
  8. 所附数据
  9. MCP 一体化
  10. API 参考
  11. Participant
  12. CallControlClient
  13. 事件 - 路由点( Own DN)
  14. 事件 - 有监测的扩展
  15. 事件 - 连接
  16. 许可证

@ 3cx/ 呼叫控制- sdk

TypeScript的3CX呼叫控制SDK为3CX呼叫控制API提供了更高级的接口.

安装

npm install @3cx/call-control-sdk

快速启动

import { CallControlClient } from "@3cx/call-control-sdk";

const client = new CallControlClient({
  pbxBase: "https://your-pbx.example.com",
  appId: "your-app-id",
  appSecret: "your-app-secret",
});

await client.connect();

client.on("participantConnected", async (participant) => {
  console.log(`Participant ${participant.id} connected`);

  // Get audio stream from the caller (PCM 8kHz 16-bit mono)
  const audioStream = await participant.getAudioStream();

  // Create audio writer to the caller
  const audioWriter = participant.getAudioWriter();

  // Pipe audio through your AI provider (STT -> Agent -> TTS)
  // ...

  // Write PCM 8kHz 16-bit mono audio back
  audioWriter.write(pcmBuffer);

  // Call actions directly on the participant
  await participant.transfer("1001");
});

client.on("participantDisconnected", (participantId) => {
  // SDK automatically cleans up audio streams/writers
  console.log(`Participant ${participantId} disconnected`);
});

WebSocket 重新连接

在出人意料的断开时,SDK会自动与指数回放连接. 默认的 永不放弃 (单位:千美元)maxReconnects: Infinity) (中文(简体) ).

选项默认说明
websocket.maxReconnectsInfinity放弃前的尝试。 设定一个有限的数字以停止重试 .
websocket.reconnectDelayMs5000第一次重试前的基数延后( 每次尝试都用 Doubles) .
websocket.reconnectBackoffMaxMs120000每回试2分)上盖.

延迟会增加大约5分 10分 20分 达到上限 抓取失败 重新连接使用相同的回放. 限制重试 :

websocket: { maxReconnects: 5 }

延期参与者监测

当扩展附着在应用程序的服务本位( 受控 DNs) 时, SDK 发布单独的事件 用于他们的呼叫活动。 这样你就可以监视那些电话 扩展时不将其与应用程序自己的可编程DN的调用相混为一谈.

// Calls on the app's own DN - full control (audio + call actions)
client.on("participantConnected", async (participant) => {
  console.log(`Own DN call from ${participant.info.party_caller_id}`);
  const audio = await participant.getAudioStream(); // works
  // ...
});

// Calls on monitored extensions - call actions only, no audio
client.on("extensionParticipantConnected", async (participant) => {
  console.log(
    `Extension ${participant.dn} call from ${participant.info.party_caller_id}`,
  );
  // participant.getAudioStream() -> throws Error
  await participant.transfer("1001"); // works
});

client.on("extensionParticipantDisconnected", (participantId) => {
  console.log(`Extension call ${participantId} ended`);
});

为什么没有关于扩展参与者的音频?

音频流和DTMF仅供应用程序自己的可编程DN(RoutePoint)使用. 分机电话由分机自己的设备(IP电话、软电话、, ——媒体路径在PBX和那个设备之间直接走. 可编程 扩展从不为这些电话坐在媒体的道路上,所以没有音频可以截取 或注射 。 调用控制动作( 调用、 调用、 调用、 路由) 在信号中工作 级别; answer() 需要扩展时 supportsDirectControl (中文(简体) ).

事件周期说明

路由点( app's own DN) 自动回答来电 - 参与者 几乎总是以身份到达 Connected这样 participantConnected 立即开火.

扩展功能行为不同: 设备上的第一个调用环( S)Ringing),且仅限 变成 Connected 用户接后. 这个 extensionParticipantConnected 事件 只有在扩展部分实际响应呼叫时才开火. 检测进货( 环行) 呼叫扩展名, 请收听 extensionParticipantUpdated 检查 participant.info.status数字 :

client.on("extensionParticipantUpdated", (participant) => {
  if (participant.info.status === "Ringing") {
    console.log(`Extension ${participant.dn} is ringing`);
    if (participant.supportsDirectControl) {
      void participant.answer(); // uaCSTA leg only
    }
  }
});

多设备扩展获取 每个设备一个参与者腿调用 answer() 编辑 带脚的 supportsDirectControl === true而不是移动/软手机腿上.

能力拥有 DN( RoutePoint)扩展DNA
调用事件participantConnected 编号 : Updated 编号 : DisconnectedextensionParticipantConnected 编号 : Updated 编号 : Disconnected
音频流对没有
自动取款机对没有
传送/ 丢弃对对
变形对对.. Ringing 仅输入
路径到对对.. Ringing 或者说 Connected
答复对仅与 supportsDirectControl (华夏)
附加数据对对

divert() 重定向一个未回复的呼叫——在 Ringing 入境 脚步( 失败) Connected) (中文(简体) ). routeTo() 在参与者留在电话中时增加其他路线: 有效时间 Ringing 或者说 Connected启用 transfer() 已确认的盲转 打电话 也不适用于出入境 Dialing 脚.

外出通话

makeCall 返回直接从 API 响应中创建的参与者ID,所以您 用不着等WebSocket事件来联系到这个电话:

const participantId = await client.makeCall("101");
// or from a specific source DN:
const participantId = await client.makeCall("101", "cctest2");

if (participantId !== undefined) {
  await client.attachPartyData(participantId, { public_ticket: "1234" });
}

PBX 可返回 202 Accepted (电话已接受,id尚未提供)。 这样的话 makeCall 决定: undefined - 通过WebSocket事件追踪电话.

所附数据

将元数据附加到呼叫中 attachParticipantData 编号 : attachPartyData。 。 。 . 每把钥匙 必须是前缀 public_ - PBX 拒绝带有 HTTP 的其他密钥 422。 。 。 .

两种方法都采用 同一参与方的身份证 并撰写两个不同的字段 那个 参与人 记录(同一条呼叫腿)。 没错 没有 瞄准焦距的 DN 或第二个参与者反对:

SDK API/访问器此参与者的 PBX 字段含义
attachParticipantData 编号 : participantDataparticipant_attached_data有关这名参与者自己的腿的数据
attachPartyData 编号 : partyDatacaller_attached_data此段的会场( 相同的参与者 ID) 中的会场数据

两个值都从 受控参与人手柄 ——相机没有 暴露为单独的对象。 需要时随附缔约方关于监测扩展的数据 元数据以活过传输或类似操作.

// via the client (same participantId for both)
await client.attachParticipantData(participantId, { public_ticket: "1234" });
await client.attachPartyData(participantId, { public_peer: "181" });

// or via a participant handle
await participant.attachParticipantData({ public_ticket: "1234" });
await participant.attachPartyData({ public_peer: "181" });

console.log(participant.participantData.public_ticket); // '1234'
console.log(participant.partyData.public_peer); // '181'

await participant.attachPartyData({ public_ticket: "5678" });
await participant.refresh(); // party data is not pushed over WebSocket
console.log(participant.partyData.public_ticket); // '5678'

为了检查另一个DNA的腿,使用 client.getState().callcontrol.get(dn)?.participants (或从 getExtensionParticipantHandle) (中文(简体) ). 没有 getParticipantByDn 帮手.

MCP 一体化

使用带有 3CX MCP 服务器的 SDK 令牌商店 :

const authProvider = client.createMcpAuthProvider();
const mcpUrl = client.getMcpUrl();

const mcpServer = new MCPServerStreamableHttp({
  url: mcpUrl,
  authProvider,
});

API 参考

Participant

方法说明
id数字参与者编号
dnDN 号码 此参与者属于
isExtensionParticipanttrue 如果这是受监视的扩展参与者
info原始 3CX CallParticipant 元数据
destroyed是否清理了参与者
supportsDirectControl密码 direct_control (uACSTA); 所需时间 answer() 扩展时
participantData这条腿 participant_attached_data 通过 attachParticipantData() (单位:千美元){} (无)
partyData剪辑器 caller_attached_data 穿过这条腿 attachPartyData() (单位:千美元){} (无)
getAudioStream()获取可读 PCM 音频流( 仅拥有 DN)
getAudioWriter()获取可写音频频道( 仅拥有 DN)
makeCall(to)从此参与者的 DN 中设置一个新的独立外出呼叫
transfer(destination, reason?)转移到目的地
drop()调用
answer()回答呼叫( Route Point 或 supportsDirectControl 仅限)
routeTo(destination, timeout?, reason?)添加可选择的路由( N)Ringing 或者说 Connected(中文(简体) )
divert(destination, reason?)重定向未回复的呼叫( N)Ringing (仅输入)
transferToVoiceMail(dn, reason?)转入语音信箱
routeToVoiceMail(dn, timeout?, reason?)语音邮件路径
divertToVoiceMail(dn, reason?)切入语音邮件
attachParticipantData(data)写入 participant_attached_data 在这条腿上
attachPartyData(data)写入 caller_attached_data (不是远程DN)
refresh()从 REST API 中获取此腿并更新缓存状态
cancelStreamQueue()取消 PBX 排队的外出音频( 结束活动 POST; 偏好) getAudioWriter().clear() (用于驳船)

CallControlClient

方法说明
connect()连接到 3CX PBX (Auth + WebSocket)
disconnect()断开和清理资源
getParticipantHandle(id)搜索活动 自定义 Participant 由 ID 处理
getExtensionParticipantHandle(id)搜索活动 扩展 Participant 用ID处理( 用于监测的DNA)
getAudioStream(participantId)获取可读 PCM 音频流
createAudioWriter(participantId)创建可写入的音频频道, 并保持保留
transfer(participantId, destination)转会参与者转会至延期
drop(participantId)从呼叫中放弃参与者
answer(participantId)回答呼叫( Route Point 或 direct_control / 仅限 uACSTA )
routeTo(participantId, destination)添加可选择的路由( N)Ringing 或者说 Connected) (中文(简体) ). 没有 timeout ——使用 participant.routeTo() 暂停
divert(participantId, destination)重定向未回复的呼叫( N)Ringing (仅输入)
makeCall(to, from?)发送呼叫 返回创建的参与者ID( HTTP 200) 或 undefined (HTTP 202) (韩语). from 默认为 appId
attachParticipantData(participantId, data)写入 participant_attached_data 在那个参与者的腿上
attachPartyData(participantId, data)写入 caller_attached_data 该参与者的腿上(相同的身份证;而不是远程DNA)
cancelStreamQueue(participantId)取消 PBX 排队的外出音频( 结束活动 POST; 偏好) createAudioWriter().clear() (用于驳船)
controlParticipant(participantId, method, body?)通用呼叫控制动作
getParticipant(id)原始内容 CallParticipant 从状态到 只有应用程序自己的 DN。扩展腿:使用 getExtensionParticipantHandle(id) 或者说 getState().callcontrol.get(dn)?.participants
getState()获取全调控制状态( 所有可见 DN 及其参与者)
getFullInfo()从 REST API 获取完整状态( 不更新缓存)
refreshParticipant(participantId)从 REST API 中获取一个参与者并更新缓存状态 + 控件
createMcpAuthProvider()创建 MCP 认证提供者
getMcpUrl()获取 MCP 端点 URL

事件 - 路由点( Own DN)

在应用程序自己的可编程DN上为参与者被炒作. 全能控制:音频流,DTMF,和调用动作.

活动有效载荷说明
participantConnectedParticipant连接到呼叫的参与者
participantDisconnectednumber取消召集
participantUpdatedParticipant参加国已更改
dtmfstring, numberDTMF 收到的数字和按键的参与者

事件 - 有监测的扩展

在服务校长的延期上为与会者解雇。 仅调用控制 - 没有音频或DTMF(媒体路径由扩展自己的设备处理,而不是由可编程扩展处理).

活动有效载荷说明
extensionParticipantConnectedParticipant连接扩展参与者
extensionParticipantDisconnectednumber延期参与者已删除
extensionParticipantUpdatedParticipant扩展参与者状态已更改

事件 - 连接

活动有效载荷说明
connected- 说吧已连接 WebSocket
disconnected- 说吧WebSocket 已断开
errorError发生错误

许可证

麻省理工学院

版本与发布记录

@3cx/call-control-sdk 共发布 4 个版本,首次发布于 2026/07/29;过去 12 个月发布了 4 个版本。

版本发布日期距今
0.1.10latest2026/08/043 个月前
0.1.92026/07/303 个月前
0.1.82026/07/293 个月前
0.1.72026/07/293 个月前

关于 @3cx/call-control-sdk 的常见问题

@3cx/call-control-sdk 是什么?

与 3CX 调用控制 API 交互的 SDK

如何安装 @3cx/call-control-sdk?

在项目目录运行 npm install @3cx/call-control-sdk,或使用 pnpm add / yarn add / bun add。

@3cx/call-control-sdk 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@3cx/call-control-sdk 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@3cx/call-control-sdk 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=24.0.0。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@3cx/call-control-sdk 可以商用吗?

@3cx/call-control-sdk 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@3cx/call-control-sdk 的下载量有多少?

根据 npm 官方下载统计,@3cx/call-control-sdk 在 2026/10/05 – 2026/11/03 这 30 天内被下载 1,992 次,日均约 66 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@3cx/call-control-sdk 还在维护吗?

最新版本 0.1.10 发布于 2026/08/04(3 个月前);至今共发布 4 个版本,其中过去 12 个月发布了 4 个;首次发布于 2026/07/29。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。