API Reference

vPBX Panel API

Inicia llamadas Click2Call, consulta el historial con grabaciones y descarga audios desde cualquier aplicación. Obtén un token, haz una petición POST y la centralita conecta al agente con el destino.

Base URL: https://vpbx-panel-homeserve.deveco.it/ Auth: Bearer JWT Token válido: 24 horas Formato: JSON

Visión general

La API permite iniciar llamadas Click2Call, consultar el historial de llamadas con URLs de grabación y descargar audios MP3 desde cualquier sistema externo. El flujo Click2Call es simple: la centralita llama primero al teléfono del agente (la extensión indicada) y, cuando éste descuelga, conecta automáticamente con el número de destino.

Todas las peticiones a la API requieren un Bearer token JWT que se obtiene con usuario y contraseña. Los tokens caducan a las 24 horas.

Todas las peticiones y respuestas usan Content-Type: application/json, excepto GET /api/recording/:id que devuelve audio/mpeg.

Inicio rápido

1
Obtén un token

Llama a POST /api/auth/token con tus credenciales.

2
Inicia la llamada

Llama a POST /api/call incluyendo el token en la cabecera Authorization.

3
El teléfono suena

La centralita llama al softphone de la extensión. Al descolgar, conecta con el destino.

4
Renueva el token cuando expire

Cuando recibas 401 Token expirado, repite el paso 1.

Ejemplo completo

bash · curl
# 1. Obtener token
TOKEN=$(curl -s -X POST https://vpbx-panel-homeserve.deveco.it/api/auth/token \
  -H "Content-Type: application/json" \
  -d '{"username":"tu_usuario","password":"tu_contraseña"}' \
  | grep -o '"access_token":"[^"]*' | cut -d'"' -f4)

# 2. Iniciar llamada
curl -X POST https://vpbx-panel-homeserve.deveco.it/api/call \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $TOKEN" \
  -d '{"extension":"201","destination":"666123456"}'

Autenticación

POST /api/auth/token Sin autenticación

Devuelve un Bearer token JWT válido durante 24 horas. No requiere ninguna cabecera de autenticación previa.

Parámetros del cuerpo

Campo Tipo Descripción
username requerido string Nombre de usuario
password requerido string Contraseña

Petición

curl
curl -X POST https://vpbx-panel-homeserve.deveco.it/api/auth/token \
  -H "Content-Type: application/json" \
  -d '{"username": "tu_usuario", "password": "tu_contraseña"}'
javascript
const res = await fetch('/api/auth/token', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ username: 'tu_usuario', password: 'tu_contraseña' })
});
const { access_token } = await res.json();

Respuestas

200 OK
{ "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2...", "token_type": "Bearer", "expires_in": 86400 }
400: Faltan campos
{ "success": false, "message": "username y password son requeridos" }
401: Credenciales inválidas
{ "success": false, "message": "Credenciales inválidas" }
El token caduca a las 24 horas. Cuando la API devuelva 401 Token expirado, solicita uno nuevo con este endpoint.

Usar el token

Incluye el token en la cabecera Authorization de cada petición:

cabecera HTTP
Authorization: Bearer ey...
Guarda el token y reutilízalo en todas las peticiones hasta que expire. No es necesario pedir un token nuevo en cada llamada.

Iniciar llamada

POST /api/call 🔒 Requiere token

Inicia una llamada Click2Call. La centralita marca al softphone de la extension indicada. Cuando el agente descuelga, conecta automáticamente con destination.

Parámetros del cuerpo

Campo Tipo Default Descripción
extension requerido string Extensión SIP (Session Initiation Protocol) del agente que recibirá la llamada primero (ej. "201")
destination requerido string Número a marcar. Espacios, guiones y paréntesis se eliminan automáticamente; se conserva +.
timeout opcional number 30 Segundos que suena el softphone del agente antes de cancelar
autoAnswer opcional boolean false Si true, el softphone contesta automáticamente sin que el agente descuelgue

Petición

curl
curl -X POST https://vpbx-panel-homeserve.deveco.it/api/call \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <token>" \
  -d '{
    "extension":   "201",
    "destination": "666 123 456",
    "timeout":     30
  }'
javascript
const res = await fetch('/api/call', {
  method: 'POST',
  headers: {
    'Content-Type':  'application/json',
    'Authorization': `Bearer ${access_token}`
  },
  body: JSON.stringify({
    extension:   '201',
    destination: '666123456'
  })
});
const data = await res.json();

Respuestas

