C cenaly.com
Bắt đầu miễn phí

API và MCP của tổng đài riêng

Khóa và quyền hạn, REST API, máy chủ MCP cho Claude và Cursor, kết nối ChatGPT và claude.ai qua OAuth không cần khóa, ví dụ, giới hạn và mã lỗi

15 min read Bản demo trang quản trị
On this page 10

API và MCP của tổng đài riêng

Tổng đài riêng Cenaly mở cho các chương trình và trợ lý AI của bạn:

  • CRM, hệ thống kế toán, website riêng — REST API: nhật ký cuộc gọi, ghi âm và bản chép lời, gọi một cú nhấp, số máy lẻ, chuyển tiếp, quy tắc cuộc gọi;
  • Claude Code, Claude Desktop, Cursor — máy chủ MCP từ xa bằng khóa;
  • ChatGPT, claude.ai, Perplexity, Cursor và VS Code — cùng máy chủ MCP đó không cần khóa: bạn đăng nhập vào tài khoản của mình và cấp quyền truy cập (OAuth).

Mọi thứ hoạt động dưới danh nghĩa chủ tài khoản và chỉ với các quyền mà chủ tài khoản đã cấp. Mỗi thay đổi đều hiển thị trong nhật ký hành động của tổng đài kèm tên khóa hoặc ứng dụng.

Địa chỉ#

Mục Địa chỉ
REST API https://api.cenaly.com/pbx-api/v1
Máy chủ MCP (streamable HTTP) https://api.cenaly.com/pbx-api/mcp
Tài liệu về các phương thức api.cenaly.com/pbx-api/v1/docs
Mô tả OpenAPI 3.1 api.cenaly.com/pbx-api/v1/openapi.json
Siêu dữ liệu OAuth https://api.cenaly.com/.well-known/oauth-authorization-server/pbx-api

Khóa và quyền#

Chỉ chủ tài khoản tạo được khóa: Điện thoại → API và MCP → Khóa → Tạo khóa. Nhân viên có vai trò «Quản trị viên tổng đài» không thấy khóa — khóa hoạt động dưới danh nghĩa chủ tài khoản.

  • Khóa dạng cpx_… chỉ hiển thị một lần — ngay sau khi tạo, cùng với các đoạn mã sẵn sàng để dán vào kết nối. Chúng tôi chỉ lưu dấu vân tay của khóa (SHA-256), không thể khôi phục khóa — chỉ có thể phát hành khóa mới.
  • Có thể giới hạn khóa theo chi nhánh (nếu không — tất cả chi nhánh, kể cả chi nhánh trong tương lai) và theo thời hạn (30, 90, 365 ngày hoặc vô thời hạn).
  • «Quyền» thay đổi quyền và chi nhánh của khóa ngay lập tức: tích hợp vẫn tiếp tục hoạt động, các quyền mới có hiệu lực từ yêu cầu tiếp theo.
  • «Thu hồi» — ngay lập tức và vĩnh viễn: yêu cầu tiếp theo với khóa này sẽ nhận 401.
  • Số khóa và kết nối đang hoạt động — tối đa 20 trên mỗi tài khoản.

Quyền được chọn theo bộ có sẵn hoặc theo từng ô tích:

Quyền Cấp những gì Bộ
calls:read nhật ký cuộc gọi và thẻ cuộc gọi kèm lộ trình CRM, trợ lý AI, chỉ đọc, tất cả
recordings:read liên kết đến ghi âm cuộc trò chuyện (10 phút) và bản chép lời CRM, trợ lý AI, chỉ đọc, tất cả
calls:dial gọi một cú nhấp: trước tiên điện thoại của nhân viên đổ chuông, sau khi trả lời tổng đài quay số CRM, trợ lý AI, tất cả
extensions:read số máy lẻ, nhóm, chuyển tiếp, «không làm phiền» CRM, trợ lý AI, chỉ đọc, tất cả
extensions:write thay đổi chuyển tiếp cá nhân và «không làm phiền» trợ lý AI, tất cả
presence:read ai đang rảnh, đang gọi, đang trò chuyện trợ lý AI, chỉ đọc, tất cả
reports:read số liệu tổng đài trong kỳ: mức độ phục vụ, thời gian chờ, cuộc gọi bị bỏ, AHT, FCR, CSAT, nơi mất cuộc gọi, phân tích theo hàng đợi, đường dây, nhân viên chỉ đọc, tất cả
routes:read số điện thoại, quy tắc cuộc gọi đến và đi, lịch sử quy tắc chỉ đọc, tất cả
routes:write tạo, thay đổi, xóa và sắp xếp lại quy tắc cuộc gọi, khôi phục từ lịch sử chỉ bằng ô tích riêng
config:export xuất cài đặt tổng đài ra tệp chỉ bằng ô tích riêng
config:import tải cài đặt từ bảng: xem trước, sau đó áp dụng chỉ bằng ô tích riêng
webhooks:write tạo, thay đổi, kiểm tra và xóa webhook sự kiện cuộc gọi chỉ bằng ô tích riêng
daynight:write đóng chi nhánh đến hết ngày hoặc đến một ngày cụ thể, mở lại — giống như *28 từ điện thoại chỉ bằng ô tích riêng

