C cenaly.com
Inizia gratuitamente

API e MCP del proprio centralino

Chiavi e permessi, REST API, server MCP per Claude e Cursor, connessione a ChatGPT e claude.ai tramite OAuth senza chiave, esempi, limiti ed errori

On this page 10

API e MCP del proprio centralino

Il proprio centralino Cenaly è aperto ai vostri programmi e assistenti IA:

  • CRM, sistema di contabilità, sito proprio — REST API: registro delle chiamate, registrazioni e trascrizioni, chiamata con un clic, interni, inoltro, regole di chiamata;
  • Claude Code, Claude Desktop, Cursor — server MCP remoto tramite chiave;
  • ChatGPT, claude.ai, Perplexity, Cursor e VS Code — lo stesso server MCP senza chiave: si accede al proprio account e si autorizza l'accesso (OAuth).

Tutto funziona a nome del proprietario dell'account e solo con i diritti che egli ha concesso. Ogni modifica è visibile nel registro delle azioni del centralino con il nome della chiave o dell'app.

Indirizzi#

Cosa Indirizzo
REST API https://api.cenaly.com/pbx-api/v1
Server MCP (streamable HTTP) https://api.cenaly.com/pbx-api/mcp
Documentazione sui metodi api.cenaly.com/pbx-api/v1/docs
Descrizione OpenAPI 3.1 api.cenaly.com/pbx-api/v1/openapi.json
Metadati OAuth https://api.cenaly.com/.well-known/oauth-authorization-server/pbx-api

Chiave e diritti#

La chiave è creata solo dal proprietario dell'account: Telefonia → API e MCP → Chiavi → Crea chiave. Un collaboratore con il ruolo «Amministratore del centralino» non vede le chiavi — la chiave agisce a nome del proprietario.

  • La chiave nel formato cpx_… è mostrata una sola volta — subito dopo la creazione, insieme agli snippet pronti per la connessione. Da noi è conservata solo la sua impronta (SHA-256), non è possibile recuperare la chiave — solo emetterne una nuova.
  • La chiave può essere limitata per sedi (altrimenti — tutte le sedi, comprese quelle future) e per scadenza (30, 90, 365 giorni o senza scadenza).
  • «Diritti» modificano i diritti e le sedi della chiave al volo: l'integrazione continua a funzionare, i nuovi diritti hanno effetto dalla richiesta successiva.
  • «Revoca» — immediata e definitiva: la richiesta successiva con questa chiave riceverà 401.
  • Chiavi e connessioni attive — fino a 20 per account.

I diritti si scelgono con un set pronto o con le caselle:

Diritto Cosa consente Set
calls:read registro delle chiamate e scheda della chiamata con il percorso CRM, Assistente IA, sola lettura, tutto
recordings:read link alla registrazione della conversazione (10 minuti) e trascrizione CRM, Assistente IA, sola lettura, tutto
calls:dial chiamata con un clic: prima squilla il telefono del collaboratore, dopo la risposta il centralino compone il numero CRM, Assistente IA, tutto
extensions:read interni, gruppi, inoltro, «non disturbare» CRM, Assistente IA, sola lettura, tutto
extensions:write modificare l'inoltro personale e «non disturbare» Assistente IA, tutto
presence:read chi è libero, sta chiamando, sta parlando Assistente IA, sola lettura, tutto
reports:read metriche del call center per periodo: livello di servizio, attesa, abbandonate, AHT, FCR, CSAT, dove perdiamo le chiamate, analisi per code, linee, collaboratori sola lettura, tutto
routes:read numeri, regole in entrata e in uscita, storico delle regole sola lettura, tutto
routes:write creare, modificare, eliminare e riordinare le regole di chiamata, ripristinare dallo storico solo con casella separata
config:export esportazione delle impostazioni del centralino in formato file solo con casella separata
config:import caricamento delle impostazioni da tabelle: anteprima, poi applicazione solo con casella separata
webhooks:write creare, modificare, verificare ed eliminare webhook degli eventi di chiamata solo con casella separata
daynight:write chiudere una sede fino a fine giornata o fino a una data, riaprire — come *28 dal telefono solo con casella separata

I diritti che modificano qualcosa o consumano minuti sono contrassegnati in ambra nella finestra. Gli ultimi tre diritti non fanno parte di nessun set, nemmeno di «tutto» — possono essere concessi solo consapevolmente, con una casella separata.

ChatGPT, claude.ai, Perplexity, Cursor, VS Code: connessione senza chiave#

