Legan Studio
Все статьи
~ 18 мин чтения

Бот для записи на услуги в MAX: архитектура и пример

Как устроен бот для записи в MAX: расписание, слоты, напоминания, отмены, интеграция с календарем и нюансы поддержки разных мастеров.

  • MAX
  • услуги
  • архитектура

Бот для онлайн-записи — один из самых востребованных сценариев у малого и среднего сервисного бизнеса в России: салоны красоты, медицинские клиники, барбершопы, фитнес-студии, СТО, репетиторы, ветклиники. Снаружи это «выберите время», внутри — расписание, конфликты, отмены, оплата, напоминания и интеграция с уже работающей системой записи.

Главная боль администратора — приём заявок по телефону и в соцсетях: пропущенные звонки, ошибки в журнале, ручной перенос, no-show без штрафа. Бот в MAX закрывает первичный канал записи без передачи телефона: пользователь уже залогинен в мессенджере, не вводит логин-пароль и не ставит лишнее приложение. Для бизнеса это снижение нагрузки на ресепшн на 40–60% и рост повторных визитов за счёт удобной отмены и переноса в один клик.

Разберём архитектуру по слоям: что видит клиент, как считаются слоты, где живёт расписание, как защититься от двойного бронирования, как снизить no-show, и куда подключаются YClients, Altegio, Dikidi и 1С:Медицина.

Поверхность бота: что видит клиент

Универсальный сценарий записи одинаков для салона, клиники и автосервиса:

  1. Открыл бота → приветствие и кнопка «Записаться».
  2. Выбрал филиал (если сеть).
  3. Выбрал услугу из плоского списка с длительностью и ценой.
  4. Выбрал мастера или специалиста (опционально, либо «любой свободный»).
  5. Выбрал день и время из доступных слотов.
  6. Подтвердил, ввёл имя и телефон (если ещё не сохранён).
  7. Получил подтверждение и серию напоминаний.
  8. После визита — приглашение оставить отзыв и накопить бонусы.
  9. В любой момент может отменить или перенести запись.

Это и есть ядро. Программа лояльности, рекомендации, листы ожидания — поверх. Ключевая метрика поверхности — глубина воронки: сколько кнопок и экранов между «открыл бота» и «получил подтверждение». Для MAX комфортный потолок — 5–6 шагов, дальше начинается падение конверсии на 8–12% за каждый лишний экран.

Mini App в MAX vs inline-кнопки для календаря

Календарь — самый сложный экран в боте записи. Здесь два архитектурных подхода:

КритерийInline-клавиатураMini App в MAX
UX выбора датыСкролл по неделям кнопкамиПривычный календарь с месяцем
Скорость первого экрана200–400 мс1.2–2.5 с (загрузка WebView)
Глубина горизонтаУдобно до 14 днейМесяц и больше
Сложность разработки2.5× (фронт + initData + бэк)
Перенос/отменаВ 2 тапаВ 2 тапа, но с визуальным календарём
Доступность для оффлайн-кэшаЛучшеХуже
Подходит дляБарбершоп, СТО, репетиторКлиника с расписанием на месяц, фитнес

Практическое правило: до 30 слотов в день и горизонт 1–2 недели — берите inline. Сетка фитнес-классов на месяц или клиника с десятками врачей и слотов на 60 дней вперёд — Mini App. Гибрид тоже работает: inline для типичной записи и Mini App для «посмотреть полный календарь».

Mini App в MAX открывается через стандартный для MAX Bot API механизм WebView с initData; авторизация пользователя проверяется на бэке по подписи. Не доверяйте user_id из query string без верификации подписи — это классическая дыра, через которую можно записаться от имени соседа.

Модель данных

Скелет схемы БД для бота записи:

  • Филиалы (адрес, часы работы, часовой пояс, услуги).
  • Услуги (название, длительность, цена, к кому из мастеров привязана, к каким филиалам).
  • Мастера (ФИО, специализация, рабочие часы по дням недели, привязка к филиалу).
  • Расписание-исключения (выходные, отпуска, нестандартные смены, технические перерывы).
  • Записи (клиент, услуга, мастер, филиал, время начала, статус, источник).
  • Клиенты (контакт MAX, телефон, история визитов, no-show счётчик, теги).
  • Очередь напоминаний (запись, тип, время отправки, статус).