Các quyền có thể thay đổi điều gì đó hoặc tiêu tốn phút được đánh dấu màu hổ phách trong cửa sổ. Ba quyền cuối không thuộc bất kỳ bộ nào, kể cả «Tất cả» — chỉ có thể cấp một cách có ý thức, bằng ô tích riêng.

ChatGPT, claude.ai, Perplexity, Cursor, VS Code: kết nối không cần khóa#

Các trợ lý đám mây không biết chèn khóa vào tiêu đề — chúng kết nối máy chủ MCP qua OAuth. Không cần tạo khóa.

claude.ai: Cài đặt → Trình kết nối → Thêm trình kết nối của bạn → dán https://api.cenaly.com/pbx-api/mcp → Kết nối.

ChatGPT: Cài đặt → Ứng dụng và trình kết nối → Nâng cao → bật chế độ nhà phát triển → Tạo → dán https://api.cenaly.com/pbx-api/mcp, xác thực — OAuth.

Perplexity: Cài đặt tài khoản → Trình kết nối → «+ Trình kết nối của bạn» → Từ xa → dán https://api.cenaly.com/pbx-api/mcp, xác thực — OAuth.

Cursor và VS Code: thêm máy chủ MCP từ xa (HTTP) với địa chỉ https://api.cenaly.com/pbx-api/mcp không có tiêu đề chứa khóa — cửa sổ đăng nhập sẽ mở trong trình duyệt. Trong Cursor: .cursor/mcp.json → {"mcpServers": {"cenaly-pbx": {"url": "https://api.cenaly.com/pbx-api/mcp"}}}; trong VS Code: lệnh «MCP: Add Server» → HTTP.

Sau đó trợ lý sẽ mở cửa sổ đăng nhập Cenaly. Sau khi đăng nhập bạn sẽ thấy:

  • ai đang yêu cầu quyền truy cập — ứng dụng được gọi tên theo địa chỉ mà phản hồi sẽ trả về: «Trình kết nối đã xác minh · chatgpt.com» (tương tự — claude.ai, perplexity.ai, vscode.dev, Cursor), «Chương trình trên máy tính này» hoặc cảnh báo «Địa chỉ không quen thuộc». Tên mà ứng dụng gửi đến được hiển thị nhỏ — bất kỳ ai cũng có thể viết tên đó;
  • quyền — những gì ứng dụng yêu cầu được đánh dấu (nếu ứng dụng yêu cầu «mọi thứ», bộ «Trợ lý AI» được đánh dấu); các quyền «chỉ bằng ô tích riêng» không bao giờ được đánh dấu trước;
  • chi nhánh — tất cả hoặc những chi nhánh được chọn.

«Cho phép» đưa bạn trở lại trợ lý, và trợ lý ngay lập tức thấy các công cụ của tổng đài. Kết nối xuất hiện trong Điện thoại → API và MCP → Khóa dưới dạng một dòng có nhãn OAuth và địa chỉ ứng dụng — tại đó cũng có thể thay đổi quyền và thu hồi quyền truy cập. Kết nối lại cùng một ứng dụng sẽ cập nhật quyền của ứng dụng đó, chứ không tạo kết nối thứ hai.

Chỉ chủ tài khoản mới có thể cho phép kết nối.

Claude Code, Claude Desktop, Cursor: kết nối bằng khóa#