Gli assistenti cloud non sanno inserire una chiave nell'intestazione — collegano il server MCP tramite OAuth. Non è necessario creare una chiave.

claude.ai: Impostazioni → Connettori → Aggiungi il proprio connettore → incollate https://api.cenaly.com/pbx-api/mcp → Connetti.

ChatGPT: Impostazioni → App e connettori → Avanzate → attivate la modalità sviluppatore → Crea → incollate https://api.cenaly.com/pbx-api/mcp, autenticazione — OAuth.

Perplexity: Impostazioni account → Connettori → «+ Proprio connettore» → Remoto → incollate https://api.cenaly.com/pbx-api/mcp, autenticazione — OAuth.

Cursor e VS Code: aggiungete un server MCP remoto (HTTP) con l'indirizzo https://api.cenaly.com/pbx-api/mcp senza intestazione con la chiave — la finestra di accesso si aprirà nel browser. In Cursor: .cursor/mcp.json → {"mcpServers": {"cenaly-pbx": {"url": "https://api.cenaly.com/pbx-api/mcp"}}}; in VS Code: comando «MCP: Add Server» → HTTP.

Poi l'assistente aprirà la finestra di accesso a Cenaly. Dopo l'accesso vedrete:

  • chi richiede l'accesso — l'app è denominata in base all'indirizzo a cui tornerà la risposta: «Connettore verificato · chatgpt.com» (così anche — claude.ai, perplexity.ai, vscode.dev, Cursor), «Programma su questo computer» o l'avviso «Indirizzo sconosciuto». Il nome inviato dall'app è mostrato in piccolo — può essere scritto da chiunque;
  • diritti — è contrassegnato ciò che richiede l'app (se chiede «tutto di seguito», è contrassegnato il set «Assistente IA»); i diritti «solo con casella separata» non vengono mai preselezionati;
  • sedi — tutte o quelle scelte.

«Consenti» vi riporta all'assistente, che vede subito gli strumenti del centralino. La connessione compare in Telefonia → API e MCP → Chiavi come riga con l'etichetta OAuth e l'indirizzo dell'app — lì si modificano i diritti e si revoca l'accesso. Una nuova connessione della stessa app aggiorna i suoi diritti, non crea una seconda connessione.

Solo il proprietario dell'account può autorizzare le connessioni.

Claude Code, Claude Desktop, Cursor: connessione tramite chiave#

Claude Code — con un solo comando:

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

Claude Desktop, Cursor (.cursor/mcp.json) e altri client con file di configurazione:

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

Gli snippet pronti con la Sua chiave vengono mostrati subito dopo la creazione della chiave. L'Assistente IA vede solo gli strumenti consentiti dalla chiave e può rispondere a «chi ha chiamato oggi e non ha ricevuto risposta», «richiama questo ospite dal telefono di Anna», «attivami la deviazione sul cellulare».

REST API#

La chiave va nell'intestazione Authorization: Bearer <ключ> (oppure X-Api-Key). Se la chiave ha una sola Sede, il parametro location può essere omesso; se ne ha più di una, la risposta 400 location_required le elencherà.

