API e MCP da sua central telefônica
Sua central telefônica Cenaly está aberta para seus programas e assistentes de IA:
- CRM, sistema de gestão, seu site — REST API: histórico de chamadas, gravações e transcrições, chamada com um clique, ramais, encaminhamento, regras de chamadas;
- Claude Code, Claude Desktop, Cursor — servidor MCP remoto por chave;
- ChatGPT, claude.ai, Perplexity, Cursor e VS Code — o mesmo servidor MCP sem chave: você entra na sua conta e autoriza o acesso (OAuth).
Tudo funciona em nome do proprietário da conta e apenas com as permissões que ele concedeu. Cada alteração aparece no histórico de ações da central telefônica com o nome da chave ou do aplicativo.
Endereços#
| O quê | Endereço |
|---|---|
| REST API | https://api.cenaly.com/pbx-api/v1 |
| Servidor MCP (streamable HTTP) | https://api.cenaly.com/pbx-api/mcp |
| Documentação dos métodos | api.cenaly.com/pbx-api/v1/docs |
| Descrição OpenAPI 3.1 | api.cenaly.com/pbx-api/v1/openapi.json |
| Metadados OAuth | https://api.cenaly.com/.well-known/oauth-authorization-server/pbx-api |
Chave e permissões#
A chave é criada apenas pelo proprietário da conta: Telefonia → API e MCP → Chaves → Criar chave. Um colaborador com a função "Administrador da central telefônica" não vê as chaves — a chave funciona em nome do proprietário.
- A chave no formato
cpx_…é exibida uma única vez — logo após a criação, junto com os trechos prontos para conexão. Guardamos apenas a impressão digital dela (SHA-256); não é possível recuperar a chave — só emitir uma nova. - A chave pode ser limitada por unidades (caso contrário — todas as unidades, incluindo as futuras) e por prazo (30, 90, 365 dias ou sem prazo).
- "Permissões" alteram as permissões e as unidades da chave na hora: a integração continua funcionando, e as novas permissões passam a valer a partir da próxima requisição.
- "Revogar" — imediato e definitivo: a próxima requisição com essa chave receberá 401.
- Chaves e conexões ativas — até 20 por conta.
As permissões são escolhidas por conjunto pronto ou por caixas de seleção:
| Permissão | O que concede | Conjuntos |
|---|---|---|
calls:read |
histórico de chamadas e ficha da chamada com rota | CRM, assistente de IA, somente leitura, tudo |
recordings:read |
link para a gravação da conversa (10 minutos) e transcrição | CRM, assistente de IA, somente leitura, tudo |
calls:dial |
chamada com um clique: primeiro toca o telefone do colaborador, e após o atendimento a central telefônica disca o número | CRM, assistente de IA, tudo |
extensions:read |
ramais, grupos, encaminhamento, "não perturbe" | CRM, assistente de IA, somente leitura, tudo |
extensions:write |
alterar o encaminhamento pessoal e o "não perturbe" | assistente de IA, tudo |
presence:read |
quem está livre, em chamada, falando | assistente de IA, somente leitura, tudo |
reports:read |
métricas do call center por período: nível de atendimento, espera, abandonadas, AHT, FCR, CSAT, onde perdemos chamadas, recortes por filas, linhas, colaboradores | somente leitura, tudo |
routes:read |
números, regras de entrada e saída, histórico de regras | somente leitura, tudo |
routes:write |
criar, alterar, excluir e reordenar regras de chamadas, restaurar do histórico | apenas por caixa de seleção separada |
config:export |
exportação das configurações da central telefônica em formato de arquivo | apenas por caixa de seleção separada |
config:import |
importação das configurações a partir de tabelas: pré-visualização e depois aplicação | apenas por caixa de seleção separada |
webhooks:write |
criar, alterar, testar e excluir webhooks de eventos de chamadas | apenas por caixa de seleção separada |
daynight:write |
fechar a unidade até o fim do dia ou até uma data, abrir — o mesmo que *28 pelo telefone |
apenas por caixa de seleção separada |
As permissões que alteram algo ou consomem minutos estão marcadas em âmbar na janela. As três últimas permissões não fazem parte de nenhum conjunto, nem mesmo de "Tudo" — só podem ser concedidas conscientemente, por caixa de seleção separada.
ChatGPT, claude.ai, Perplexity, Cursor, VS Code: conexão sem chave#
Os assistentes na nuvem não conseguem inserir uma chave no cabeçalho — eles conectam o servidor MCP por OAuth. Não é preciso criar uma chave.
claude.ai: Configurações → Conectores → Adicionar seu conector → cole https://api.cenaly.com/pbx-api/mcp → Conectar.
ChatGPT: Configurações → Aplicativos e conectores → Avançado → ative o modo desenvolvedor → Criar → cole https://api.cenaly.com/pbx-api/mcp, autenticação — OAuth.
Perplexity: Configurações da conta → Conectores → "+ Seu conector" → Remoto → cole https://api.cenaly.com/pbx-api/mcp, autenticação — OAuth.
Cursor e VS Code: adicione um servidor MCP remoto (HTTP) com o endereço https://api.cenaly.com/pbx-api/mcp sem cabeçalho com chave — a janela de login abrirá no navegador. No Cursor: .cursor/mcp.json → {"mcpServers": {"cenaly-pbx": {"url": "https://api.cenaly.com/pbx-api/mcp"}}}; no VS Code: comando "MCP: Add Server" → HTTP.
Em seguida, o assistente abrirá a janela de login da Cenaly. Após entrar, você verá:
- quem está pedindo acesso — o aplicativo é nomeado pelo endereço para onde a resposta retornará: "Conector verificado · chatgpt.com" (o mesmo vale para claude.ai, perplexity.ai, vscode.dev, Cursor), "Programa neste computador" ou o aviso "Endereço desconhecido". O nome enviado pelo aplicativo é exibido em letras pequenas — qualquer um pode escrevê-lo;
- permissões — está marcado o que o aplicativo pede (se ele pede "tudo", o conjunto "Assistente de IA" fica marcado); as permissões "apenas por caixa de seleção separada" nunca são marcadas de antemão;
- unidades — todas ou as selecionadas.
"Permitir" retorna você ao assistente, e ele passa a ver imediatamente as ferramentas da central telefônica. A conexão aparece em Telefonia → API e MCP → Chaves como uma linha com a etiqueta OAuth e o endereço do aplicativo — é ali também que as permissões são alteradas e o acesso é revogado. Reconectar o mesmo aplicativo atualiza suas permissões, em vez de criar uma segunda conexão.
Somente o proprietário da conta pode autorizar conexões.
Claude Code, Claude Desktop, Cursor: conexão por chave#
Claude Code — com um único 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 outros clientes com arquivo de configurações:
{
"mcpServers": {
"telephony": {
"type": "http",
"url": "https://api.cenaly.com/pbx-api/mcp",
"headers": { "Authorization": "Bearer cpx_…" }
}
}
}
Os trechos prontos para colar com a sua chave são exibidos logo após a criação da chave. O Assistente de IA vê apenas as ferramentas permitidas pela chave e pode responder a "quem ligou hoje e não foi atendido", "ligue de volta para este cliente do telefone da Anna", "ative para mim o encaminhamento para o celular".
REST API#
A chave vai no cabeçalho Authorization: Bearer <chave> (ou X-Api-Key). Se a chave tiver uma única unidade, o parâmetro location pode ser omitido; se tiver várias, a resposta 400 location_required vai listá-las.
| Método | Caminho | Permissão | O que faz |
|---|---|---|---|
| GET | /v1/me |
— | permissões, unidades e limites da chave |
| GET | /v1/locations |
— | unidades da chave e suas centrais telefônicas |
| GET | /v1/calls |
calls:read |
histórico de chamadas: since, until, direction, status (answered, missed, ai), number, extension, limit até 100, cursor |
| GET | /v1/calls/{id} |
calls:read |
chamada com o percurso: qual regra foi acionada, qual telefone tocou, quem atendeu |
| GET | /v1/calls/{id}/recording |
recordings:read |
link para a gravação por 10 minutos |
| GET | /v1/calls/{id}/transcript |
recordings:read |
transcrição e resumo da conversa |
| POST | /v1/dial |
calls:dial |
chamada com um clique: { "extension": "101", "number": "+995555123456" } |
| GET | /v1/dial/{commandId} |
calls:dial |
como está a chamada com um clique |
| GET | /v1/extensions |
extensions:read |
ramais, grupos, encaminhamento, "não perturbe" |
| PUT | /v1/extensions/{ext}/forwarding |
extensions:write |
encaminhamento pessoal do ramal |
| PUT | /v1/extensions/{ext}/dnd |
extensions:write |
"não perturbe": { "on": true } |
| GET | /v1/presence |
presence:read |
quem está livre agora |
| GET | /v1/metrics |
reports:read |
métricas do call center por período: from, to (datas locais da unidade YYYY-MM-DD, padrão — últimos 7 dias), compare (prev, year, prev,year ou vazio), sections (metrics,loss,queues,lines,operators,directions,heatmap,daily), callbacks=true — também a velocidade de retorno de chamada |
| GET | /v1/routes |
routes:read |
números, regras de entrada e de saída |
| GET | /v1/routes/history |
routes:read |
histórico de alterações das regras |
| POST, PUT, DELETE | /v1/routes/inbound…, /v1/routes/outbound… |
routes:write |
regras de chamadas uma a uma: criar, alterar os campos enviados, excluir, reordenar, reverter |
| GET, POST | /v1/config/export, /v1/config/import |
config:export, config:import |
configurações em formato de arquivo; o upload — pré-visualização e depois aplicação |
| GET | /v1/events |
calls:read |
eventos de chamadas da unidade nos últimos 7 dias (até 2000): since (id do evento ou horário), types, limit (até 500 por resposta) |
| GET, POST, PATCH, DELETE | /v1/webhooks… |
webhooks:write |
webhooks: listar, criar, alterar, excluir; …/rotate-secret, …/test, …/deliveries |
| GET, PUT | /v1/open-closed |
routes:read, daynight:write |
se está fechado e até quando; fechar até o fim do dia { "closed": true }, até uma data { "closed": true, "until": "2026-10-05T09:00" } ou abrir |
A descrição completa dos campos está na página de documentação e no OpenAPI.
Quem ligou hoje e não foi atendido:
curl -H "Authorization: Bearer cpx_…" \
"https://api.cenaly.com/pbx-api/v1/calls?status=missed&since=$(date -u +%Y-%m-%dT00:00:00Z)"
Ligar de volta para o cliente do telefone do ramal 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"
Métricas de setembro com comparação com agosto e com setembro do ano passado — para o seu painel de 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"
Na resposta, metrics traz os mesmos números que na tela Telefonia → Histórico de chamadas → Análise: chamadas recebidas, atendidas e perdidas, nível de atendimento (limite e meta — das configurações da fila), espera média e máxima, proporção de abandonadas sem chamadas curtas, AHT, FCR, proporção de transferências e de bot, CSAT. compare.prev e compare.year — os mesmos campos do período anterior e do mesmo período um ano atrás. A comparação com o ano passado é calculada para períodos de até 400 dias.
Ativar o encaminhamento para o celular se o funcionário não atender em 15 segundos:
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"
Ferramentas MCP#
As mesmas operações do REST. O cliente vê apenas as ferramentas permitidas pela chave ou pela conexão:
| Ferramentas | Permissão |
|---|---|
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 |
O servidor funciona sem sessões (streamable HTTP, JSON-RPC 2.0): initialize, tools/list, tools/call.
OAuth para desenvolvedores dos próprios conectores#
O servidor de autorização segue a especificação de autorização MCP — qualquer cliente que a suporte serve:
- Uma requisição ao MCP sem token recebe 401 com o cabeçalho
WWW-Authenticate: Bearer resource_metadata="https://api.cenaly.com/.well-known/oauth-protected-resource/pbx-api/mcp". - Os metadados do recurso (RFC 9728) indicam o servidor de autorização —
https://api.cenaly.com/pbx-api; os metadados dele (RFC 8414) ficam emhttps://api.cenaly.com/.well-known/oauth-authorization-server/pbx-api(e…/pbx-api/.well-known/openid-configuration). - Registro dinâmico do cliente (RFC 7591):
POST /pbx-api/oauth/register. O endereço de retorno é apenaslocalhost/127.0.0.1(qualquer porta) ou o endereço de um conector conhecido (claude.ai, claude.com, chatgpt.com, chat.openai.com, vscode.dev, www.perplexity.ai, enterprise.perplexity.ai; no Cursor — ainda exatamentecursor://anysphere.cursor-mcp/oauth/callback). O método de entrada do cliente énone(cliente público),client_secret_postouclient_secret_basic. - Código de autorização com PKCE (apenas
S256):GET /pbx-api/oauth/authorize→ janela de entrada e consentimento do proprietário → retorno ao seu endereço comcode,stateeiss. O parâmetroresourceé o endereço do servidor MCP. - Tokens:
POST /pbx-api/oauth/token. O código é de uso único e dura 2 minutos (umcode_verifierincorreto o invalida); o token de acesso dura 1 hora; o token de atualização dura 90 dias, a cada atualização é emitido um novo e o anterior deixa de funcionar. - Revogação:
POST /pbx-api/oauth/revoke(RFC 7009) encerra a conexão por completo — como “Revogar” na janela “API e MCP”.
O token de acesso é aceito em todos os lugares onde a chave é: tanto no MCP quanto no REST, com as permissões e unidades que o proprietário autorizou.
Limites e erros#
- Por chave ou conexão: 120 requisições por minuto e 20 000 por dia (REST e MCP juntos).
- Chamada em um clique — separadamente: 10 por minuto e 300 por dia.
- O histórico — até 100 chamadas por requisição; no histórico da central telefônica da unidade — as últimas 400 chamadas em 90 dias.
| Código | Erro | O que fazer |
|---|---|---|
| 401 | api_key_missing, api_key_invalid |
a chave não existe, foi revogada ou expirou; o token OAuth expirou — atualize-o |
| 403 | scope_required |
a chave não tem a permissão — adicione-a em “Permissões” |
| 400 | location_required |
a chave tem várias unidades — informe location |
| 404 | location_not_found |
a unidade não existe ou a chave está limitada a outras unidades |
| 429 | rate_limited |
o limite foi atingido, tente novamente em Retry-After segundos |
Eventos de chamadas#
Em tempo real — webhooks da central telefônica: Telefonia → API e MCP → Webhooks ou POST /v1/webhooks com uma chave com a permissão webhooks:write. No seu endereço https chega em segundos um POST a cada evento: call.started (começou: direção, quem, para onde, linha), call.ringing (em quem está tocando — um por telefone), call.answered (quem atendeu), call.transferred (quem transferiu e para quem), call.ended (resultado, duração, conversa, quem atendeu). O webhook pode ser limitado por unidades e tipos de 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": "Анна" } } }
Assinatura — cabeçalho X-Cenaly-Signature: t=<tempo unix>,v1=<HMAC-SHA256 de "t.corpo" com o segredo do webhook>. Recalcule, compare em tempo constante e rejeite t com mais de 5 minutos. O segredo whsec_… é mostrado uma única vez; «Novo segredo» invalida o anterior imediatamente.
Entrega — responda 2xx em 10 segundos. Caso contrário, repetimos com pausas de 10 s, 30 s, 2, 5, 10, 15 e 15 minutos (8 tentativas, cerca de uma hora); a repetição carrega o mesmo id — use-o para descartar duplicatas. O histórico de entregas (código de resposta, horário, tentativa) fica na janela do webhook e em GET /v1/webhooks/{id}/deliveries; «Verificar» envia o webhook.ping de serviço. Se o endereço não aceitou nenhuma entrega por 3 dias seguidos, o webhook se desliga sozinho: o proprietário recebe um e-mail, na janela «Webhooks» aparece a marca «Desativado automaticamente» e o botão «Ativar» (ou PATCH /v1/webhooks/{id} com { "enabled": true }).
Sem servidor próprio — o mesmo feed por consulta: GET /v1/events?since=<id do último evento> ou a ferramenta MCP get_call_events (eventos da unidade dos últimos 7 dias, até 2000; por resposta, até 500, more: true — continue a paginar com o mesmo since).
Após o processamento da gravação — a chamada com transcrição e análise chega por webhook da fonte «Webhook» na seção «Chamadas».
«Aberto / fechado» — PUT /v1/open-closed com { "closed": true } fecha a unidade até o fim do dia (como *28 pelo telefone: as chamadas seguem as regras de «fora do horário»), false — abre; GET mostra se está fechado e por quem. Com "until" — fechado até esse momento (no máximo 30 dias), depois abre sozinho: o horário sem fuso (2026-10-05T09:00) é lido no fuso da unidade; false também o remove. É preciso ter a opção «Fechar com o código *28» ativada no roteamento (caso contrário, 409).
Segurança#
- A chave é mostrada uma única vez e guardamos apenas sua impressão digital; uma chave vazada é revogada com um botão.
- A conexão OAuth só é autorizada pelo proprietário, e é visível para qual endereço o acesso vai; a conexão autorizada pode ser limitada por permissões e unidades e revogada a qualquer momento.
- Números de telefone são dados pessoais dos seus clientes: conceda permissões de histórico e gravações apenas às integrações que realmente precisam delas.
- Todas as alterações por chave ou conexão ficam no histórico de ações da central telefônica com o nome da chave ou do aplicativo.