Бронь столов

Раздел для сервисов бронирования: приложений и сайтов, где гость выбирает заведение, время и количество гостей. Через API вы видите залы и свободные столы, ставите бронь, переносите и отменяете её, а о посадке гостя, закрытии счёта и неявке узнаёте вебхуком.

Бронь, поставленная через API, — обычная бронь заведения: кассир видит её на схеме зала с названием вашего сервиса, при посадке открывает на столе счёт, а депозит, который вы приняли у гостя, ложится в этот счёт оплатой.

Доступ

Раздел открыт только ключу с галочкой «Бронирование столов». Её включает заведение в кабинете: Интеграции → API-ключи → Редактировать. Без галочки любой метод раздела отвечает 403:

{ "message": "У ключа нет доступа к бронированию столов. Включите его в кабинете: Интеграции → API-ключи" }

Там же, в меню ключа, заведение указывает адрес вашего вебхука.

Порядок работы

  1. Прочитайте правила точки — GET /reservations/settings: длительность брони, часы работы, как заведение принимает брони без стола.
  2. Загрузите залы и столы — GET /reservations/halls.
  3. Когда гость выбирает дату, покажите свободное время — GET /reservations/availability.
  4. Поставьте бронь — POST /reservations. На стол или «на заведение», без стола.
  5. Получайте события вебхуком: посадка, закрытие, неявка, отказ заведения.
  6. Если гость передумал — перенесите или отмените бронь.

Бронь на стол и бронь на заведение

На стол — вы передаёте table_id, выбранный по залам и свободным столам. Такая бронь подтверждается сразу (created): стол проверяется на пересечения при создании, ответ синхронный.

На заведение — table_id не передаётся: «4 гостя в 19:00». Стол назначит кассир заранее или посадит гостя на свободный, когда тот придёт. Так удобно сервисам, которым схема зала не нужна. Как такая бронь подтверждается, решает заведение:

  • по умолчанию она приходит заявкой (pending): кассир подтверждает её (created) или отклоняет с причиной (declined), вы узнаёте об этом вебхуком;
  • заведение может включить автоподтверждение с лимитом «не больше N гостей без стола на час начала» — в пределах лимита бронь сразу created, сверх лимита — заявка.

Лимит виден в правилах точки — unassigned_auto_confirm_guests_per_hour. Заявку, которую заведение не подтвердило до начала брони, Mison отклоняет сам (declined, cancelled_by: system).

Статусы

СтатусЧто значитКто ставит
pendingЗаявка, ждёт решения заведенияMison (бронь без стола сверх лимита)
createdПодтверждена, ждём гостяMison или заведение
openedГость сел, на столе открыт счётЗаведение
closedСчёт закрытЗаведение
cancelledОтменена — гостем, вами или заведением (cancelled_by)Вы или заведение
no_showГость не пришёлЗаведение или Mison через no_show_after_minutes после начала
declinedЗаявку отклонилиЗаведение или Mison

Вы можете только создать, перенести и отменить бронь. Посадку, закрытие и «не пришёл» ставит заведение — вы узнаёте о них вебхуком или в ленте изменений.

Время

Всё время — местное время точки без часового пояса (19:00), даты — YYYY-MM-DD. Пояс точки — в правилах точки, поле timezone.

Бронь через полночь допустима: начало 23:30, конец 01:30 — это 2 часа, конец на следующий день. Длительность — от 15 минут до 12 часов, бронь — не дальше чем на 90 дней вперёд и не в прошлом.

Правила точки

GET /reservations/settings?point_id={uuid}
{
  "point_id": "c91fdd54-a539-44ab-8a8f-103838d31388",
  "point_name": "Центральная",
  "timezone": "Asia/Tashkent",
  "default_duration_minutes": 120,
  "min_duration_minutes": 15,
  "max_duration_minutes": 720,
  "max_days_ahead": 90,
  "slot_step_minutes": 30,
  "no_show_after_minutes": 60,
  "unassigned_auto_confirm_guests_per_hour": null,
  "working_hours": [
    { "day": 1, "is_open": true, "open": "10:00", "close": "23:00" },
    { "day": 7, "is_open": true, "open": "10:00", "close": "02:00" }
  ]
}
ПолеОписание
default_duration_minutesСколько длится бронь, если вы не передали конец
no_show_after_minutesЧерез сколько минут после начала бронь без посадки становится no_show. 0 — заведение отмечает неявку только вручную
unassigned_auto_confirm_guests_per_hourЛимит автоподтверждения броней без стола. null — каждая такая бронь приходит заявкой
working_hoursДень недели 1 — понедельник … 7 — воскресенье. close раньше open — работает за полночь

