自社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 |
通話イベントのウェブフックの作成、変更、テスト、削除 | 個別のチェックボックスのみ |
daynight:write |
店舗を当日終了までまたは指定日まで閉じる、開く — 電話からの*28と同じ |
個別のチェックボックスのみ |
何かを変更したり通話時間を消費したりする権限は、ウィンドウ内で琥珀色で表示されます。最後の3つの権限はどのセットにも含まれず、「すべて」にも含まれません — 個別のチェックボックスでのみ、意識的に付与できます。
ChatGPT、claude.ai、Perplexity、Cursor、VS Code:キーなし接続#
クラウドアシスタントはヘッダーにキーを挿入できません — MCPサーバーをOAuthで接続します。キーの作成は不要です。
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のラベルとアプリのアドレス付きの行として表示されます — そこでも権限の変更とアクセスの失効ができます。同じアプリの再接続は、その権限を更新するもので、2つ目の接続を作成するものではありません。
接続を許可できるのはアカウント所有者のみです。
Claude Code、Claude Desktop、Cursor:キーによる接続#
Claude Code — コマンド1つで:
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)に指定します。キーに店舗が1つしかない場合は 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 |
通話ルールを1件ずつ:作成、渡されたフィールドの変更、削除、並べ替え、ロールバック |
| GET, POST | /v1/config/export、/v1/config/import |
config:export、config:import |
ファイル形式の設定;アップロードはプレビュー後に適用 |
| GET | /v1/events |
calls:read |
店舗の7日間の通話イベント(最大2000件):since(イベントidまたは時刻)、types、limit(1応答あたり最大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"
9月の指標を8月および前年9月と比較 — 自社の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認可仕様に準拠しているため、対応するクライアントであればどれでも利用できます:
- トークンなしでMCPにリクエストすると、
WWW-Authenticate: Bearer resource_metadata="https://api.cenaly.com/.well-known/oauth-protected-resource/pbx-api/mcp"ヘッダー付きの401が返ります。 - リソースメタデータ(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)にあります。 - 動的クライアント登録(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`のいずれかです。 - PKCE付き認可コード(
S256のみ):GET /pbx-api/oauth/authorize→ オーナーのログインと同意画面 →code、state、issを付けて指定のアドレスへ戻ります。resourceパラメータはMCPサーバーのアドレスです。 - トークン:
POST /pbx-api/oauth/token。コードは1回限りで有効期限は2分です(誤ったcode_verifierはコードを無効にします)。アクセストークンは1時間、リフレッシュトークンは90日で、更新のたびに新しいトークンが発行され、以前のトークンは使えなくなります。 - 失効:
POST /pbx-api/oauth/revoke(RFC 7009)は接続全体を無効にします — 「APIとMCP」画面の「取り消す」と同じです。
アクセストークンはキーと同じ場所すべてで受け付けられます。MCPでもRESTでも、オーナーが許可した権限と店舗で利用できます。
制限とエラー#
- キーまたは接続ごと:1分あたり120リクエスト、1日あたり20 000リクエスト(RESTとMCPの合計)。
- クリック発信は別枠:1分あたり10回、1日あたり300回。
- 通話履歴は1リクエストあたり最大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秒後に再試行してください |
通話イベント#
リアルタイム — PBXのウェブフック:テレフォニー → APIとMCP → ウェブフック、または webhooks:write 権限を持つキーで POST /v1/webhooks。お客様のhttpsアドレスに、各イベントごとに数秒でPOSTが届きます:call.started(開始:方向、発信者、着信先、回線)、call.ringing(誰の電話が鳴っているか — 電話機ごとに1件)、call.answered(誰が応答したか)、call.transferred(誰が誰に転送したか)、call.ended(結果、通話時間、会話、応答者)。ウェブフックは店舗とイベント種別で制限できます。
{ "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=<ウェブフックのシークレットによる "t.本文" のHMAC-SHA256>。再計算し、定数時間で比較し、5分より古い t は拒否してください。シークレット whsec_… は一度だけ表示されます。「新しいシークレット」は以前のものを即座に無効にします。
配信 — 10秒以内に2xxで応答してください。それ以外の場合は10秒、30秒、2、5、10、15、15分の間隔で再試行します(8回、約1時間)。再試行は同じ id を持ちます — それによって重複を破棄してください。配信ログ(応答コード、時刻、試行回数)はウェブフックの画面と GET /v1/webhooks/{id}/deliveries にあります。「確認」はサービス用の webhook.ping を送信します。アドレスが3日連続で1件も配信を受け取らなかった場合、ウェブフックは自動的に無効になります:オーナーにメールが届き、「ウェブフック」画面には「自動的に無効」のマークと「有効にする」ボタンが表示されます(または { "enabled": true } を指定した PATCH /v1/webhooks/{id})。
自前のサーバーなし — 同じフィードをポーリングで取得:GET /v1/events?since=<最後のイベントのid> またはMCPツール get_call_events(店舗のイベントを過去7日分、最大2000件。1回の応答は最大500件、more: true の場合は同じ since でさらに読み進めてください)。
録音の処理後 — 文字起こしと分析を含む通話は、「ウェブフック」ソースのウェブフックとして「通話」セクションに届きます。
「開店 / 閉店」 — { "closed": true } を指定した PUT /v1/open-closed は店舗をその日の終わりまで閉店します(電話からの *28 と同様:通話は「営業時間外」のルールで処理されます)、false は開店します。GET は閉店中かどうか、誰によってかを表示します。"until" を指定するとその時刻まで閉店し(最大30日先まで)、その後は自動的に開店します:タイムゾーンオフセットなしの時刻(2026-10-05T09:00)は店舗のタイムゾーンで解釈されます。false はそれも解除します。ルーティングで「*28コードで閉店」チェックボックスが有効になっている必要があります(そうでない場合は409)。
セキュリティ#
- キーは一度だけ表示され、当社ではフィンガープリントのみを保存します。漏洩したキーはワンボタンで失効できます。
- OAuth接続を許可できるのはオーナーのみで、どのアドレスにアクセス権が渡るかを確認できます。許可された接続は権限と店舗で制限でき、いつでも取り消せます。
- 電話番号はお客様の顧客の個人データです:通話履歴と録音への権限は、本当に必要なインテグレーションにのみ付与してください。
- キーまたは接続によるすべての変更は、キーまたはアプリの名前とともにPBXの操作ログに記録されます。