200 OK
{ "success": true, "variables": { "callId": "abc-123-def-456" } }
400: Faltan parámetros
{ "success": false, "message": "Faltan parámetros: extension y destination son requeridos" }
401: Token ausente o expirado
{ "success": false, "message": "Token expirado" }
500: Error de conexión con vPBX
{ "success": false, "message": "Error al iniciar la llamada", "error": "connect ECONNREFUSED 185.x.x.x:443" }
El campo callId identifica la llamada en el sistema. Guárdalo si necesitas referenciarlo posteriormente.

Listado de llamadas

POST /api/calls 🔒 Requiere token

Devuelve el historial de llamadas (CDR) del periodo indicado, con una recordingUrl lista para usar en cada llamada que tenga grabación disponible. Se pueden filtrar por extensiones concretas o recuperar todas.

Parámetros del cuerpo

Campo Tipo Default Descripción
from requerido string Fecha de inicio del periodo, formato DD/MM/YYYY (ej. "16/01/2026").
to requerido string Fecha de fin del periodo, formato DD/MM/YYYY (ej. "20/01/2026").
extensions opcional array<string> Lista de extensiones a filtrar como origen (src) o destino (dst), ej. ["201", "202"]. Si se omite, se devuelven las llamadas de todas las extensiones. Las llamadas se deduplicarán por callId.

Petición

curl
curl -X POST https://vpbx-panel-homeserve.deveco.it/api/calls \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <token>" \
  -d '{
    "from":       "16/01/2026",
    "to":         "20/01/2026",
    "extensions": ["201", "202"]
  }'
javascript
const res = await fetch('/api/calls', {
  method: 'POST',
  headers: {
    'Content-Type':  'application/json',
    'Authorization': `Bearer ${access_token}`
  },
  body: JSON.stringify({
    from:       '16/01/2026',
    to:         '20/01/2026',
    extensions: ['201', '202']
  })
});
const calls = await res.json();

Respuestas

En caso de éxito, devuelve un array de objetos de llamada ordenado por fecha descendente. Campos de cada objeto:

Campo Tipo Descripción Notas
callId String Identificador único de la llamada
created long Fecha de la llamada Timestamp en milisegundos desde el 1/1/1970 (GMT)
duration int Duración total de la llamada En segundos
billsec int Duración con la llamada descolgada En segundos
hangupCause String Causa de cuelgue Valores posibles:
NORMAL_CLEARING: Cuelgue normal; una de las partes terminó la llamada
NO_ANSWER: El destino no contestó en el tiempo límite
NO_USER_RESPONSE: El destino no respondió en absoluto (sin señal de progreso)
USER_BUSY: El destino estaba ocupado
BLIND_TRANSFER: La llamada fue transferida sin consulta previa (transferencia ciega)
type String Dirección de la llamada Valores posibles:
OUTBOUND: Llamada saliente
INBOUND: Llamada entrante
dst String Número de destino
src String Número de origen
srcName String Nombre del origen
did String Número externo por el que entró la llamada
c2c Boolean Si la llamada fue originada mediante Click2Call
c2cRequestIp String IP desde la que se originó el Click2Call
c2cFrom String Número de origen del Click2Call
c2cTo String Número de destino del Click2Call
recording Boolean Si hay grabación disponible
recordingUrl String URL para descargar la grabación MP3 null si no hay grabación. Campo añadido por este servidor.
hangupRemote Boolean Si el cuelgue lo inició el extremo remoto (el destino) false significa que colgó el agente/extensión local
transferUuid String UUID de la llamada destino cuando se realizó una transferencia null si la llamada no fue transferida
outboundCid String Número presentado al destino como Caller ID en llamadas salientes Corresponde al DID (Direct Inward Dialing - número virtual utilizado para comunicaciones de larga distancia) de la extensión que originó la llamada
remoteHeader1 String Valor de una cabecera SIP (Session Initiation Protocol) personalizada enviada por el extremo remoto null en llamadas salientes normales. Útil para integración CTI en llamadas entrantes.
var1var5 String Variables personalizadas asignables durante el flujo de la llamada (IVR, dialplan) null si no se configuran en la centralita
queueId String ID de la cola Solo si la llamada pasó por una cola
queueWaitTime int Tiempo de espera en la cola En segundos. Solo si la llamada pasó por una cola
queueAgent String Agente que atendió la llamada Solo si la llamada pasó por una cola
queueCause String Causa de colgado en la cola Solo si la llamada pasó por una cola. Valores posibles:
cancel: No ha sido atendido el llamante
answered: Se ha atendido al llamante
queueReason String Razón de colgado en la cola cuando queueCause es cancel Solo si la llamada pasó por una cola. Valores posibles:
NONE: Sin razón específica
TIMEOUT: Superado el tiempo máximo de espera en cola
NO_AGENT_TIMEOUT: Superado el tiempo máximo esperando a un agente
BREAK_OUT: El llamante abandonó la cola
200 OK: Llamada completada (con grabación)
[ { "callId": "f10929a0-9bc5-4b5c-9bf4-5dcc8300846a", "created": 1777286020000, "duration": 344, "billsec": 331, "hangupCause": "NORMAL_CLEARING", "type": "OUTBOUND", "dst": "635842308", "src": "202", "did": null, "srcName": "202", "c2c": true, "c2cRequestIp": null, "c2cFrom": "202", "c2cTo": "635842308", "c2cReference": "d8b5b25c-5027-406a-8614-865248ae8c32", "recording": true, "queueId": null, "queueWaitTime": null, "queueAgent": null, "queueCause": null, "queueReason": null, "queueRelatedId": null, "hangupRemote": false, "transferUuid": null, "outboundCid": "876006257", "remoteHeader1": null, "var1": null, "var2": null, "var3": null, "var4": null, "var5": null, "recordingUrl": "https://vpbx-panel-homeserve.deveco.it/api/recording/f10929a0-9bc5-4b5c-9bf4-5dcc8300846a" } ]
200 OK: Llamada sin contestar (sin grabación)
[ { "callId": "xyz-789-ghi-012", "created": 1777282400000, "duration": 38, "billsec": 0, "hangupCause": "NO_ANSWER", "type": "OUTBOUND", "dst": "912345678", "src": "201", "recording": false, "recordingUrl": null } ]
400: Campos obligatorios ausentes o fecha inválida
{ "success": false, "message": "Los campos from y to son obligatorios (formato DD/MM/YYYY, ej: 15/03/2026)" }
401: Token ausente o expirado
{ "success": false, "message": "Token expirado" }
500: Error de conexión
{ "success": false, "message": "Error al obtener el listado de llamadas", "error": "connect ECONNREFUSED 185.x.x.x:443" }
El campo recordingUrl apunta directamente al endpoint GET /api/recording/:id de este servidor. Si la llamada no tiene grabación, el campo vale null.

