C cenaly.com
免费开始

自有电话系统(PBX)的 API 与 MCP

密钥与权限、REST API、适用于 Claude 和 Cursor 的 MCP 服务器、通过 OAuth 免密钥连接 ChatGPT 和 claude.ai、示例、限额与错误

5 min read 管理后台演示
On this page 10

自有 PBX 的 API 与 MCP

自有 PBX Cenaly 面向您的程序和 AI 助手开放:

  • CRM、记账系统、自有网站 — REST API:通话记录、录音与转写、点击呼叫、分机、转接、呼叫规则;
  • Claude Code、Claude Desktop、Cursor — 通过密钥连接远程 MCP 服务器;
  • ChatGPT、claude.ai、Perplexity、Cursor 和 VS Code — 同一个 MCP 服务器,无需密钥:登录自己的账户并授权访问(OAuth)。

一切操作均以账户所有者名义执行,且仅限其授予的权限。每项变更都会显示在 PBX 操作日志中,并标注密钥或应用名称。

地址#

项目 地址
REST API https://api.cenaly.com/pbx-api/v1
MCP 服务器(streamable HTTP) https://api.cenaly.com/pbx-api/mcp
方法文档 api.cenaly.com/pbx-api/v1/docs
OpenAPI 3.1 描述 api.cenaly.com/pbx-api/v1/openapi.json
OAuth 元数据 https://api.cenaly.com/.well-known/oauth-authorization-server/pbx-api

密钥与权限#

密钥仅由账户所有者创建:电话 → API 与 MCP → 密钥 → 创建密钥。拥有“PBX 管理员”角色的员工看不到密钥——密钥以所有者名义生效。

  • cpx_… 形式的密钥只显示一次——创建后立即显示,并附带可直接粘贴的连接片段。我们只保存其指纹(SHA-256),密钥无法恢复——只能重新签发。
  • 密钥可限制门店(否则为全部门店,包括未来新增的)和有效期(30、90、365 天或永久)。
  • **“权限”**可即时更改密钥的权限和门店:集成继续运行,新权限从下一个请求起生效。
  • “撤销”——立即且永久:使用该密钥的下一个请求将收到 401。
  • 每个账户的有效密钥和连接最多 20 个。

权限可通过预设组合或勾选选择:

权限 作用 组合
calls:read 通话记录及含路由的通话详情 CRM、AI 助手、只读、全部
recordings:read 通话录音链接(10 分钟)和转写 CRM、AI 助手、只读、全部
calls:dial 点击呼叫:先呼叫员工电话,接听后 PBX 再拨号 CRM、AI 助手、全部
extensions:read 分机、群组、转接、“免打扰” CRM、AI 助手、只读、全部
extensions:write 更改个人转接和“免打扰” AI 助手、全部
presence:read 谁空闲、通话中、正在交谈 AI 助手、只读、全部
reports:read 某时段的呼叫中心指标:服务水平、等待、未接、AHT、FCR、CSAT、在哪里丢失来电,按队列、线路、员工的细分 只读、全部
routes:read 号码、呼入和呼出规则、规则历史 只读、全部
routes:write 创建、更改、删除和调整呼叫规则顺序,从历史回滚 仅单独勾选
config:export 以文件格式导出 PBX 设置 仅单独勾选
config:import 从表格导入设置:先预览,再应用 仅单独勾选
webhooks:write 创建、更改、测试和删除通话事件 webhook 仅单独勾选
daynight:write 将门店关闭至当日结束或指定日期,开启同理,等同于电话上的 *28 仅单独勾选

会更改内容或消耗分钟的权限在窗口中用琥珀色标记。最后三项权限不属于任何组合,甚至不在“全部”中——只能通过单独勾选有意识地授予。

ChatGPT、claude.ai、Perplexity、Cursor、VS Code:无需密钥连接#

云端助手无法将密钥插入请求头——它们通过 OAuth 连接 MCP 服务器。无需创建密钥。

**claude.ai:**设置 → 连接器 → 添加自定义连接器 → 粘贴 https://api.cenaly.com/pbx-api/mcp → 连接。

**ChatGPT:**设置 → 应用与连接器 → 高级 → 启用开发者模式 → 创建 → 粘贴 https://api.cenaly.com/pbx-api/mcp,身份验证——OAuth。

**Perplexity:**账户设置 → 连接器 → “+ 自定义连接器” → 远程 → 粘贴 https://api.cenaly.com/pbx-api/mcp,身份验证——OAuth。

