Перейти к основному содержимому

WebSocket событий

GET /api/v1/events переходит в WebSocket, который передаёт активные события вашей компании в виде текстовых кадров JSON. Так интеграция отслеживает звонки и изменения настроек. MonoPbx не обращается к вашему серверу: исходящих вебхуков пока нет.

Полезная нагрузка событий — это JSON-конверты; их схема JSON отдаётся по адресу GET /api/v1/events/schema.

Подключение​

Сначала пройдите аутентификацию, затем оформляйте подписку. До успешной аутентификации ничего не подписывается, не повторяется и не отправляется.

КлиентКак пройти аутентификацию
Сервер, CLI, интеграцияОтправьте Authorization: Bearer <access token or mpbx_k1_ API key> в запросе на переход. Неверные учётные данные дают обычный 401 и без сокета.
Браузер (WebSocket в браузере не может задавать заголовки)Подключитесь без учётных данных и отправьте {"op":"auth","token":"..."} как первый кадр в течение 5 секунд. Любой другой первый кадр, неверный токен или молчание закрывают сокет с кодом 4401.

Никогда не передавайте токен в строке запроса. URL попадают в журналы. Cookie на этом маршруте тоже никогда не принимаются, поэтому страница на другом сайте не может открыть сокет с вашей сессией.

Системный субъект выбирает компанию через X-Mpbx-Tenant. Учётные данные компании не могут выбрать другую компанию (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);
};

По желанию передайте topics и cursor в строке запроса (?topics=call,presence&cursor=<event id>), чтобы подписаться сразу после ready. Без них сокет ждёт кадра subscribe.

Кадры​

От клиента к серверу:

КадрЗначение
{"op":"auth","token":"..."}Первый кадр сокета, который не передал учётные данные в заголовке.
{"op":"subscribe","topics":["call","presence"],"cursor":"<event id>"}Добавляет темы. Если не указать topics (или передать пустой список), подписка будет на все темы, которые вы можете видеть. cursor возобновляет доставку после этого события и принимается только в первом subscribe.
{"op":"unsubscribe","topics":["presence"]}Удаляет темы.
{"op":"ping"}Получает ответ {"op":"pong"}.

От сервера к клиенту:

КадрЗначение
{"op":"ready","protocol":1,"tenant_id":"...","topics":[...],"heartbeat_secs":20}Аутентификация пройдена. topics — темы, на которые вы можете подписаться.
{"op":"subscribed","topics":[...],"denied":[...],"resync_required":false}Активные темы. denied перечисляет запрошенные темы, которые вы не можете видеть.
{"op":"event","cursor":"<event id>","event":{...}}Одно событие. cursor равен event.id.
{"op":"replay_done","count":3}События после вашего cursor отправлены; далее идут текущие события.
{"op":"heartbeat","ts":1790000000000}Признак работоспособности, каждые heartbeat_secs.
{"op":"resync_required","reason":"events_lost"}Сервер потерял события. Загрузите состояние заново через REST.
{"op":"error","code":"MPBX-EVENTS-WS-...","detail":"..."}Отклонённый кадр. Сокет остаётся открытым, кроме случая повторных некорректных кадров (закрытие 1008).

Доставка выполняется не менее одного раза: убирайте дубликаты по id события. События от одного источника приходят по порядку.

Темы и права доступа​

Вы получаете только события своей компании и только те, которые разрешает ваша роль:

ТемаНужное правоОбласть
call, legcalls:controlотдел
queuequeues:readотдел
presencedirectory:readвся компания
voicemailvoicemail:readотдел
configправо чтения изменяемого объекта (например, users:read для пользователя)
auditaudit:readвся компания
devicedevices:readотдел

Вошедший пользователь всегда видит события, в которых участвует (свои звонки, присутствие и почтовый ящик). Ключ API этого не получает: он видит только то, что разрешают его области доступа. Изменение роли вступает в силу в течение 60 секунд, а просроченные или отозванные учётные данные закрывают сокет с кодом 4401 за то же время.

События звонков и участков звонка (leg), очередей, присутствия и голосовой почты публикуются по мере появления этих функций, поэтому некоторые темы пока могут не содержать событий.

Возобновление после разрыва​

Сохраняйте cursor последнего обработанного события. После переподключения откройте сокет с ?cursor=<it> (или передайте его в первом subscribe). Сервер отправит события вашей компании после него, затем replay_done, затем текущие события.

Сервер хранит последние 8192 события в памяти. Если ваш cursor вне этого окна, сервер был перезапущен или идентификатор не относится к событиям вашей компании, subscribed.resync_required равен true, и ничего не повторяется: загрузите состояние заново через REST, затем продолжайте по текущему потоку. Надёжное повторение после перезапусков пока не реализовано.

Ограничения​

ПараметрПо умолчанию
Признак работы (heartbeat)каждые 20 с
Тайм-аут простоя (нет кадров от вас; pong тоже учитывается)60 с, затем закрытие 4408
Время на отправку кадра auth5 с
Кадров в очереди на соединение256; очередь, которая остаётся полной 5 с, закрывает сокет (1013)
Наибольший клиентский кадр16 КиБ
Открытых соединений10 000 на установку, 5 000 на компанию, 20 на пользователя или ключ

Клиент, который читает слишком медленно, отключается и никогда не замедляет других; переподключитесь с вашим cursor.

Коды закрытия: 4401 — не аутентифицирован или учётные данные истекли; 4403 — компания не выбрана; 4408 — простой; 4429 — слишком много соединений (или закрыто, чтобы освободить место для другой компании); 1008 — злоупотребление протоколом; 1011 — ошибка сервера; 1012 — сервер перезапускается; 1013 — медленный получатель. При 1011, 1012 и 1013 переподключитесь с вашим cursor.

Звонок по клику​

POST /api/v1/calls/click-to-call с {"destination":"+15550123","device_id":"<uuid>"} — опубликованный контракт, который сейчас отвечает 501 (MPBX-NOT-IMPLEMENTED). Он проверяет вызывающего и тело запроса (400 для неверного адреса назначения), затем останавливается. Инициирование звонка из API пока не реализовано; запрос и ответ не изменятся, когда это появится.