Claude Code — bằng một lệnh:

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

Claude Desktop, Cursor (.cursor/mcp.json) và các ứng dụng khác có tệp cấu hình:

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

Các đoạn mã mẫu sẵn sàng với khóa của bạn sẽ hiển thị ngay sau khi tạo khóa. Trợ lý AI chỉ thấy những công cụ được khóa cho phép và có thể trả lời cho các câu hỏi như “hôm nay ai đã gọi mà không liên lạc được”, “gọi lại cho vị khách này từ số điện thoại của Anna”, “bật cho tôi chuyển tiếp đến số di động”.

REST API#

Khóa — trong tiêu đề Authorization: Bearer <ключ> (hoặc X-Api-Key). Nếu khóa có một chi nhánh, có thể không truyền tham số location; nếu có nhiều chi nhánh — phản hồi 400 location_required sẽ liệt kê chúng.

Phương thức Đường dẫn Quyền Chức năng
GET /v1/me — quyền, chi nhánh và giới hạn của khóa
GET /v1/locations — các chi nhánh của khóa và tổng đài của chúng
GET /v1/calls calls:read nhật ký cuộc gọi: since, until, direction, status (answered, missed, ai), number, extension, limit tối đa 100, cursor
GET /v1/calls/{id} calls:read cuộc gọi kèm lộ trình: quy tắc nào đã kích hoạt, số điện thoại của ai đã đổ chuông, ai đã trả lời
GET /v1/calls/{id}/recording recordings:read liên kết đến ghi âm trong 10 phút
GET /v1/calls/{id}/transcript recordings:read bản gỡ băng và tóm tắt cuộc trò chuyện
POST /v1/dial calls:dial gọi một chạm: { "extension": "101", "number": "+995555123456" }
GET /v1/dial/{commandId} calls:dial tiến trình cuộc gọi một chạm
GET /v1/extensions extensions:read số nội bộ, nhóm, chuyển tiếp, “không làm phiền”
PUT /v1/extensions/{ext}/forwarding extensions:write chuyển tiếp cá nhân của số nội bộ
PUT /v1/extensions/{ext}/dnd extensions:write “không làm phiền”: { "on": true }
GET /v1/presence presence:read ai đang rảnh ngay bây giờ
GET /v1/metrics reports:read chỉ số tổng đài trong kỳ: from, to (ngày địa phương của chi nhánh YYYY-MM-DD, mặc định — 7 ngày gần nhất), compare (prev, year, prev,year hoặc để trống), sections (metrics,loss,queues,lines,operators,directions,heatmap,daily), callbacks=true — thêm cả tốc độ gọi lại
GET /v1/routes routes:read số điện thoại, quy tắc cuộc gọi đến và đi
GET /v1/routes/history routes:read lịch sử thay đổi quy tắc
POST, PUT, DELETE /v1/routes/inbound…, /v1/routes/outbound… routes:write quy tắc cuộc gọi từng cái một: tạo, sửa các trường được truyền, xóa, sắp xếp lại, khôi phục
GET, POST /v1/config/export, /v1/config/import config:export, config:import cài đặt ở định dạng tệp; tải lên — xem trước, sau đó áp dụng
GET /v1/events calls:read sự kiện cuộc gọi của chi nhánh trong 7 ngày (tối đa 2000): since (id sự kiện hoặc thời gian), types, limit (tối đa 500 mỗi phản hồi)
GET, POST, PATCH, DELETE /v1/webhooks… webhooks:write webhook: danh sách, tạo, sửa, xóa; …/rotate-secret, …/test, …/deliveries
GET, PUT /v1/open-closed routes:read, daynight:write đã đóng hay chưa và đến khi nào; đóng đến hết ngày { "closed": true }, đến ngày { "closed": true, "until": "2026-10-05T09:00" } hoặc mở lại

Mô tả đầy đủ các trường — trên trang tài liệu và trong OpenAPI.

Ai đã gọi hôm nay mà không liên lạc được:

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

Gọi lại cho khách hàng từ số điện thoại của số nội bộ 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"

Chỉ số tháng 9 với so sánh tháng 8 và tháng 9 năm ngoái — cho bảng BI của riêng bạn:

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