**Cursor 和 VS Code:**添加地址为 https://api.cenaly.com/pbx-api/mcp 的远程(HTTP)MCP 服务器,不带密钥请求头——登录窗口将在浏览器中打开。在 Cursor 中:.cursor/mcp.json → {"mcpServers": {"cenaly-pbx": {"url": "https://api.cenaly.com/pbx-api/mcp"}}};在 VS Code 中:命令“MCP: Add Server” → HTTP。

随后助手将打开 Cenaly 登录窗口。登录后您会看到:

  • 谁在请求访问——应用按回调地址命名:“已验证连接器 · chatgpt.com”(claude.ai、perplexity.ai、vscode.dev、Cursor 同理)、“此计算机上的程序”或警告“未知地址”。应用发送的名称以小字显示——任何人都可以填写;
  • 权限——勾选应用所请求的内容(如果它请求“全部”,则勾选“AI 助手”组合);“仅单独勾选”的权限从不预先勾选;
  • 门店——全部或所选门店。

“允许”会将您带回助手,它立即就能看到 PBX 工具。连接会出现在电话 → API 与 MCP → 密钥中,带 OAuth 标记和应用地址——在那里也可更改权限和撤销访问。同一应用重复连接会更新其权限,而不会创建第二个连接。

只有账户所有者可以批准连接。

Claude Code、Claude Desktop、Cursor:通过密钥连接#

Claude Code — 一条命令:

claude mcp add --transport http telephony https://api.cenaly.com/pbx-api/mcp --header "Authorization: Bearer cpx_…"

Claude Desktop、Cursor(.cursor/mcp.json)及其他带配置文件的客户端:

{
  "mcpServers": {
    "telephony": {
      "type": "http",
      "url": "https://api.cenaly.com/pbx-api/mcp",
      "headers": { "Authorization": "Bearer cpx_…" }
    }
  }
}

创建密钥后立即显示带有密钥的现成插入内容。AI 助手只能看到密钥允许的工具,可以回答“今天谁打来但没接通”“用安娜的电话回拨这位客户”“帮我开启呼叫转移到手机”。

REST API#

密钥放在请求头 Authorization: Bearer <密钥>(或 X-Api-Key)。如果密钥只有一个门店,可以不传 location 参数;如果有多个,响应 400 location_required 会列出它们。

方法 路径 权限 作用
GET /v1/me — 密钥的权限、门店和限额
GET /v1/locations — 密钥的门店及其 PBX
GET /v1/calls calls:read 通话记录:since、until、direction、status(answered、missed、ai)、number、extension、limit 最多 100、cursor
GET /v1/calls/{id} calls:read 带路由的通话:触发了哪条规则、谁的电话响了、谁接听了
GET /v1/calls/{id}/recording recordings:read 有效期 10 分钟的录音链接
GET /v1/calls/{id}/transcript recordings:read 通话转写和摘要
POST /v1/dial calls:dial 点击呼叫:{ "extension": "101", "number": "+995555123456" }
GET /v1/dial/{commandId} calls:dial 点击呼叫的进度
GET /v1/extensions extensions:read 分机、组、呼叫转移、“免打扰”
PUT /v1/extensions/{ext}/forwarding extensions:write 分机的个人呼叫转移
PUT /v1/extensions/{ext}/dnd extensions:write “免打扰”:{ "on": true }
GET /v1/presence presence:read 当前谁空闲
GET /v1/metrics reports:read 某时段的呼叫中心指标:from、to(门店本地日期 YYYY-MM-DD,默认最近 7 天)、compare(prev、year、prev,year 或留空)、sections(metrics,loss,queues,lines,operators,directions,heatmap,daily)、callbacks=true — 还包括回拨速度
GET /v1/routes routes:read 号码、呼入和呼出规则
GET /v1/routes/history routes:read 规则变更历史
POST、PUT、DELETE /v1/routes/inbound…、/v1/routes/outbound… routes:write 逐条处理通话规则:创建、修改传入字段、删除、重排、回滚
GET、POST /v1/config/export、/v1/config/import config:export、config:import 文件格式的设置;上传 — 预览,然后应用
GET /v1/events calls:read 门店 7 天内的通话事件(最多 2000):since(事件 id 或时间)、types、limit(每次响应最多 500)
GET、POST、PATCH、DELETE /v1/webhooks… webhooks:write Webhook:列表、创建、修改、删除;…/rotate-secret、…/test、…/deliveries
GET、PUT /v1/open-closed routes:read、daynight:write 是否已关闭及关闭到何时;关闭到当天结束 { "closed": true },关闭到某日期 { "closed": true, "until": "2026-10-05T09:00" },或开启