Descargar grabación

GET /api/recording/:callId 🔒 Requiere token

Descarga el audio MP3 de una llamada grabada. El callId se obtiene del campo callId devuelto por POST /api/calls o directamente de la recordingUrl incluida en cada registro.

Parámetros de ruta

Parámetro Tipo Descripción
callId requerido string Identificador único de la llamada (ej. abc-123-def-456)

Petición

curl
curl -X GET https://vpbx-panel-homeserve.deveco.it/api/recording/abc-123-def-456 \
  -H "Authorization: Bearer <token>" \
  --output recording.mp3
javascript
const res = await fetch('/api/recording/abc-123-def-456', {
  headers: { 'Authorization': `Bearer ${access_token}` }
});
const blob = await res.blob();
// Reproducir directamente en el navegador
const url = URL.createObjectURL(blob);
const audio = new Audio(url);
audio.play();

Respuestas

200 OK  ·  Content-Type: audio/mpeg
<binary MP3 data>
404: Grabación no encontrada
{ "success": false, "message": "Grabación no encontrada", "error": "HTTP 404" }
La respuesta incluye la cabecera Content-Disposition: inline; filename="recording-{callId}.mp3", por lo que puede reproducirse directamente en el navegador o guardarse en disco.

Listar extensiones

GET /api/extension 🔒 Requiere token

Devuelve un array JSON con todas las extensiones de la centralita asignada a la API Key. Los campos sipPassword y webPassword se omiten de la respuesta.

Campos de cada objeto

Campo Tipo Descripción
id String Identificador único de la extensión
name String Nombre descriptivo de la extensión
username String Número de extensión / username SIP
type String Tipo de extensión (ej. SIP)
licenseType String Tipo de licencia asignada (ej. BUSINESS)
email String Email asociado a la extensión
domain String Dominio SIP (Session Initiation Protocol) de la centralita al que debe conectarse el softphone
cw Boolean Call Waiting: si true, puede recibir llamadas mientras está ocupado
dnd Boolean Do Not Disturb: si true, rechaza todas las llamadas entrantes
webLogin Boolean Si el acceso al portal web está habilitado para esta extensión

Petición

