WebSocket de eventos
GET /api/v1/events cambia a un WebSocket que transmite los eventos activos de su empresa como marcos de texto JSON. Así una integración sigue las llamadas y los cambios de configuración. MonoPbx no llama a su servidor: todavía no hay webhooks salientes.
Las cargas de los eventos son sobres JSON; su esquema JSON se sirve en GET /api/v1/events/schema.
Conexión
Autentíquese primero y luego suscríbase. Nada se suscribe, se reproduce ni se envía antes de que la autenticación sea exitosa.
| Cliente | Cómo autenticarse |
|---|---|
| Servidor, CLI, integración | Envíe Authorization: Bearer <token de acceso o clave de API mpbx_k1_> en la solicitud de actualización. Una credencial incorrecta es un 401 simple y no se abre el socket. |
| Navegador (un WebSocket del navegador no puede definir encabezados) | Conéctese sin credencial y envíe {"op":"auth","token":"..."} como primer marco en un plazo de 5 segundos. Cualquier otro primer marco, un token incorrecto o el silencio cierran el socket con el código 4401. |
Nunca ponga el token en la cadena de consulta. Las URL terminan en los registros. Esta ruta tampoco acepta cookies, para que una página de otro sitio no pueda abrir el socket con su sesión.
Un principal de sistema selecciona la empresa con X-Mpbx-Tenant. Una credencial de empresa no puede seleccionar otra empresa (403).
// Browser
const ws = new WebSocket("wss://pbx.example.com/api/v1/events");
ws.onopen = () => ws.send(JSON.stringify({ op: "auth", token: accessToken }));
ws.onmessage = (m) => {
const f = JSON.parse(m.data);
if (f.op === "ready") ws.send(JSON.stringify({ op: "subscribe", topics: ["call", "presence"] }));
if (f.op === "event") handle(f.event, f.cursor);
};
Opcionalmente, pase topics y cursor en la consulta (?topics=call,presence&cursor=<id del evento>) para suscribirse justo después de ready. Sin ellos, el socket espera un marco subscribe.
Marcos
Del cliente al servidor:
| Marco | Significado |
|---|---|
{"op":"auth","token":"..."} | Primer marco de un socket que no envió credencial en el encabezado. |
{"op":"subscribe","topics":["call","presence"],"cursor":"<id del evento>"} | Agrega temas. Omitir topics (o una lista vacía) significa todos los temas que usted puede ver. cursor reanuda después de ese evento y solo se acepta en el primer subscribe. |
{"op":"unsubscribe","topics":["presence"]} | Quita temas. |
{"op":"ping"} | Se responde con {"op":"pong"}. |
Del servidor al cliente:
| Marco | Significado |
|---|---|
{"op":"ready","protocol":1,"tenant_id":"...","topics":[...],"heartbeat_secs":20} | Autenticado. topics son los temas a los que puede suscribirse. |
{"op":"subscribed","topics":[...],"denied":[...],"resync_required":false} | Los temas activos. denied enumera los temas solicitados que no puede ver. |
{"op":"event","cursor":"<id del evento>","event":{...}} | Un evento. cursor es igual a event.id. |
{"op":"replay_done","count":3} | Se enviaron los eventos posteriores a su cursor; siguen los eventos en vivo. |
{"op":"heartbeat","ts":1790000000000} | Señal de vida, cada heartbeat_secs. |
{"op":"resync_required","reason":"events_lost"} | El servidor perdió eventos. Recargue su estado por REST. |
{"op":"error","code":"MPBX-EVENTS-WS-...","detail":"..."} | Un marco rechazado. El socket permanece abierto, salvo después de marcos mal formados repetidos (cierre 1008). |
La entrega es al menos una vez: elimine duplicados por el id del evento. Los eventos de un mismo productor llegan en orden.
Temas y permisos
Solo recibe los eventos de su empresa, y solo los que permite su rol:
| Tema | Permiso necesario | Alcance |
|---|---|---|
call, leg | calls:control | departamento |
queue | queues:read | departamento |
presence | directory:read | toda la empresa |
voicemail | voicemail:read | departamento |
config | el permiso de lectura de la entidad modificada (por ejemplo users:read para un usuario) | |
audit | audit:read | toda la empresa |
device | devices:read | departamento |
Un usuario que inició sesión siempre ve los eventos en los que participa (sus propias llamadas, presencia y buzón). Una clave de API no obtiene esto: solo ve lo que permiten sus ámbitos. Un cambio de rol surte efecto en un plazo de 60 segundos, y una credencial vencida o revocada cierra el socket con 4401 en ese mismo plazo.
Los eventos de llamada, pierna, cola, presencia y buzón se publican a medida que se lanzan las funciones, así que algunos temas pueden no tener eventos todavía.
Reanudar después de una desconexión
Guarde el cursor del último evento que procesó. Después de reconectarse, abra el socket con ?cursor=<it> (o póngalo en el primer subscribe). El servidor envía los eventos de su empresa posteriores a él, luego replay_done, y después los eventos en vivo.
El servidor guarda en memoria los últimos 8192 eventos. Si su cursor está fuera de esa ventana, el servidor se reinició, o el id no es uno de los eventos de su empresa, subscribed.resync_required es true y no se reproduce nada: recargue su estado por REST y luego continúe desde el flujo en vivo. La reproducción duradera tras reinicios todavía no está construida.
Límites
| Parámetro | Valor predeterminado |
|---|---|
| Señal de vida (heartbeat) | cada 20 s |
| Tiempo de inactividad (sin marcos de usted; un pong cuenta) | 60 s, luego cierre 4408 |
Tiempo para enviar el marco auth | 5 s |
| Marcos en cola por conexión | 256; una cola que permanece llena 5 s cierra el socket (1013) |
| Marco de cliente más grande | 16 KiB |
| Conexiones abiertas | 10 000 por instalación, 5 000 por empresa, 20 por usuario o clave |
Un cliente que lee demasiado lento se desconecta y nunca frena a los demás; reconéctese con su cursor.
Códigos de cierre: 4401 no autenticado o credencial terminada, 4403 no hay empresa seleccionada, 4408 inactivo, 4429 demasiadas conexiones (o cerrado para dar lugar a otra empresa), 1008 abuso de protocolo, 1011 error del servidor, 1012 el servidor se está reiniciando, 1013 consumidor lento. Para 1011, 1012 y 1013, reconéctese con su cursor.
Llamar con un clic
POST /api/v1/calls/click-to-call con {"destination":"+15550123","device_id":"<uuid>"} es un contrato publicado que actualmente responde 501 (MPBX-NOT-IMPLEMENTED). Autentica a quien llama y valida el cuerpo (400 para un destino incorrecto), y luego se detiene. Originar llamadas desde la API todavía está pendiente; la solicitud y la respuesta no cambiarán cuando eso ocurra.