Ключевой принцип — слоты не хранятся, а вычисляются из расписания мастера и существующих записей на каждый запрос. Хранить слоты отдельно — путь к рассинхронизации, особенно когда администратор правит расписание в основной CRM.

CREATE TABLE appointments (
    id              BIGSERIAL PRIMARY KEY,
    branch_id       INT NOT NULL,
    master_id       INT NOT NULL,
    service_id      INT NOT NULL,
    client_id       BIGINT NOT NULL,
    starts_at       TIMESTAMPTZ NOT NULL,
    ends_at         TIMESTAMPTZ NOT NULL,
    status          TEXT NOT NULL DEFAULT 'confirmed',
    source          TEXT NOT NULL DEFAULT 'max_bot',
    deposit_paid    NUMERIC DEFAULT 0,
    created_at      TIMESTAMPTZ DEFAULT now(),
    EXCLUDE USING gist (
        master_id WITH =,
        tstzrange(starts_at, ends_at, '[)') WITH &&
    ) WHERE (status IN ('confirmed', 'pending'))
);

EXCLUDE USING gist с диапазоном времени — самый честный способ запретить пересечение записей у одного мастера на уровне БД. Это страховка от багов в коде расчёта слотов.

Алгоритм поиска слотов

Запрос «свободно ли в четверг с 14:00 на стрижку (40 мин) у мастера X»:

  1. Берём рабочие часы мастера на четверг с учётом часового пояса филиала.
  2. Применяем расписание-исключения (отпуск → пусто, сокращённый день → урезаем).
  3. Вычитаем уже занятые записи с буфером 5–10 минут между ними.
  4. Получаем массив свободных промежутков.
  5. Внутри каждого ищем слоты длиной 40 минут с шагом сетки (15 или 30 минут).
  6. Возвращаем кандидатов, отсортированных по времени.

Подводные камни:

  • Перерывы между записями. Большинство мастеров хотят 5–10 минут на «передохнуть». Это конфигурируется на уровне мастера и услуги.
  • Часовые пояса. Если бизнес в нескольких городах, считаем всё в UTC, отображаем в локальном поясе филиала. Не клиента — клиент может быть в Калининграде, а салон в Хабаровске.
  • Сложные услуги. Окрашивание = «нанесение 30 мин → выдержка 40 мин → смывка 20 мин». Технологически мастер занят только в первый и третий блок, в выдержке может принимать другого клиента. Это ломает наивный алгоритм «один блок времени».
  • Кабинеты как отдельный ресурс. В клинике на одну услугу нужны и врач, и кабинет, и оборудование. Слот свободен, только если все три ресурса доступны одновременно.

Конфликты двойного бронирования

Два клиента нажали «забронировать» одновременно на один и тот же слот. Без защиты оба получат подтверждение, мастер увидит коллизию утром, один клиент уйдёт с обидой. Решение в три приёма:

  • Distributed lock на слот в Redis: SET slot:{master}:{ts} {user_id} NX EX 60 на время чек-аута. Кто первый поставил ключ, тот и бронирует.
  • Транзакция с проверкой пересечения при финальном INSERT в appointments. Если БД (через EXCLUDE USING gist) отказала — показываем «слот только что заняли, выберите другое время».
  • Идемпотентность создания записи по ключу (client_id, slot_start, master_id) — если клиент дважды нажал «Подтвердить», вторая запись не создаётся.

Интеграция с системами записи

Если у бизнеса уже есть отраслевая CRM (а это типично для салонов и клиник), бот не дублирует логику, а становится фронт-каналом. Расписание и записи живут в основной системе, бот ходит в её API.

СистемаСегментAPIWebhookСложность
YClientsБьюти, фитнес, медицинаREST JSON, токенЕсть, надёжныйСредняя
AltegioБьюти, фитнес (бывш. YClients CIS)REST JSONЕстьСредняя
DikidiБьюти, барбершопыREST JSONЧастичныйПростая
SonlineСервисы, фитнесREST JSONЕстьПростая
1С:МедицинаКлиникиREST + ODataЧерез расширенияВысокая
IDENTСтоматологияREST JSONЕстьСредняя