字段的完整说明见文档页面和 OpenAPI。

今天谁打来但没接通:

curl -H "Authorization: Bearer cpx_…" \
  "https://api.cenaly.com/pbx-api/v1/calls?status=missed&since=$(date -u +%Y-%m-%dT00:00:00Z)"

用分机 101 的电话回拨客户:

curl -X POST -H "Authorization: Bearer cpx_…" -H "Content-Type: application/json" \
  -d '{"extension":"101","number":"+995555123456"}' "https://api.cenaly.com/pbx-api/v1/dial"

九月的指标并与八月和去年九月对比 — 用于自己的 BI 面板:

curl -H "Authorization: Bearer cpx_…" \
  "https://api.cenaly.com/pbx-api/v1/metrics?from=2026-09-01&to=2026-09-30&compare=prev,year"

响应中的 metrics — 与 电话 → 通话记录 → 分析 屏幕上相同的数字:呼入、已接听和未接听、服务水平(阈值和目标 — 来自队列设置)、平均和最长等待、未接通即挂断占比、AHT、FCR、转接和机器人占比、CSAT。compare.prev 和 compare.year — 上一时段和去年同期相同的字段。与去年同期的对比仅对不超过 400 天的时段计算。

如果员工 15 秒内未接听,则开启呼叫转移到手机:

curl -X PUT -H "Authorization: Bearer cpx_…" -H "Content-Type: application/json" \
  -d '{"forward":{"noAnswer":{"kind":"number","number":"+995599000111"},"noAnswerSec":15}}' \
  "https://api.cenaly.com/pbx-api/v1/extensions/101/forwarding"

MCP 工具#

与 REST 相同的操作。客户端只能看到密钥或连接所允许的工具:

工具 权限
list_locations —
list_calls、get_call calls:read
get_call_recording、get_call_transcript recordings:read
call_number、get_dial_status calls:dial
list_extensions extensions:read
set_forwarding、set_do_not_disturb extensions:write
get_presence presence:read
get_call_metrics reports:read
list_routes、list_route_history routes:read
save_inbound_rule、delete_inbound_rule、move_inbound_rule、save_outbound_rule、delete_outbound_rule、move_outbound_rule、set_outbound_calls、restore_route_version routes:write
export_config config:export
import_config config:import
get_call_events calls:read
list_webhooks、save_webhook、delete_webhook、rotate_webhook_secret、test_webhook、list_webhook_deliveries webhooks:write
get_open_closed routes:read
set_open_closed daynight:write

服务器无需会话即可运行(streamable HTTP、JSON-RPC 2.0):initialize、tools/list、tools/call。

为自建连接器开发者提供的 OAuth#

