API und MCP Ihrer eigenen TK-Anlage
Ihre eigene TK-Anlage Cenaly ist offen für Ihre Programme und KI-Assistenten:
- CRM, Buchhaltungssystem, eigene Website — REST API: Anrufjournal, Aufzeichnungen und Transkriptionen, Anruf per Klick, Durchwahlen, Weiterleitung, Anrufregeln;
- Claude Code, Claude Desktop, Cursor — entfernter MCP-Server per Schlüssel;
- ChatGPT, claude.ai, Perplexity, Cursor und VS Code — derselbe MCP-Server ohne Schlüssel: Sie melden sich in Ihrem Konto an und erteilen die Freigabe (OAuth).
Alles arbeitet im Namen des Kontoinhabers und nur mit den Rechten, die er erteilt hat. Jede Änderung ist im Aktionsprotokoll der TK-Anlage sichtbar, mit dem Namen des Schlüssels oder der App.
Adressen#
| Was | Adresse |
|---|---|
| REST API | https://api.cenaly.com/pbx-api/v1 |
| MCP-Server (streamable HTTP) | https://api.cenaly.com/pbx-api/mcp |
| Dokumentation der Methoden | api.cenaly.com/pbx-api/v1/docs |
| OpenAPI-3.1-Beschreibung | api.cenaly.com/pbx-api/v1/openapi.json |
| OAuth-Metadaten | https://api.cenaly.com/.well-known/oauth-authorization-server/pbx-api |
Schlüssel und Rechte#
Den Schlüssel erstellt nur der Kontoinhaber: Telefonie → API und MCP → Schlüssel → Schlüssel erstellen. Ein Mitarbeiter mit der Rolle „TK-Administrator“ sieht keine Schlüssel — der Schlüssel handelt im Namen des Inhabers.
- Ein Schlüssel der Form
cpx_…wird einmal angezeigt — direkt nach der Erstellung, zusammen mit fertigen Einfügungen für die Verbindung. Bei uns wird nur sein Fingerabdruck (SHA-256) gespeichert; der Schlüssel kann nicht wiederhergestellt werden — nur ein neuer ausgegeben werden. - Der Schlüssel kann auf Standorte beschränkt werden (andernfalls — alle Standorte, einschließlich künftiger) und auf eine Laufzeit (30, 90, 365 Tage oder unbefristet).
- „Rechte“ ändern die Rechte und Standorte des Schlüssels im laufenden Betrieb: Die Integration funktioniert weiter, die neuen Rechte gelten ab der nächsten Anfrage.
- „Widerrufen“ — sofort und dauerhaft: Die nächste Anfrage mit diesem Schlüssel erhält 401.
- Aktive Schlüssel und Verbindungen — bis zu 20 pro Konto.
Die Rechte werden als fertiges Set oder per Häkchen gewählt:
| Recht | Was es gewährt | Sets |
|---|---|---|
calls:read |
Anrufjournal und Anrufkarte mit Route | CRM, KI-Assistent, nur Lesen, alles |
recordings:read |
Link zur Gesprächsaufzeichnung (10 Minuten) und Transkription | CRM, KI-Assistent, nur Lesen, alles |
calls:dial |
Anruf per Klick: Zuerst klingelt das Telefon des Mitarbeiters, nach dem Abheben wählt die TK-Anlage die Nummer | CRM, KI-Assistent, alles |
extensions:read |
Durchwahlen, Gruppen, Weiterleitung, „Nicht stören“ | CRM, KI-Assistent, nur Lesen, alles |
extensions:write |
persönliche Weiterleitung und „Nicht stören“ ändern | KI-Assistent, alles |
presence:read |
wer frei ist, telefoniert, spricht | KI-Assistent, nur Lesen, alles |
reports:read |
Callcenter-Metriken für einen Zeitraum: Servicelevel, Wartezeit, verpasste Anrufe, AHT, FCR, CSAT, wo wir Anrufe verlieren, Aufschlüsselungen nach Warteschlangen, Leitungen, Mitarbeitern | nur Lesen, alles |
routes:read |
Nummern, Regeln für eingehende und ausgehende Anrufe, Verlauf der Regeln | nur Lesen, alles |
routes:write |
Anrufregeln erstellen, ändern, löschen und umsortieren, aus dem Verlauf zurücksetzen | nur per separatem Häkchen |
config:export |
Export der TK-Einstellungen als Datei | nur per separatem Häkchen |
config:import |
Import der Einstellungen aus Tabellen: Vorschau, dann Anwendung | nur per separatem Häkchen |
webhooks:write |
Webhooks für Anrufereignisse anlegen, ändern, prüfen und löschen | nur per separatem Häkchen |
daynight:write |
einen Standort bis Tagesende oder bis zu einem Datum schließen, öffnen — dasselbe wie *28 vom Telefon |
nur per separatem Häkchen |
Rechte, die etwas ändern oder Minuten verbrauchen, sind im Fenster bernsteinfarben markiert. Die letzten drei Rechte sind in keinem Set enthalten, auch nicht in „Alles“ — sie können nur bewusst per separatem Häkchen erteilt werden.
ChatGPT, claude.ai, Perplexity, Cursor, VS Code: Verbindung ohne Schlüssel#
Cloud-Assistenten können keinen Schlüssel in den Header einfügen — sie verbinden den MCP-Server per OAuth. Ein Schlüssel muss nicht erstellt werden.
claude.ai: Einstellungen → Konnektoren → Eigenen Konnektor hinzufügen → https://api.cenaly.com/pbx-api/mcp einfügen → Verbinden.
ChatGPT: Einstellungen → Apps und Konnektoren → Erweitert → Entwicklermodus aktivieren → Erstellen → https://api.cenaly.com/pbx-api/mcp einfügen, Authentifizierung — OAuth.
Perplexity: Kontoeinstellungen → Konnektoren → „+ Eigener Konnektor“ → Remote → https://api.cenaly.com/pbx-api/mcp einfügen, Authentifizierung — OAuth.
Cursor und VS Code: Fügen Sie einen entfernten (HTTP) MCP-Server mit der Adresse https://api.cenaly.com/pbx-api/mcp ohne Header mit Schlüssel hinzu — das Anmeldefenster öffnet sich im Browser. In Cursor: .cursor/mcp.json → {"mcpServers": {"cenaly-pbx": {"url": "https://api.cenaly.com/pbx-api/mcp"}}}; in VS Code: Befehl „MCP: Add Server“ → HTTP.
Danach öffnet der Assistent das Cenaly-Anmeldefenster. Nach der Anmeldung sehen Sie:
- wer den Zugriff anfragt — die App wird nach der Adresse benannt, an die die Antwort zurückgeht: „Verifizierter Konnektor · chatgpt.com“ (ebenso — claude.ai, perplexity.ai, vscode.dev, Cursor), „Programm auf diesem Computer“ oder die Warnung „Unbekannte Adresse“. Der Name, den die App gesendet hat, wird klein angezeigt — ihn kann jeder beliebige eintragen;
- Rechte — markiert ist, was die App anfragt (wenn sie „alles Mögliche“ anfragt, ist das Set „KI-Assistent“ markiert); Rechte „nur per separatem Häkchen“ werden nie im Voraus markiert;
- Standorte — alle oder ausgewählte.
„Erlauben“ bringt Sie zum Assistenten zurück, und er sieht sofort die Tools der TK-Anlage. Die Verbindung erscheint in Telefonie → API und MCP → Schlüssel als Zeile mit der Markierung OAuth und der Adresse der App — dort werden auch die Rechte geändert und der Zugriff widerrufen. Eine erneute Verbindung derselben App aktualisiert ihre Rechte, statt eine zweite Verbindung anzulegen.
Verbindungen erlauben kann nur der Kontoinhaber.
Claude Code, Claude Desktop, Cursor: Verbindung per Schlüssel#
Claude Code – mit einem Befehl:
claude mcp add --transport http telephony https://api.cenaly.com/pbx-api/mcp --header "Authorization: Bearer cpx_…"
Claude Desktop, Cursor (.cursor/mcp.json) und andere Clients mit Konfigurationsdatei:
{
"mcpServers": {
"telephony": {
"type": "http",
"url": "https://api.cenaly.com/pbx-api/mcp",
"headers": { "Authorization": "Bearer cpx_…" }
}
}
}
Fertige Einfügungen mit Ihrem Schlüssel werden direkt nach dem Erstellen des Schlüssels angezeigt. Der Assistent sieht nur die durch den Schlüssel erlaubten Tools und kann auf „Wer hat heute angerufen und nicht durchgekommen“, „Ruf diesen Gast vom Telefon von Anna zurück“, „Aktiviere mir die Weiterleitung auf das Mobiltelefon“ antworten.
REST API#
Der Schlüssel steht im Header Authorization: Bearer <ключ> (oder X-Api-Key). Hat der Schlüssel einen Standort, kann der Parameter location weggelassen werden; bei mehreren listet die Antwort 400 location_required sie auf.
| Methode | Pfad | Recht | Was sie tut |
|---|---|---|---|
| GET | /v1/me |
— | Rechte, Standorte und Limits des Schlüssels |
| GET | /v1/locations |
— | Standorte des Schlüssels und ihre Telefonanlagen |
| GET | /v1/calls |
calls:read |
Anrufjournal: since, until, direction, status (answered, missed, ai), number, extension, limit bis 100, cursor |
| GET | /v1/calls/{id} |
calls:read |
Anruf mit Route: welche Regel gegriffen hat, wessen Telefon geklingelt hat, wer geantwortet hat |
| GET | /v1/calls/{id}/recording |
recordings:read |
Link zur Aufzeichnung für 10 Minuten |
| GET | /v1/calls/{id}/transcript |
recordings:read |
Transkript und Ergebnis des Gesprächs |
| POST | /v1/dial |
calls:dial |
Anruf per Klick: { "extension": "101", "number": "+995555123456" } |
| GET | /v1/dial/{commandId} |
calls:dial |
Wie der Anruf per Klick läuft |
| GET | /v1/extensions |
extensions:read |
Durchwahlen, Gruppen, Weiterleitung, „Nicht stören“ |
| PUT | /v1/extensions/{ext}/forwarding |
extensions:write |
Persönliche Weiterleitung der Durchwahl |
| PUT | /v1/extensions/{ext}/dnd |
extensions:write |
„Nicht stören“: { "on": true } |
| GET | /v1/presence |
presence:read |
Wer gerade frei ist |
| GET | /v1/metrics |
reports:read |
Callcenter-Metriken für einen Zeitraum: from, to (lokale Daten des Standorts YYYY-MM-DD, Standard – die letzten 7 Tage), compare (prev, year, prev,year oder leer), sections (metrics,loss,queues,lines,operators,directions,heatmap,daily), callbacks=true – zusätzlich die Rückrufgeschwindigkeit |
| GET | /v1/routes |
routes:read |
Nummern, Regeln für eingehende und ausgehende Anrufe |
| GET | /v1/routes/history |
routes:read |
Änderungsverlauf der Regeln |
| POST, PUT, DELETE | /v1/routes/inbound…, /v1/routes/outbound… |
routes:write |
Anrufregeln einzeln: erstellen, übergebene Felder ändern, löschen, umsortieren, zurücksetzen |
| GET, POST | /v1/config/export, /v1/config/import |
config:export, config:import |
Einstellungen im Dateiformat; Import – Vorschau, dann Anwendung |
| GET | /v1/events |
calls:read |
Anrufereignisse des Standorts für 7 Tage (bis 2000): since (Ereignis-ID oder Zeit), types, limit (bis 500 pro Antwort) |
| GET, POST, PATCH, DELETE | /v1/webhooks… |
webhooks:write |
Webhooks: auflisten, erstellen, ändern, löschen; …/rotate-secret, …/test, …/deliveries |
| GET, PUT | /v1/open-closed |
routes:read, daynight:write |
Ob geschlossen und bis wann; schließen bis zum Ende des Tages { "closed": true }, bis zu einem Datum { "closed": true, "until": "2026-10-05T09:00" } oder öffnen |
Die vollständige Beschreibung der Felder finden Sie auf der Seite der Dokumentation und in der OpenAPI.
Wer hat heute angerufen und ist nicht durchgekommen:
curl -H "Authorization: Bearer cpx_…" \
"https://api.cenaly.com/pbx-api/v1/calls?status=missed&since=$(date -u +%Y-%m-%dT00:00:00Z)"
Einen Kunden vom Telefon der Durchwahl 101 zurückrufen:
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"
Metriken für September mit Vergleich zu August und September des Vorjahres – für Ihr eigenes BI-Panel:
curl -H "Authorization: Bearer cpx_…" \
"https://api.cenaly.com/pbx-api/v1/metrics?from=2026-09-01&to=2026-09-30&compare=prev,year"
In der Antwort enthält metrics dieselben Zahlen wie auf dem Bildschirm Telefonie → Anrufjournal → Analyse: eingehende, angenommene und verpasste Anrufe, Servicelevel (Schwelle und Ziel – aus den Einstellungen der Warteschlange), durchschnittliche und maximale Wartezeit, Anteil der Abbrecher ohne kurze Anrufe, AHT, FCR, Anteil der Weiterleitungen und des Bots, CSAT. compare.prev und compare.year – dieselben Felder für den vorherigen Zeitraum und für denselben Zeitraum vor einem Jahr. Der Vergleich mit dem Vorjahr wird für Zeiträume bis 400 Tage berechnet.
Weiterleitung auf das Mobiltelefon aktivieren, wenn ein Mitarbeiter nicht innerhalb von 15 Sekunden antwortet:
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-Tools#
Dieselben Vorgänge wie bei REST. Der Client sieht nur die Tools, die durch den Schlüssel oder die Verbindung erlaubt sind:
| Tools | Recht |
|---|---|
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 |
Der Server arbeitet ohne Sitzungen (streamable HTTP, JSON-RPC 2.0): initialize, tools/list, tools/call.
OAuth für Entwickler eigener Konnektoren#
Der Autorisierungsserver folgt der MCP-Autorisierungsspezifikation – jeder Client, der sie unterstützt, ist geeignet:
- Eine Anfrage an MCP ohne Token erhält 401 mit dem Header
WWW-Authenticate: Bearer resource_metadata="https://api.cenaly.com/.well-known/oauth-protected-resource/pbx-api/mcp". - Die Ressourcen-Metadaten (RFC 9728) nennen den Autorisierungsserver –
https://api.cenaly.com/pbx-api; seine Metadaten (RFC 8414) – unter der Adressehttps://api.cenaly.com/.well-known/oauth-authorization-server/pbx-api(und…/pbx-api/.well-known/openid-configuration). - Dynamische Registrierung des Clients (RFC 7591):
POST /pbx-api/oauth/register. Die Rückkehradresse – nurlocalhost/127.0.0.1(beliebiger Port) oder die Adresse eines bekannten Konnektors (claude.ai, claude.com, chatgpt.com, chat.openai.com, vscode.dev, www.perplexity.ai, enterprise.perplexity.ai; bei Cursor – zusätzlich genaucursor://anysphere.cursor-mcp/oauth/callback). Die Anmeldeart des Clients –none(öffentlicher Client),client_secret_postoderclient_secret_basic. - Autorisierungscode mit PKCE (nur
S256):GET /pbx-api/oauth/authorize→ Anmelde- und Zustimmungsfenster des Inhabers → Rückkehr an Ihre Adresse mitcode,stateundiss. Der Parameterresource– die Adresse des MCP-Servers. - Tokens:
POST /pbx-api/oauth/token. Der Code ist einmalig und gilt 2 Minuten (ein falschercode_verifiermacht ihn ungültig); das Zugriffstoken – 1 Stunde; das Aktualisierungstoken – 90 Tage, bei jeder Aktualisierung wird ein neues ausgegeben, das vorherige funktioniert nicht mehr. - Widerruf:
POST /pbx-api/oauth/revoke(RFC 7009) beendet die Verbindung vollständig – wie „Widerrufen“ im Fenster „API und MCP“.
Das Zugriffstoken wird überall akzeptiert, wo der Schlüssel gilt: sowohl in MCP als auch in REST, mit den Rechten und Standorten, die der Inhaber erlaubt hat.
Limits und Fehler#
- Pro Schlüssel oder Verbindung: 120 Anfragen pro Minute und 20 000 pro Tag (REST und MCP zusammen).
- Anruf per Klick – separat: 10 pro Minute und 300 pro Tag.
- Das Journal – bis zu 100 Anrufe pro Anfrage; im Journal der TK-Anlage eines Standorts – die letzten 400 Anrufe der vergangenen 90 Tage.
| Code | Fehler | Was zu tun ist |
|---|---|---|
| 401 | api_key_missing, api_key_invalid |
der Schlüssel fehlt, wurde widerrufen oder ist abgelaufen; das OAuth-Token ist abgelaufen – aktualisieren Sie es |
| 403 | scope_required |
dem Schlüssel fehlt das Recht – fügen Sie es unter „Rechte“ hinzu |
| 400 | location_required |
der Schlüssel gilt für mehrere Standorte – übergeben Sie location |
| 404 | location_not_found |
der Standort existiert nicht oder der Schlüssel ist auf andere Standorte beschränkt |
| 429 | rate_limited |
das Limit ist erschöpft, wiederholen Sie nach Retry-After Sekunden |
Anrufereignisse#
In Echtzeit — Webhooks der Telefonanlage: Telefonie → API und MCP → Webhooks oder POST /v1/webhooks mit einem Schlüssel mit der Berechtigung webhooks:write. An Ihre https-Adresse kommt innerhalb von Sekunden ein POST für jedes Ereignis: call.started (begonnen: Richtung, wer, wohin, Leitung), call.ringing (bei wem es klingelt — je einer pro Telefon), call.answered (wer geantwortet hat), call.transferred (wer an wen weitergeleitet hat), call.ended (Ergebnis, Dauer, Gespräch, wer geantwortet hat). Der Webhook kann auf Standorte und Ereignistypen beschränkt werden.
{ "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": "Анна" } } }
Signatur — Header X-Cenaly-Signature: t=<Unix-Zeit>,v1=<HMAC-SHA256 von "t.Body" mit dem Webhook-Secret>. Berechnen Sie sie neu, vergleichen Sie in konstanter Zeit und lehnen Sie t ab, das älter als 5 Minuten ist. Das Secret whsec_… wird einmal angezeigt; „Neues Secret“ macht das vorherige sofort ungültig.
Zustellung — antworten Sie mit 2xx innerhalb von 10 Sekunden. Andernfalls wiederholen wir mit Pausen von 10 s, 30 s, 2, 5, 10, 15 und 15 Minuten (8 Versuche, etwa eine Stunde); die Wiederholung trägt dieselbe id — verwerfen Sie daran Duplikate. Das Zustellungsprotokoll (Antwortcode, Zeit, Versuch) — im Webhook-Fenster und über GET /v1/webhooks/{id}/deliveries; „Prüfen“ sendet ein Dienst-webhook.ping. Hat die Adresse 3 Tage in Folge keine einzige Zustellung angenommen, wird der Webhook automatisch deaktiviert: Der Inhaber erhält eine E-Mail, im Fenster „Webhooks“ erscheint die Markierung „Automatisch deaktiviert“ und die Schaltfläche „Aktivieren“ (oder PATCH /v1/webhooks/{id} mit { "enabled": true }).
Ohne eigenen Server — derselbe Feed per Abfrage: GET /v1/events?since=<id des letzten Ereignisses> oder das MCP-Tool get_call_events (Ereignisse des Standorts der letzten 7 Tage, bis zu 2000; pro Antwort bis zu 500, more: true — blättern Sie mit demselben since weiter).
Nach der Verarbeitung der Aufzeichnung — der Anruf mit Transkript und Auswertung kommt per Webhook der Quelle „Webhook“ im Bereich „Anrufe“.
„Offen / geschlossen“ — PUT /v1/open-closed mit { "closed": true } schließt den Standort bis zum Ende des Tages (wie *28 vom Telefon: Anrufe laufen nach den Regeln „außerhalb der Geschäftszeiten“), false — öffnet ihn; GET zeigt, ob und von wem geschlossen wurde. Mit "until" — geschlossen bis zu diesem Zeitpunkt (nicht weiter als 30 Tage), danach öffnet er sich von selbst: eine Zeit ohne Versatz (2026-10-05T09:00) wird in der Zeitzone des Standorts gelesen; false hebt auch das auf. Erforderlich ist das aktivierte Kontrollkästchen „Mit Code *28 schließen“ in der Weiterleitung (sonst 409).
Sicherheit#
- Der Schlüssel wird einmal angezeigt und bei uns nur als Fingerabdruck gespeichert; ein kompromittierter Schlüssel wird mit einer Schaltfläche widerrufen.
- Eine OAuth-Verbindung erlaubt nur der Inhaber, und es ist sichtbar, an welche Adresse der Zugriff geht; eine erlaubte Verbindung kann auf Berechtigungen und Standorte beschränkt und jederzeit widerrufen werden.
- Telefonnummern sind personenbezogene Daten Ihrer Kunden: Geben Sie Rechte auf Journal und Aufzeichnungen nur den Integrationen, die sie wirklich brauchen.
- Alle Änderungen per Schlüssel oder Verbindung — im Aktionsprotokoll der Telefonanlage mit dem Namen des Schlüssels oder der App.