Metodo Percorso Permesso Cosa fa
GET /v1/me — permessi, Sedi e limiti della chiave
GET /v1/locations — Sedi della chiave e i loro centralini
GET /v1/calls calls:read registro delle chiamate: since, until, direction, status (answered, missed, ai), number, extension, limit fino a 100, cursor
GET /v1/calls/{id} calls:read chiamata con il percorso: quale regola è scattata, quale telefono ha squillato, chi ha risposto
GET /v1/calls/{id}/recording recordings:read link alla Registrazione valido 10 minuti
GET /v1/calls/{id}/transcript recordings:read trascrizione e riepilogo della conversazione
POST /v1/dial calls:dial chiamata con un clic: { "extension": "101", "number": "+995555123456" }
GET /v1/dial/{commandId} calls:dial come procede la chiamata con un clic
GET /v1/extensions extensions:read interni, gruppi, deviazione, «non disturbare»
PUT /v1/extensions/{ext}/forwarding extensions:write deviazione personale dell'interno
PUT /v1/extensions/{ext}/dnd extensions:write «non disturbare»: { "on": true }
GET /v1/presence presence:read chi è libero in questo momento
GET /v1/metrics reports:read metriche del call center per periodo: from, to (date locali della Sede YYYY-MM-DD, predefinito — ultimi 7 giorni), compare (prev, year, prev,year o vuoto), sections (metrics,loss,queues,lines,operators,directions,heatmap,daily), callbacks=true — anche la velocità di richiamata
GET /v1/routes routes:read numeri, regole in entrata e in uscita
GET /v1/routes/history routes:read cronologia delle modifiche alle regole
POST, PUT, DELETE /v1/routes/inbound…, /v1/routes/outbound… routes:write regole delle chiamate una alla volta: creare, modificare i campi trasmessi, eliminare, riordinare, ripristinare
GET, POST /v1/config/export, /v1/config/import config:export, config:import impostazioni in formato file; il caricamento — anteprima, poi applicazione
GET /v1/events calls:read eventi delle chiamate della Sede degli ultimi 7 giorni (fino a 2000): since (id dell'evento o orario), types, limit (fino a 500 per risposta)
GET, POST, PATCH, DELETE /v1/webhooks… webhooks:write webhook: elenco, creare, modificare, eliminare; …/rotate-secret, …/test, …/deliveries
GET, PUT /v1/open-closed routes:read, daynight:write se è chiuso e fino a quando; chiudere fino a fine giornata { "closed": true }, fino a una data { "closed": true, "until": "2026-10-05T09:00" } oppure aprire

La descrizione completa dei campi è nella pagina della documentazione e nell'OpenAPI.

Chi ha chiamato oggi e non ha ricevuto risposta:

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

Richiamare un cliente dal telefono dell'interno 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"

Metriche di settembre con confronto con agosto e con settembre dell'anno scorso — per la propria dashboard 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"

Nella risposta metrics — gli stessi numeri che si vedono nella schermata Telefonia → Registro chiamate → Analisi: in entrata, accettate e perse, livello di servizio (soglia e obiettivo — dalle impostazioni della coda), attesa media e massima, quota di abbandonate senza attesa breve, AHT, FCR, quota di trasferimenti e del bot, CSAT. compare.prev e compare.year — gli stessi campi per il periodo precedente e per lo stesso periodo di un anno fa. Il confronto con l'anno scorso viene calcolato per periodi fino a 400 giorni.

Attivare la deviazione sul cellulare se il dipendente non risponde entro 15 secondi:

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"

Strumenti MCP#

Le stesse operazioni del REST. Il client vede solo gli strumenti consentiti dalla chiave o dalla connessione:

Strumenti Diritto
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

Il server funziona senza sessioni (streamable HTTP, JSON-RPC 2.0): initialize, tools/list, tools/call.

OAuth per sviluppatori di connettori propri#

Il server di autorizzazione segue la specifica di autorizzazione MCP: va bene qualsiasi client che la supporti:

  1. Una richiesta a MCP senza token riceve 401 con l'intestazione WWW-Authenticate: Bearer resource_metadata="https://api.cenaly.com/.well-known/oauth-protected-resource/pbx-api/mcp".
  2. I metadati della risorsa (RFC 9728) indicano il server di autorizzazione — https://api.cenaly.com/pbx-api; i suoi metadati (RFC 8414) sono all'indirizzo https://api.cenaly.com/.well-known/oauth-authorization-server/pbx-api (e …/pbx-api/.well-known/openid-configuration).
  3. Registrazione dinamica del client (RFC 7591): POST /pbx-api/oauth/register. L'indirizzo di ritorno è solo localhost / 127.0.0.1 (qualsiasi porta) oppure l'indirizzo di un connettore noto (claude.ai, claude.com, chatgpt.com, chat.openai.com, vscode.dev, www.perplexity.ai, enterprise.perplexity.ai; per Cursor anche esattamente cursor://anysphere.cursor-mcp/oauth/callback). Il metodo di accesso del client è none (client pubblico), client_secret_post o client_secret_basic.
  4. Codice di autorizzazione con PKCE (solo S256): GET /pbx-api/oauth/authorize → finestra di accesso e consenso del proprietario → ritorno al vostro indirizzo con code, state e iss. Il parametro resource è l'indirizzo del server MCP.
  5. Token: POST /pbx-api/oauth/token. Il codice è monouso e dura 2 minuti (un code_verifier errato lo annulla); il token di accesso dura 1 ora; il token di aggiornamento dura 90 giorni, a ogni aggiornamento ne viene emesso uno nuovo e il precedente smette di funzionare.
  6. Revoca: POST /pbx-api/oauth/revoke (RFC 7009) annulla la connessione del tutto — come «Revoca» nella finestra «API e MCP».

Il token di accesso è accettato ovunque sia accettata la chiave: sia in MCP sia in REST, con i diritti e le sedi che il proprietario ha consentito.

Limiti ed errori#

  • Per chiave o connessione: 120 richieste al minuto e 20 000 al giorno (REST e MCP insieme).
  • Chiamata in click — a parte: 10 al minuto e 300 al giorno.
  • Il registro — fino a 100 chiamate per richiesta; nel registro del centralino le sedi — le ultime 400 chiamate in 90 giorni.
Codice Errore Cosa fare
401 api_key_missing, api_key_invalid la chiave non c'è, è stata revocata o è scaduta; il token OAuth è scaduto — aggiornatelo
403 scope_required la chiave non ha il diritto — aggiungetelo in «Diritti»
400 location_required la chiave ha più sedi — passate location
404 location_not_found la sede non c'è o la chiave è limitata ad altre sedi
429 rate_limited limite esaurito, riprovate dopo Retry-After secondi

Eventi delle chiamate#

In tempo reale — webhook del centralino: Telefonia → API e MCP → Webhook oppure POST /v1/webhooks con una chiave con il diritto webhooks:write. Al vostro indirizzo https arriva in pochi secondi un POST per ogni evento: call.started (iniziata: direzione, chi, verso chi, linea), call.ringing (a chi sta squillando — uno per ogni telefono), call.answered (chi ha risposto), call.transferred (chi ha trasferito e a chi), call.ended (esito, durata, conversazione, chi ha risposto). Il webhook può essere limitato per sedi e tipi di evento.

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

Firma — intestazione X-Cenaly-Signature: t=<tempo unix>,v1=<HMAC-SHA256 di "t.corpo" con il segreto del webhook>. Ricalcolate, confrontate a tempo costante e rifiutate t più vecchi di 5 minuti. Il segreto whsec_… viene mostrato una sola volta; «Nuovo segreto» disattiva immediatamente il precedente.

Consegna — rispondete 2xx entro 10 secondi. Altrimenti ripetiamo con pause di 10 s, 30 s, 2, 5, 10, 15 e 15 minuti (8 tentativi, circa un'ora); la ripetizione porta lo stesso id — usatelo per scartare i duplicati. Il registro delle consegne (codice di risposta, ora, tentativo) è nella finestra del webhook e in GET /v1/webhooks/{id}/deliveries; «Verifica» invia il webhook.ping di servizio. Se l'indirizzo per 3 giorni consecutivi non ha accettato nessuna consegna, il webhook si disattiva da solo: al proprietario arriva un'email, nella finestra «Webhook» compaiono la nota «Disattivato automaticamente» e il pulsante «Attiva» (oppure PATCH /v1/webhooks/{id} con { "enabled": true }).

Senza un proprio server — la stessa sequenza tramite polling: GET /v1/events?since=<id dell'ultimo evento> oppure lo strumento MCP get_call_events (eventi della sede degli ultimi 7 giorni, fino a 2000; per risposta fino a 500, more: true — continuate a scorrere con lo stesso since).

Dopo l'elaborazione della registrazione — la chiamata con trascrizione e analisi arriva tramite webhook della fonte «Webhook» nella sezione «Chiamate».

«Aperto / chiuso» — PUT /v1/open-closed con { "closed": true } chiude la sede fino alla fine della giornata (come *28 dal telefono: le chiamate seguono le regole «fuori orario»), false la apre; GET mostra se è chiusa e da chi. Con "until" — chiusa fino a quel momento (non oltre 30 giorni), poi si riapre da sola: un orario senza offset (2026-10-05T09:00) viene letto nel fuso della sede; false annulla anche questo. Serve la casella «Chiudi con il codice *28» attiva nell'instradamento (altrimenti 409).

Sicurezza#

  • La chiave viene mostrata una sola volta e da noi è conservata solo come impronta; una chiave compromessa si revoca con un pulsante.
  • Il collegamento OAuth è autorizzato solo dal proprietario ed è visibile a quale indirizzo viene concesso l'accesso; un collegamento autorizzato può essere limitato per diritti e sedi e revocato in qualsiasi momento.
  • I numeri di telefono sono dati personali dei vostri clienti: concedete i diritti sul registro e sulle registrazioni solo alle integrazioni che ne hanno davvero bisogno.
  • Tutte le modifiche tramite chiave o collegamento sono nel registro delle azioni del centralino con il nome della chiave o dell'app.
Was this article helpful?