Залы и столы

GET /reservations/halls?point_id={uuid}
{
  "point_id": "c91fdd54-a539-44ab-8a8f-103838d31388",
  "halls": [
    {
      "id": 2332,
      "name": "Основной зал",
      "tables": [
        { "id": 11237, "name": "1", "capacity": 4 },
        { "id": 11238, "name": "2", "capacity": null }
      ]
    }
  ]
}

capacity — вместимость стола; null — заведение её не указало. Залы и столы меняются редко — кешируйте их на час.

Свободные столы и время

GET /reservations/availability?point_id={uuid}&date=2026-10-25&guests=4&time=19:00
ПараметрОписание
point_idUUID точки, обязательно
dateДата брони, обязательно
guestsЧисло гостей, обязательно. Столы меньшей вместимости отсеиваются, столы без вместимости — нет
timeВремя начала. С ним — свободные столы на этот интервал, без него — сетка слотов на день
duration_minutesДлительность. По умолчанию — default_duration_minutes точки

На конкретное время:

{
  "point_id": "c91fdd54-a539-44ab-8a8f-103838d31388",
  "date": "2026-10-25",
  "time": "19:00",
  "end_time": "21:00",
  "duration_minutes": 120,
  "guests": 4,
  "is_available": true,
  "free_tables_count": 6,
  "unassigned_reservations": 1,
  "tables": [
    { "id": 11237, "name": "1", "hall_id": 2332, "hall_name": "Основной зал", "capacity": 4 }
  ]
}

Без time — сетка на день по часам работы точки с шагом slot_step_minutes; прошедшее время сегодняшнего дня не показывается:

{
  "point_id": "c91fdd54-a539-44ab-8a8f-103838d31388",
  "date": "2026-10-25",
  "guests": 4,
  "duration_minutes": 120,
  "is_open": true,
  "slots": [
    { "time": "10:00", "date": "2026-10-25", "is_available": true, "free_tables_count": 7, "table_ids": [11237, 11238] }
  ]
}

Как считается свободное:

  • стол занимают брони в статусах pending, created, opened — от всех источников: кассы, колл-центра, других сервисов. Отменённые, закрытые и «не пришёл» стол не держат;
  • брони без стола на это время уменьшают free_tables_count — каждая займёт один стол, но какой, пока неизвестно (unassigned_reservations);
  • на сегодня занятыми считаются и столы, за которыми сейчас сидят гости без брони.

Ответ — снимок на момент запроса. Стол могут занять между вашими запросами, поэтому окончательно занятость проверяет создание брони.

Создать бронь

POST /reservations
{
  "point_id": "c91fdd54-a539-44ab-8a8f-103838d31388",
  "external_id": "booking-58123",
  "date": "2026-10-25",
  "start_time": "19:00",
  "guests": 4,
  "table_id": 11237,
  "guest": { "phone": "+998901234567", "name": "Азиз" },
  "comment": "У окна, день рождения",
  "deposit": { "amount": 200000, "payment_type_id": 810 }
}
ПолеОписание
external_idОбязательно. Ваш id брони. Повтор с тем же external_id вернёт уже созданную бронь с кодом 200 — повторяйте запрос при таймауте без опаски
date, start_timeОбязательно. Местное время точки
end_time или duration_minutesКонец брони. Без них — default_duration_minutes точки
guestsОбязательно, число гостей
table_idСтол. Без него — бронь на заведение, см. выше
guest.phoneОбязательно. Гость находится по телефону или заводится в базе заведения
guest.nameКак гость назвался у вас. Кассир увидит это имя; карточку клиента заведения оно не переписывает
typetable (по умолчанию) или banquet
commentДо 255 символов
depositДепозит, который вы приняли у гостя

Ответ 201 — бронь целиком:

{
  "id": "72179267-7a2c-47a7-a4ae-8d1b48b2f977",
  "external_id": "booking-58123",
  "point_id": "c91fdd54-a539-44ab-8a8f-103838d31388",
  "status": "created",
  "type": "table",
  "table": { "id": 11237, "name": "1", "hall_id": 2332, "hall_name": "Основной зал" },
  "date": "2026-10-25",
  "start_time": "19:00",
  "end_time": "21:00",
  "duration_minutes": 120,
  "guests": 4,
  "guest": { "name": "Азиз", "phone": "998901234567" },
  "comment": "У окна, день рождения",
  "deposit": { "amount": 200000, "payment_type_id": 810, "status": "held" },
  "cancel_reason": null,
  "cancelled_by": null,
  "created_at": "2026-10-20 19:28:28",
  "updated_at": "2026-10-20 19:28:28",
  "status_changed_at": "2026-10-20 19:28:28"
}

id — UUID брони в Mison, по нему работают остальные методы.

Стол занят — 409, в conflict — интервал, которым он занят (без данных чужого гостя):

{
  "message": "Стол уже занят: 19:00 - 21:00",
  "conflict": { "table_id": 11237, "date": "2026-10-25", "start_time": "19:00", "end_time": "21:00" }
}

Депозит

Если гость внёс у вас предоплату, передайте её в deposit:

  • amount — сумма;
  • payment_type_id — тип оплаты из GET /payment-types, который заведение завело под ваши депозиты (например, «Депозит — ваш сервис»). От его настройки зависит, как заведение учитывает эти деньги: поступлением на счёт или долгом сервиса.

Деньги держите вы — своим эквайрингом на счёт заведения или у себя с выплатой по договору с заведением. Mison в расчёты не вмешивается, он учитывает депозит:

deposit.statusКогда
heldВнесён, ждёт визита
appliedГость сел — депозит лёг в счёт оплатой, кассир берёт только остаток
retainedГость не пришёл или отменил, депозит удержан
refundedДепозит возвращён гостю

После неявки (no_show) Mison депозит сам не возвращает и не удерживает: решение — по вашим условиям с гостем, итог сообщите методом POST /reservations/{id}/deposit.

Перенести бронь

PATCH /reservations/{id}

Передавайте только то, что меняется: date, start_time, end_time, duration_minutes, guests, table_id (null — снять стол), comment, guest.name, deposit. Только пока бронь ждёт гостя (pending, created), иначе 409.

{ "start_time": "19:30", "guests": 5 }

Сдвиг начала сохраняет длительность брони. Ответ — бронь целиком; занятый стол — 409 с conflict. Перенос брони без стола заново проверяет лимит автоподтверждения: сверх лимита бронь вернётся в заявку.

Отменить бронь

POST /reservations/{id}/cancel
{ "reason": "Гость передумал", "cancelled_by": "guest" }
ПолеОписание
reasonПричина, её увидит кассир
cancelled_byguest (по умолчанию) — отменил гость; partner — отменили вы
deposit_statusОбязательно, если по брони внесён депозит: retained или refunded

Только пока бронь ждёт гостя. Повторная отмена уже отменённой брони отвечает 200.

Итог депозита

POST /reservations/{id}/deposit
{ "status": "retained" }

После неявки (no_show), отмены или отказа заведения: retained — депозит удержан, refunded — возвращён гостю. Сообщается один раз.

Список броней

GET /reservations?point_id={uuid}&date_from=2026-10-25&date_to=2026-10-31&status=created,pending

Ваши брони за период по дате брони, до 31 дня. Без дат — на сегодня. status — один или несколько через запятую.

С include=occupancy в ответе ещё и занятость столов чужими бронями за тот же период — без данных гостя:

{
  "count": 1,
  "has_more": false,
  "reservations": [ { "id": "72179267-…", "status": "created" } ],
  "occupancy": [
    { "point_id": "c91fdd54-…", "table_id": 11238, "date": "2026-10-25", "start_time": "18:00", "end_time": "20:00", "guests": 2, "is_seated": false }
  ]
}

Лента изменений

GET /reservations?changed_since=2026-10-20T00:00:00&after_id=0&limit=100

Ваши брони, изменённые после момента, по возрастанию времени изменения. В ответе next — подставьте его в следующий запрос, пока has_more равно true:

{ "count": 100, "has_more": true, "reservations": [], "next": { "changed_since": "2026-10-20 19:26:56", "after_id": 16121 } }

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

Одна бронь

GET /reservations/{id}

Только ваша бронь; чужая — 404.