Универсальный паттерн интеграции:

  • На каждый запрос слотов от клиента — запрос в API системы записи. Кэш на 30–60 секунд для частых запросов одного и того же дня.
  • На создание записи — синхронный вызов API системы. Запись создаётся именно там, бот сохраняет только «зеркало» с external_id.
  • На изменения, сделанные администратором (отмена, перенос, новый мастер) — webhook в бот. Если webhook ненадёжен, поверх ставим пуллинг раз в 5–10 минут на изменённые записи за последний час.

Дублировать расписание в собственной БД бота — путь к рассинхрону. Администратор подвинет запись в YClients, клиент придёт по старому времени, мастер скажет «у меня тут другой человек».

Архитектура: webhook + пуллинг для надёжности

Webhook от системы записи — основной канал свежих данных. Но webhooks падают по сотне причин: TLS-ошибки, таймауты, ретраи без идемпотентности, очереди на стороне поставщика. Полагаться только на push — рискованно.

Рабочая схема:

  1. Push-канал. Webhook прилетает на наш HTTPS-эндпоинт, проверяем подпись, кладём в очередь (Redis Streams / RabbitMQ), отвечаем 200 быстро.
  2. Worker разбирает очередь, обновляет зеркало в нашей БД, отправляет уведомления клиентам в MAX.
  3. Pull-канал. Раз в 5–10 минут фоновая job опрашивает API «дай изменения с момента T» и сверяет с зеркалом. Расхождения логируются и применяются.

Pull догоняет потерянные webhooks. Без него вы узнаёте о пропущенном событии только когда клиент жалуется «мне никто не напомнил».

Перенос, отмена, штраф, лист ожидания

Отмена в один тап — обязательная функция. Если её нет, клиенты не отменяют, а просто не приходят (no-show). Правила бизнеса:

  • Бесплатная отмена за N часов до визита (обычно 24 ч в бьюти, 4 ч у репетитора).
  • Платная отмена в последний момент — списание депозита целиком или частично.
  • Перенос — отдельный сценарий: клиент видит свободные слоты на ближайшие дни, выбирает новый.
  • Штраф за no-show — отметка в карточке клиента, при следующей записи требуется 100% предоплата.

Лист ожидания — мощная и недооценённая функция. Клиент не нашёл удобного времени → жмёт «Уведомить, если освободится». При отмене другого клиента бот рассылает приглашение первым 2–3 в листе. Кто первый подтвердил — забрал слот. Это закрывает 30–50% освободившихся слотов, которые иначе остались бы пустыми.

Каждое изменение записи фиксируется в логе с timestamp и инициатором (клиент / администратор / автомат) — для разбора спорных ситуаций и аналитики причин отмен.

Напоминания и снижение no-show

Без напоминаний 15–25% клиентов не приходят. С грамотными напоминаниями — обычно 5–10%. Минимальная схема напоминаний:

  • За 24 часа — «Завтра в 14:00 у вас запись к мастеру Анне на стрижку. Подтвердите кнопкой». Кнопки: «Да, приду» / «Отменить» / «Перенести».
  • За 2 часа — короткое напоминание с адресом и схемой проезда.
  • За 30 минут — для длинных услуг или клиентов из дальнего пригорода.
  • При неподтверждении за 24 ч — административный сигнал: возможно, слот стоит освободить.

Технически напоминания — это отложенные задачи в очереди (BullMQ, Asynq, sidekiq-аналог в Go). Очередь должна переживать перезапуск сервиса (персист на диск) и иметь идемпотентность: один и тот же appointment_id × reminder_type не должен отправиться дважды, даже при ретраях после падения worker.

async def schedule_reminders(appt_id: int, starts_at: datetime):
    plan = [
        ("24h", starts_at - timedelta(hours=24)),
        ("2h",  starts_at - timedelta(hours=2)),
        ("30m", starts_at - timedelta(minutes=30)),
    ]
    for kind, fire_at in plan:
        if fire_at <= datetime.now(tz=timezone.utc):
            continue
        await queue.enqueue(
            "send_reminder",
            {"appt_id": appt_id, "kind": kind},
            run_at=fire_at,
            dedup_key=f"reminder:{appt_id}:{kind}",
        )