curl
curl -X GET https://vpbx-panel-homeserve.deveco.it/api/extension \
  -H "Authorization: Bearer <token>"
javascript
const res = await fetch('/api/extension', {
  headers: { 'Authorization': `Bearer ${access_token}` }
});
const extensions = await res.json();

Respuestas

200 OK
[ { "id": "8a9485949bd8a034019bdaf6a0d701c6", "name": "Agente 2", "username": "202", "type": "SIP", "licenseType": "BUSINESS", "email": "[email protected]", "domain": "deveco-abbant.vpbx.me", "cw": true, "dnd": false, "webLogin": true } ]
401: Token ausente o expirado
{ "success": false, "message": "Token expirado" }
500: Error de conexión
{ "success": false, "message": "Error al obtener las extensiones", "error": "connect ECONNREFUSED 185.x.x.x:443" }

Buscar extensión por username

GET /api/extension/findbyusername/:username 🔒 Requiere token

Devuelve el extensionId correspondiente al username indicado. Útil para obtener el ID necesario para las otras operaciones sobre extensiones.

Parámetros de ruta

Parámetro Tipo Descripción
username requerido string Número o nombre de usuario de la extensión (ej. 100)

Petición

curl
curl -X GET https://vpbx-panel-homeserve.deveco.it/api/extension/findbyusername/100 \
  -H "Authorization: Bearer <token>"
javascript
const res = await fetch('/api/extension/findbyusername/100', {
  headers: { 'Authorization': `Bearer ${access_token}` }
});
const { extensionId } = await res.json();

Respuestas

200 OK
{ "extensionId": "ff8081814a53b218014a53b2206c00ff", "username": "100" }
401: Token ausente o expirado
{ "success": false, "message": "Token expirado" }

Datos de una extensión

GET /api/extension/:extensionId 🔒 Requiere token

Devuelve los datos de una extensión a partir de su id. Los campos sipPassword y webPassword se omiten de la respuesta. Los campos devueltos son los mismos que en GET /api/extension.

Parámetros de ruta

Parámetro Tipo Descripción
extensionId requerido string Campo id devuelto por GET /api/extension

Petición

curl
curl -X GET https://vpbx-panel-homeserve.deveco.it/api/extension/8a9485949bd8a034019bdaf6a0d701c6 \
  -H "Authorization: Bearer <token>"
javascript
const res = await fetch(`/api/extension/${extensionId}`, {
  headers: { 'Authorization': `Bearer ${access_token}` }
});
const ext = await res.json();

Respuestas

200 OK
{ "id": "8a9485949bd8a034019bdaf6a0d701c6", "name": "Agente 2", "username": "202", "type": "SIP", "licenseType": "BUSINESS", "email": "[email protected]", "domain": "deveco-abbant.vpbx.me", "cw": true, "dnd": false, "webLogin": true }
401: Token ausente o expirado
{ "success": false, "message": "Token expirado" }

Códigos de error

Todas las respuestas de error incluyen un campo message con la descripción del problema.

4xx: Ejemplo de error
{ "success": false, "message": "Token expirado" }
Código Mensaje Qué hacer
400 username y password son requeridos Incluye ambos campos en el body de /api/auth/token
400 Faltan parámetros: extension y destination son requeridos Incluye extension y destination en el body de /api/call
400 Los campos from y to son obligatorios (formato DD/MM/YYYY, ej: 15/03/2026) Incluye ambos campos en el body de /api/calls
400 Formato de fecha inválido. Usa DD/MM/YYYY (ej: 15/03/2026) Verifica que el formato sea exactamente DD/MM/YYYY con día, mes y año válidos
401 Credenciales inválidas Comprueba usuario y contraseña
401 Token requerido Añade la cabecera Authorization: Bearer <token>
401 Token expirado Solicita un token nuevo con POST /api/auth/token
401 Token inválido El token está malformado. Solicita uno nuevo.
404 Grabación no encontrada El callId no existe o la llamada no tiene grabación asociada
500 Error al iniciar la llamada Error interno o de conexión con la centralita. Inténtalo de nuevo o contacta con soporte.
500 Error al obtener el listado de llamadas Error interno o de conexión con la centralita al consultar el CDR. Inténtalo de nuevo.
500 Error al obtener las extensiones Error interno o de conexión con la centralita al consultar /api/extension. Inténtalo de nuevo.
500 Error al actualizar la extensión Error interno o de conexión con la centralita. Inténtalo de nuevo.
502 Respuesta inesperada La centralita devolvió un formato no esperado. Contacta con soporte si persiste.