API — webhooks и события

Что это

Webhooks — механизм уведомления вашей системы о событиях в кабинете в реальном времени. Вместо того чтобы периодически опрашивать API, вы регистрируете URL — и MCM шлёт POST-запрос на него при каждом событии.

Подписка

Через UI: /integrations → раздел «Webhooks» → форма «URL + события» → «+».

Через API:

POST /api/v1/webhooks
Authorization: Bearer mcm_...
Content-Type: application/json

{
  "url": "https://crm.example.com/mcm-hook",
  "events": ["call.end", "callback.requested"]
}

Ответ:

{
  "ok": true,
  "id": 7,
  "secret": "1f2c3d...",
  "note": "Сохраните secret — больше он не будет показан."
}

Этот secret используется для проверки подписи каждого пуша.

Формат пуша

POST https://crm.example.com/mcm-hook
Content-Type: application/json
X-MCM-Event: call.end
X-MCM-Signature: sha256=<hex>
User-Agent: mcm-webhook/1.0

{
  "event": "call.end",
  "tenant_id": "uuid",
  "data": { ... },
  "ts": "2026-05-01T22:30:00+00:00"
}

Проверка подписи

Python:

import hmac, hashlib

def verify(secret, body, header_signature):
    expected = 'sha256=' + hmac.new(secret.encode(), body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, header_signature)

# в обработчике:
sig = request.headers['X-MCM-Signature']
if not verify(SECRET, request.body, sig):
    return Response(403)

PHP:

function verifyMcmWebhook(string $secret, string $body, string $sig): bool {
    $expected = 'sha256=' . hash_hmac('sha256', $body, $secret);
    return hash_equals($expected, $sig);
}

Поддерживаемые события

call.start Начало звонка
call.end Завершение звонка
call.answered Ответ на звонок
call.missed Пропущенный звонок
chat.new Новый чат с сайта
chat.message Новое сообщение в чате
callback.requested Заказ обратного звонка из коллтрекинга/виджета
transcript.ready Транскрипция готова
* Все события

Retry policy

Если ваш сервер вернул статус не 2xx или не ответил — MCM повторяет доставку:

  • Попытка 1 — сразу
  • Попытка 2 — через 60 секунд
  • Попытка 3 — через 5 минут
  • Попытка 4 — через 30 минут
  • Попытка 5 — через 2 часа

После 5 неудачных попыток событие помечается delivered_at = now() с last_error = "GIVE UP" и больше не отправляется. Статус последней попытки виден в /integrations.

Идемпотентность

Каждое событие имеет уникальный data.linkedid (для звонков) или data.session_id (для чатов). Используйте это поле как идемпотентность-ключ — при повторной доставке (retry) одного и того же события данные те же.

Типичные интеграции

  • amoCRM/Bitrix24 — подпишитесь на call.end, callback.requested. По call.end создавайте сделку/задачу; по callback.requested — лид.
  • Slack/Telegram — на call.missed шлите сообщение оператору с номером и временем.
  • Google Sheets / BI — на call.end добавляйте строку в таблицу для аналитики.