Pre-payment и залог через бота

Предоплата 20–50% или фиксированный залог 500–1500 ₽ резко снижает no-show: клиент, который заплатил, приходит в 92–95% случаев против 75–85% без предоплаты. Это работает особенно жёстко для популярных мастеров и дорогих услуг.

Технически — связка с эквайером (ЮKassa, Тинькофф, СБП по QR), статус заказа paid_partial подтверждает запись, без оплаты бронь снимается через 15 минут (slot lock в Redis истекает сам). Сдачу оплачивает клиент на месте картой или наличными.

Возврат залога при бесплатной отмене за 24 ч — обязательный сценарий. По 161-ФЗ и правилам платёжных систем — возврат на ту же карту в течение 10 рабочих дней. Если вернуть «бонусами на бота» — клиент пожалуется в банк, банк сделает чарджбек, эквайер возьмёт штраф 1500–2500 ₽ с салона.

Программа лояльности и отзывы

Бот в MAX — естественное место для программы лояльности услугового бизнеса. Привязка к записям делает её ощутимой: после каждого визита клиент видит «+150 баллов за стрижку», «осталось 350 баллов до бесплатной укладки».

Минимальный набор механик:

  • Кешбек баллами — 3–10% от чека после визита (не после оплаты, иначе баллы уйдут на возвраты).
  • Уровни клиента (Базовый / Серебро / Золото) — растут от суммы за 6–12 месяцев, дают повышенный кешбек и приоритет в листе ожидания.
  • Дни рождения — автоматическая скидка или подарок.
  • Реферальная программа — приглашающий получает баллы после первого визита приглашённого.
  • Серии услуг (абонементы) — оплата 5 визитов авансом со скидкой 15%.

Отзывы пост-визит — отдельный сценарий. Через 3–5 часов после визита бот спрашивает «Как прошло?» с шкалой 1–5 или эмодзи. Оценка 4–5 → просьба оставить публичный отзыв (на 2ГИС, Яндекс.Картах) с готовой ссылкой. Оценка 1–3 → «Расскажите, что не так» с эскалацией администратору, без публикации. Это NPS-фильтр: негатив едет в личку, позитив — в публичные источники. По опыту бьюти-сетей такая воронка даёт прирост публичных отзывов в 4–6 раз и отвечает на негатив до того, как он стал публичным.

Аналитика бота записи

Полезные метрики, которые бот собирает сам:

  • Конверсия из открытия в запись — сколько пользователей дошли от /start до подтверждённой записи.
  • No-show rate — по мастерам, услугам, дням недели, часам, источникам трафика.
  • Загруженность мастеров — % занятого рабочего времени, разрыв между лучшим и худшим мастером.
  • Средний чек через бота vs через ресепшн — обычно через бота на 8–15% выше за счёт допродажи в карточке услуги.
  • Retention 30/60/90 дней — доля клиентов, которые вернулись за повторной записью.
  • Источники записей по UTM — какие каналы трафика приводят лучших клиентов.
  • Воронка отмен и переносов — сколько отменили, перенесли, не пришли молча.
  • Время от открытия бота до записи — индикатор юзабилити: норма 2–4 минуты, выше 8 — проблемы со сценарием.
МетрикаНормаТревога
Конверсия открытие → запись22–35%менее 15%
No-show без предоплаты10–18%более 25%
No-show с предоплатой4–8%более 12%
Recovery листа ожидания30–50%менее 20%
Доля повторных за 60 дней35–55%менее 25%
Средний NPS8.5–9.3менее 7.5

Эти данные подтягиваются в дашборд (Metabase, Superset, Grafana поверх Postgres). Раз в неделю — автоматический отчёт владельцу в личку MAX от того же бота.

Multi-локационная сеть

Сеть из 3+ филиалов — отдельный класс задач. Что меняется:

  • Выбор филиала становится первым шагом сценария. Удобно подсказать ближайший по геолокации, но не навязывать.
  • Услуги и цены могут отличаться по филиалам (центр vs спальный район). Прайс хранится по паре (branch_id, service_id).
  • Мастера привязаны к филиалу, иногда работают в нескольких — таблица master_branches многие-ко-многим.
  • Расписание и слоты считаются на уровне филиала + мастера. Кэш — отдельно по филиалу.
  • Программа лояльности — общая на сеть (баллы накоплены в одном салоне, потрачены в другом).
  • Предоплата уходит на счёт юр.лица филиала или общий счёт сети — это решает бухгалтерия и эквайер.