Trong phản hồi metrics — cùng những con số như trên màn hình Điện thoại → Nhật ký cuộc gọi → Phân tích: cuộc gọi đến, đã nhận và bị nhỡ, mức độ phục vụ (ngưỡng và mục tiêu — từ cài đặt hàng đợi), thời gian chờ trung bình và tối đa, tỷ lệ bỏ máy không tính cuộc gọi ngắn, AHT, FCR, tỷ lệ chuyển tiếp và bot, CSAT. compare.prev và compare.year — cùng các trường đó cho kỳ trước và cùng kỳ năm ngoái. So sánh với năm ngoái được tính cho các kỳ đến 400 ngày.

Bật chuyển tiếp đến số di động nếu nhân viên không trả lời trong 15 giây:

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"

Công cụ MCP#

Các thao tác tương tự như REST. Ứng dụng khách chỉ thấy những công cụ được cho phép bởi khóa hoặc kết nối:

Công cụ Quyền
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

Máy chủ hoạt động không cần phiên (streamable HTTP, JSON-RPC 2.0): initialize, tools/list, tools/call.

OAuth cho nhà phát triển tự tạo trình kết nối#

Máy chủ ủy quyền tuân theo đặc tả ủy quyền MCP — bất kỳ ứng dụng khách nào hỗ trợ đặc tả này đều dùng được:

  1. Yêu cầu đến MCP không có token sẽ nhận 401 kèm tiêu đề WWW-Authenticate: Bearer resource_metadata="https://api.cenaly.com/.well-known/oauth-protected-resource/pbx-api/mcp".
  2. Siêu dữ liệu tài nguyên (RFC 9728) chỉ ra máy chủ ủy quyền — https://api.cenaly.com/pbx-api; siêu dữ liệu của nó (RFC 8414) nằm tại https://api.cenaly.com/.well-known/oauth-authorization-server/pbx-api (và …/pbx-api/.well-known/openid-configuration).
  3. Đăng ký động ứng dụng khách (RFC 7591): POST /pbx-api/oauth/register. Địa chỉ chuyển hướng — chỉ localhost / 127.0.0.1 (mọi cổng) hoặc địa chỉ của trình kết nối đã biết (claude.ai, claude.com, chatgpt.com, chat.openai.com, vscode.dev, www.perplexity.ai, enterprise.perplexity.ai; với Cursor — thêm đúng cursor://anysphere.cursor-mcp/oauth/callback). Phương thức đăng nhập của ứng dụng khách — none (ứng dụng khách công khai), client_secret_post hoặc client_secret_basic.
  4. Mã ủy quyền với PKCE (chỉ S256): GET /pbx-api/oauth/authorize → cửa sổ đăng nhập và đồng ý của chủ sở hữu → chuyển hướng về địa chỉ của bạn kèm code, state và iss. Tham số resource — địa chỉ của máy chủ MCP.
  5. Token: POST /pbx-api/oauth/token. Mã chỉ dùng một lần và có hiệu lực 2 phút (code_verifier sai sẽ vô hiệu hóa mã); token truy cập — 1 giờ; token làm mới — 90 ngày, mỗi lần làm mới sẽ cấp token mới, token cũ không còn hiệu lực.
  6. Thu hồi: POST /pbx-api/oauth/revoke (RFC 7009) vô hiệu hóa toàn bộ kết nối — giống nút “Thu hồi” trong cửa sổ “API và MCP”.

Token truy cập được chấp nhận ở mọi nơi dùng khóa: cả trong MCP lẫn REST, với các quyền và chi nhánh mà chủ sở hữu đã cho phép.

Giới hạn và lỗi#

  • Trên mỗi khóa hoặc kết nối: 120 yêu cầu mỗi phút và 20 000 mỗi ngày (REST và MCP tính chung).
  • Cuộc gọi một chạm — tính riêng: 10 mỗi phút và 300 mỗi ngày.
  • Nhật ký — tối đa 100 cuộc gọi mỗi yêu cầu; trong nhật ký tổng đài của chi nhánh — 400 cuộc gọi gần nhất trong 90 ngày.
Mã Lỗi Cách xử lý
401 api_key_missing, api_key_invalid không có khóa, khóa đã bị thu hồi hoặc hết hạn; token OAuth hết hạn — hãy làm mới token
403 scope_required khóa chưa có quyền — hãy thêm quyền trong “Quyền”
400 location_required khóa có nhiều chi nhánh — hãy truyền location
404 location_not_found không có chi nhánh hoặc khóa bị giới hạn ở các chi nhánh khác
429 rate_limited đã hết giới hạn, hãy thử lại sau Retry-After giây

Sự kiện cuộc gọi#

Theo thời gian thực — webhook của tổng đài: Telephony → API và MCP → Webhook hoặc POST /v1/webhooks bằng khóa có quyền webhooks:write. Đến địa chỉ https của bạn trong vài giây, một POST được gửi cho mỗi sự kiện: call.started (bắt đầu: hướng, ai, đến đâu, đường dây), call.ringing (đang đổ chuông ở đâu — một sự kiện cho mỗi điện thoại), call.answered (ai đã trả lời), call.transferred (ai chuyển và chuyển cho ai), call.ended (kết quả, thời lượng, cuộc trò chuyện, ai đã trả lời). Có thể giới hạn webhook theo chi nhánh và loại sự kiện.

{ "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": "Анна" } } }