授权服务器遵循 MCP 授权规范——任何支持该规范的客户端均可使用:

  1. 不带令牌请求 MCP 会收到 401,并带有响应头 WWW-Authenticate: Bearer resource_metadata="https://api.cenaly.com/.well-known/oauth-protected-resource/pbx-api/mcp"。
  2. 资源元数据(RFC 9728)指明授权服务器——https://api.cenaly.com/pbx-api;其元数据(RFC 8414)位于 https://api.cenaly.com/.well-known/oauth-authorization-server/pbx-api(以及 …/pbx-api/.well-known/openid-configuration)。
  3. 客户端动态注册(RFC 7591):POST /pbx-api/oauth/register。回调地址只能是 localhost / 127.0.0.1(任意端口)或已知连接器的地址(claude.ai、claude.com、chatgpt.com、chat.openai.com、vscode.dev、www.perplexity.ai、enterprise.perplexity.ai;Cursor 还额外支持恰好为 cursor://anysphere.cursor-mcp/oauth/callback 的地址)。客户端登录方式为 none(公共客户端)、client_secret_post 或 client_secret_basic。
  4. 带 PKCE 的授权码(仅 S256):GET /pbx-api/oauth/authorize → 所有者登录与同意窗口 → 携带 code、state 和 iss 返回你的地址。参数 resource 为 MCP 服务器地址。
  5. 令牌: POST /pbx-api/oauth/token。授权码一次性有效,有效期为 2 分钟(错误的 code_verifier 会使其失效);访问令牌有效期为 1 小时;刷新令牌有效期为 90 天,每次刷新都会签发新的刷新令牌,旧的随即失效。
  6. 撤销: POST /pbx-api/oauth/revoke(RFC 7009)会整体终止连接——等同于“API 和 MCP”窗口中的“撤销”。

访问令牌在所有使用密钥的地方均被接受:MCP 和 REST 皆可,并带有所有者所授予的权限和门店范围。

限制与错误#

  • 每个密钥或连接:每分钟 120 次请求,每天 20 000 次(REST 与 MCP 合计)。
  • 点击呼叫单独计算:每分钟 10 次,每天 300 次。
  • 日志——每次请求最多 100 条通话;门店的 PBX 日志——最近 90 天内的最近 400 条通话。
代码 错误 处理方法
401 api_key_missing、api_key_invalid 密钥不存在、已被撤销或已过期;OAuth 令牌已过期——请刷新
403 scope_required 密钥缺少权限——请在“权限”中添加
400 location_required 密钥关联多个门店——请传入 location
404 location_not_found 门店不存在,或密钥受限于其他门店
429 rate_limited 已达限额,请在 Retry-After 秒后重试

通话事件#

实时 — 电话系统 Webhook:电话 → API 和 MCP → Webhook,或使用具有 webhooks:write 权限的密钥调用 POST /v1/webhooks。每个事件都会在几秒内以 POST 请求发送到您的 https 地址:call.started(已开始:方向、主叫方、被叫方、线路)、call.ringing(哪部电话在响——每部电话各一条)、call.answered(谁接听了)、call.transferred(谁转给了谁)、call.ended(结果、时长、通话内容、谁接听的)。Webhook 可按门店和事件类型进行限制。

{ "id": "1727771234.56:2", "type": "call.answered", "createdAt": "2026-10-01T10:00:05Z",
  "locationId": "…", "webhookId": "wh_…", "attempt": 1,
  "data": { "callId": "1727771234.56", "seq": 2, "direction": "inbound",
            "from": { "number": "+995555123456" }, "party": { "extension": "101", "name": "Анна" } } }

签名 — 请求头 X-Cenaly-Signature: t=<unix 时间>,v1=<用 Webhook 密钥对 "t.请求体" 计算的 HMAC-SHA256>。请重新计算,以恒定时间比较,并拒绝超过 5 分钟的 t。密钥 whsec_… 仅显示一次;“新密钥”会立即废止旧密钥。

投递 — 请在 10 秒内返回 2xx。否则我们会以 10 秒、30 秒、2、5、10、15 和 15 分钟的间隔重试(共 8 次尝试,约一小时);重试携带相同的 id——据此丢弃重复项。投递日志(响应码、时间、尝试次数)位于 Webhook 窗口和 GET /v1/webhooks/{id}/deliveries;“检查”会发送一条服务性 webhook.ping。地址连续 3 天未接受任何投递——Webhook 会自动关闭:所有者会收到邮件,“Webhook”窗口中会显示“已自动关闭”标记和“启用”按钮(或使用 { "enabled": true } 调用 PATCH /v1/webhooks/{id})。

没有自己的服务器 — 通过轮询获取同一事件流:GET /v1/events?since=<最后事件的 id> 或 MCP 工具 get_call_events(门店最近 7 天的事件,最多 2000 条;每次响应最多 500 条,more: true 表示用相同的 since 继续翻页)。

录音处理完成后 — 带有转写和分析的通话会通过“Webhook”来源的 Webhook 送达,位于“通话”部分。

“已关闭 / 已开放” — 使用 { "closed": true } 调用 PUT /v1/open-closed 会将门店关闭至当日结束(如同电话上的 *28:来电按“非工作时间”规则处理),false 则开放;GET 显示是否已关闭以及由谁关闭。使用 "until" 时——关闭至该时刻(不超过 30 天),之后会自动开放:不带时区偏移的时间(2026-10-05T09:00)按门店时区解读;false 也会取消该设置。需要在路由中勾选“用代码 *28 关闭”(否则返回 409)。

安全#

  • 密钥仅显示一次,我们只以指纹形式存储;泄露的密钥可通过一个按钮撤销。
  • OAuth 连接仅限所有者授权,并且可以看到访问权限授予了哪个地址;已授权的连接可按权限和门店进行限制,并可随时撤销。
  • 电话号码是您客户的个人数据:仅向确实需要这些数据的集成授予日志和录音权限。
  • 通过密钥或连接进行的所有更改都会记录在电话系统操作日志中,并注明密钥或应用名称。
Was this article helpful?