Аналитика по сети должна резаться по филиалам: загруженность одного может быть 90%, другого — 45%. Это сигнал для маркетинга и пересмотра графиков мастеров.

Чек-лист готовности бота к запуску

  • Универсальный сценарий «услуга → мастер → дата → время → подтверждение» проходится за 5–6 шагов.
  • Слоты вычисляются на лету из расписания CRM, не хранятся в собственной БД.
  • EXCLUDE USING gist или distributed lock защищает от пересечения записей.
  • Webhook от CRM + пуллинг раз в 5–10 минут как страховка.
  • Идемпотентность создания записи и отправки напоминаний.
  • Напоминания за 24 ч, 2 ч (опционально 30 мин), переживают перезапуск сервиса.
  • Кнопки «Отменить» и «Перенести» прямо в напоминании, без ввода команд.
  • Лист ожидания на популярные слоты с авторассылкой при освобождении.
  • Pre-payment / залог через ЮKassa или СБП с автоматическим возвратом при отмене за N часов.
  • Чек по 54-ФЗ уходит в ОФД при оплате депозита.
  • Программа лояльности привязана к фактическому визиту, не к оплате.
  • Отзывы пост-визит с NPS-фильтром: позитив наружу, негатив администратору.
  • Аналитика: конверсия, no-show, загрузка, retention, средний чек.
  • Multi-локация — выбор филиала первым шагом, цены и расписание по филиалу.
  • Согласие на обработку ПДн собрано до записи (152-ФЗ).

Итого

Бот для записи в MAX — не «форма с датой», а связка из расписания, расчёта слотов с защитой от двойного бронирования, очереди напоминаний, эквайринга для предоплаты и интеграции с уже работающей CRM. При продуманной архитектуре проект занимает 3–5 недель и даёт измеримый эффект: 40–60% разгрузка ресепшна, no-show с 20% до 5–8%, средний чек выше на 10–15%, retention выше на 20–30% за счёт лояльности и удобной отмены.

Главное — не пытаться заменить YClients или 1С:Медицину собственной БД расписания. Бот — это новый канал записи и сервиса, а не вторая система учёта.

Частые вопросы

Сколько стоит бот для записи на услуги в MAX?

От 120 000 до 280 000 ₽ при разработке под ключ, срок 3–5 недель. В цену входит универсальный сценарий записи (услуга, мастер, дата, время), расчёт слотов с защитой от двойного бронирования через EXCLUDE USING gist, очередь напоминаний за 24 ч и 2 ч с идемпотентностью, отмены и переносы, базовая аналитика. Дополнительно: предоплата через ЮKassa или СБП плюс 30 000–60 000 ₽, интеграция с YClients/Altegio/Dikidi/1С:Медицина плюс 50 000–100 000 ₽, Mini App в MAX с визуальным календарём плюс 60 000–140 000 ₽, программа лояльности с уровнями и баллами плюс 40 000–80 000 ₽, multi-локация для сети филиалов плюс 30 000–60 000 ₽.

Как бот рассчитывает свободные слоты записи?

Слоты не хранятся, а вычисляются на лету при каждом запросе. Алгоритм: берём рабочие часы мастера на дату с учётом часового пояса филиала, применяем расписание-исключения (отпуска, сокращённые дни), вычитаем существующие записи с буфером 5–10 минут, получаем свободные промежутки, внутри ищем слоты нужной длины с шагом сетки 15 или 30 минут. Хранить слоты отдельно — путь к рассинхрону, особенно когда администратор правит расписание в основной CRM. Для конкурентности (два клиента жмут одновременно) ставим distributed lock в Redis на слот с TTL 60 секунд плюс EXCLUDE USING gist с диапазоном времени на уровне БД — это страховка от багов в коде.

Как уменьшить количество no-show через бота записи в MAX?

