Вебхуки

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

Сейчас вебхуки есть у брони столов.

Подключение

Адрес вебхука указывает заведение в кабинете: Интеграции → API-ключи → меню ключа → Вебхук. Там же:

  • события — какие присылать. Ничего не отмечено — приходят все;
  • секрет подписи — передайте его себе на сервер, им проверяется подпись;
  • проверить — Mison сразу отправит событие ping и покажет ответ вашего сервера;
  • последние отправки — журнал: что ушло, что не дошло, с кодом ответа и ошибкой. Недоставленное можно отправить ещё раз кнопкой.

Вебхук работает, пока у ключа включено «Бронирование столов» и не истёк срок ключа.

Запрос

POST {ваш адрес}
Content-Type: application/json
X-Mison-Event: reservation.status_changed
X-Mison-Event-Id: 6f1c2d5e-8a7b-4c3d-9e0f-1a2b3c4d5e6f
X-Mison-Signature: sha256=5d41402abc4b2a76b9719d911017c592...
{
  "event_id": "6f1c2d5e-8a7b-4c3d-9e0f-1a2b3c4d5e6f",
  "event": "reservation.status_changed",
  "occurred_at": "2026-10-25T19:04:11+05:00",
  "data": {
    "id": "72179267-7a2c-47a7-a4ae-8d1b48b2f977",
    "external_id": "booking-58123",
    "status": "opened"
  }
}

В data для событий reservation.* — бронь целиком, в том же виде, что отдаёт GET /reservations/{id} (выше показана сокращённо).

Ответьте любым кодом 2xx в течение 5 секунд — событие доставлено. Тело ответа не читается.

События

СобытиеКогдаdata
reservation.createdСоздана ваша броньБронь
reservation.updatedИзменена ваша бронь: перенос, стол, гости, итог депозитаБронь
reservation.status_changedСменился статус вашей брони: подтверждена, гость сел, счёт закрыт, не пришёл, отменена, отклоненаБронь
occupancy.changedИзменилась чужая бронь в заведении — касса, колл-центр, другой сервисЗанятость стола, без гостя
pingПроверка из кабинета{ "message": "…" }

occupancy.changed нужен, чтобы держать актуальной свою картину свободных столов:

{
  "event": "occupancy.changed",
  "data": {
    "point_id": "c91fdd54-a539-44ab-8a8f-103838d31388",
    "table_id": 11238,
    "date": "2026-10-25",
    "start_time": "18:00",
    "end_time": "20:00",
    "guests": 2,
    "is_holding": true
  }
}

is_holding: false — стол больше не держится этой бронью (её отменили, закрыли или гость не пришёл). table_id: null — бронь на заведение без стола.

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

X-Mison-Signature — HMAC-SHA256 сырого тела запроса секретом подписи, в hex, с префиксом sha256=. Считайте подпись по телу как оно пришло, до разбора JSON:

$body = file_get_contents('php://input');
$expected = 'sha256=' . hash_hmac('sha256', $body, $secret);
if (!hash_equals($expected, $_SERVER['HTTP_X_MISON_SIGNATURE'] ?? '')) {
	http_response_code(401);
	exit;
}
const crypto = require('crypto')
const expected = 'sha256=' + crypto.createHmac('sha256', secret).update(rawBody).digest('hex')
const valid = crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(req.get('X-Mison-Signature') || ''))

Секрет можно перевыпустить в кабинете — старый перестаёт работать сразу.

Повторы и порядок

Не ответили 2xx (ошибка, таймаут, сервер недоступен) — Mison повторит через 1, 5, 30 минут, 2 и 6 часов. После шестой неудачи событие помечается недоставленным; его можно отправить снова из кабинета, а пропущенное догнать лентой изменений.

Поэтому:

  • одно событие может прийти дважды — дедуплицируйте по event_id;
  • события могут прийти не по порядку (повтор старого после нового) — сравнивайте data.updated_at брони и не откатывайте свежее состояние старым.