Chữ ký — tiêu đề X-Cenaly-Signature: t=<thời gian unix>,v1=<HMAC-SHA256 của "t.nội dung" bằng secret của webhook>. Hãy tính lại, so sánh trong thời gian không đổi và từ chối t cũ hơn 5 phút. Secret whsec_… chỉ hiển thị một lần; «Secret mới» vô hiệu hóa secret trước đó ngay lập tức.

Gửi đến — hãy trả lời 2xx trong 10 giây. Nếu không, chúng tôi gửi lại với khoảng nghỉ 10 giây, 30 giây, 2, 5, 10, 15 và 15 phút (8 lần thử, khoảng một giờ); lần gửi lại mang cùng id — dựa vào đó để loại bỏ bản trùng. Nhật ký gửi đến (mã phản hồi, thời gian, lần thử) — trong cửa sổ webhook và GET /v1/webhooks/{id}/deliveries; «Kiểm tra» gửi webhook.ping dịch vụ. Địa chỉ 3 ngày liên tiếp không nhận được lần gửi nào — webhook tự tắt: chủ sở hữu nhận được email, trong cửa sổ «Webhook» — nhãn «Đã tắt tự động» và nút «Bật» (hoặc PATCH /v1/webhooks/{id} với { "enabled": true }).

Không có máy chủ riêng — cùng luồng đó bằng cách thăm dò: GET /v1/events?since=<id sự kiện cuối cùng> hoặc công cụ MCP get_call_events (sự kiện của chi nhánh trong 7 ngày qua, tối đa 2000; mỗi phản hồi — tối đa 500, more: true — tiếp tục phân trang bằng cùng since).

Sau khi xử lý ghi âm — cuộc gọi có bản chép lời và phân tích đến qua webhook của nguồn «Webhook» trong mục «Cuộc gọi».

«Mở / đóng» — PUT /v1/open-closed với { "closed": true } đóng chi nhánh đến hết ngày (như *28 từ điện thoại: cuộc gọi đi theo quy tắc «ngoài giờ»), false — mở; GET cho biết đã đóng hay chưa và do ai. Với "until" — đóng đến thời điểm đó (không quá 30 ngày), sau đó sẽ tự mở: thời gian không có độ lệch (2026-10-05T09:00) được đọc theo múi giờ của chi nhánh; false cũng gỡ bỏ nó. Cần bật tùy chọn «Đóng bằng mã *28» trong định tuyến (nếu không sẽ trả 409).

Bảo mật#

  • Khóa chỉ hiển thị một lần và chúng tôi chỉ lưu dấu vân tay; khóa bị lộ có thể thu hồi bằng một nút.
  • Chỉ chủ sở hữu mới cho phép kết nối OAuth, và có thể thấy quyền truy cập được gửi đến địa chỉ nào; kết nối đã cho phép có thể giới hạn quyền và chi nhánh và thu hồi bất cứ lúc nào.
  • Số điện thoại là dữ liệu cá nhân của khách hàng: chỉ cấp quyền truy cập nhật ký và ghi âm cho những tích hợp thực sự cần.
  • Mọi thay đổi qua khóa hoặc kết nối — trong nhật ký hành động của tổng đài kèm tên khóa hoặc ứng dụng.
Was this article helpful?