Тремя приёмами в комбинации. Напоминания за 24 ч с просьбой подтвердить кнопкой «Да, приду» / «Перенести» / «Отменить», за 2 ч короткое с адресом — снижают no-show с типичных 15–25% до 8–12%. Pre-payment или залог 500–1500 ₽ через ЮKassa или СБП — при отмене за 24 ч возврат на карту, при no-show удержание; снижает no-show до 4–8% для популярных мастеров и дорогих услуг. Отметка no-show в карточке клиента и автоматическое требование 100% предоплаты при следующей записи. Лист ожидания дополнительно закрывает 30–50% освободившихся слотов: при отмене бот рассылает приглашение первым в очереди.

Как интегрировать бот в MAX с YClients, Altegio, Dikidi или 1С:Медицина?

Бот работает как фронт-канал, расписание и записи живут в основной CRM. На каждый запрос слотов от клиента — запрос в API системы записи, кэш на 30–60 секунд для частых обращений к одному дню. На создание записи — синхронный вызов API CRM, бот сохраняет только зеркало с external_id. На изменения от администратора (отмена, перенос) — webhook от CRM в наш HTTPS-эндпоинт. Поверх webhook ставим пуллинг раз в 5–10 минут на изменённые записи за последний час — это страховка от потерянных push-событий. YClients, Altegio, Dikidi и Sonline — REST JSON с токеном, IDENT — тоже REST. 1С:Медицина — REST или OData через расширения, сложнее остальных. Срок интеграции 5–12 рабочих дней при готовом API. Дублировать расписание в собственной БД нельзя — гарантированный рассинхрон.

Нужен ли Mini App в MAX или хватит inline-кнопок для записи?

Зависит от глубины каталога услуг и горизонта расписания. До 30 слотов в день и горизонт 1–2 недели — берите inline-клавиатуру: первый экран за 200–400 мс, проще разработка, лучше работает в плохой связи. Сетка фитнес-классов на месяц или клиника с десятками врачей и слотов на 60 дней вперёд — Mini App с привычным календарём. Mini App дороже в 2.5 раза, открывается за 1.2–2.5 секунды и просаживает конверсию на загрузке, но даёт привычный UX визуального календаря. Гибрид часто оптимален: inline для типичной записи в 2–3 клика, Mini App по кнопке «Открыть полный календарь» для пользователей, кому нужен месяц вперёд. Авторизация в Mini App — по подписи initData; не доверяйте user_id из query string без проверки подписи на бэке.

Как настроить программу лояльности и сбор отзывов через бота?

Программа лояльности привязывается к фактическому визиту, а не к оплате — иначе баллы уйдут на возвраты. Базовый набор: кешбек 3–10% после визита, уровни клиента (Базовый, Серебро, Золото) от суммы за 6–12 месяцев, бонус ко дню рождения, реферальная программа с начислением после первого визита приглашённого, абонементы со скидкой 15% за оплату 5 визитов вперёд. Отзывы пост-визит — через 3–5 часов после визита бот спрашивает оценку 1–5. Оценка 4–5 — просьба оставить публичный отзыв на 2ГИС или Яндекс.Картах с готовой ссылкой. Оценка 1–3 — «расскажите, что не так» и эскалация администратору без публикации. Это NPS-фильтр: негатив едет в личку и решается, позитив выходит в публичные источники. У бьюти-сетей такая воронка даёт прирост публичных отзывов в 4–6 раз.

Какую аналитику собирает бот для записи на услуги?

Восемь полезных метрик. Конверсия из открытия бота в запись — главная воронка, норма 22–35%. No-show rate по мастерам, услугам, дням недели и источникам — норма 10–18% без предоплаты, 4–8% с предоплатой. Загруженность мастеров в процентах рабочего времени — даёт основу для пересмотра графиков. Средний чек через бота vs через ресепшн — обычно выше на 8–15% за счёт допродажи в карточке услуги. Retention 30/60/90 дней — норма 35–55% за 60 дней. Источники записей по UTM. Воронка отмен и переносов с причинами. Время от открытия бота до подтверждения записи — норма 2–4 минуты, выше 8 говорит о проблемах сценария. Метрики собираются в Postgres и подтягиваются в Metabase или Grafana, раз в неделю владельцу в MAX уходит автоматический сводный отчёт.