Обновлено: сентябрь 2026

Как сделать Telegram Mini App

Telegram Mini App — это обычная HTTPS-страница, которую мессенджер открывает в системном WebView. Чтобы она стала мини-приложением, нужны три вещи: страница на своём домене, один тег <script> и включённый Mini App в @BotFather. Ниже — рабочий минимум, проверка подписи и четыре места, где WebView ведёт себя не как браузер.

Что это такое технически

Документация Telegram описывает Mini App как интерфейс на JavaScript, который запускается внутри мессенджера и способен полностью заменить любой сайт. Сборки под платформу, публикации в сторе и ревью здесь нет: вы деплоите сайт, а Telegram открывает его в WebView.

Что это именно WebView, видно по разделу отладки: на Android документация предлагает включить Enable WebView Debug, на iOS — Allow Web View Inspection, на Desktop — Inspect по правой кнопке внутри самого окна приложения. Отсюда же следуют все дальнейшие ограничения.

Минимум, который запускается

Скрипт подключается в <head> до всех остальных, после чего появляется объект window.Telegram.WebApp. Номер после вопросительного знака — версия файла, указанная в документации на август 2026:

<!DOCTYPE html>
<html>
<head>
  <script src="https://telegram.org/js/telegram-web-app.js?63"></script>
</head>
<body>
  <p id="hello"></p>
  <script>
    const tg = window.Telegram.WebApp;
    tg.ready();
    tg.expand();
    document.getElementById('hello').textContent =
      'Привет, ' + (tg.initDataUnsafe.user?.first_name ?? 'гость');
  </script>
</body>
</html>

ready() сообщает Telegram, что интерфейс готов к показу, и убирает загрузочный плейсхолдер. Документация советует вызывать его как можно раньше, как только загрузились основные элементы: без вызова плейсхолдер снимется только после полной загрузки страницы. expand() разворачивает приложение на максимально доступную высоту.

Регистрация в @BotFather

  1. Отправьте /newbot — BotFather спросит имя и username и выдаст токен. Username бота — это 5–32 символа, латиница, цифры и подчёркивание, обязательно с окончанием bot.
  2. /mybots → выберите бота → Bot SettingsConfigure Mini AppEnable Mini App. Здесь же задаётся адрес приложения.
  3. Адрес обязан быть по HTTPS: поле url объекта WebAppInfo в Bot API так и описано — «An HTTPS URL of a Web App». Самоподписанный сертификат не подойдёт.
  4. Кнопка под полем ввода настраивается командой /setmenubutton (или Bot Settings → Menu Button); нужны текст кнопки и URL. Программный аналог — метод setChatMenuButton.

У одного бота может быть несколько Mini App: они различаются коротким именем, и это же имя стоит в прямой ссылке https://t.me/botusername/appname. Главное приложение бота открывается ссылкой без короткого имени — https://t.me/botusername?startapp.

Как передать параметр внутрь приложения

Это первое место, где легко потерять полдня. Значение startapp из прямой ссылки приезжает в приложение двумя путями сразу: в поле start_param объекта initData и в GET-параметре tgWebAppStartParam. У меню вложений параметр называется иначе — startattach.

Ловушка. Способов запуска у Mini App семь, и start_param есть не во всех. При запуске с клавиатурной кнопки и через inline-режим объект WebAppInitData пуст целиком: там нет ни user, ни query_id, ни параметра запуска. У inline-кнопки и кнопки меню данные пользователя есть, но передача start_param в их разделах документации не описана.

Практический вывод: если приложение открывается inline-кнопкой web_app, параметр надо везти в самом URL кнопки и читать его из location.search, а не ждать в initData.

У deep-link'а самого бота (t.me/botusername?start=…) ограничение жёсткое и задокументированное: 64 символа, алфавит A-Z, a-z, 0-9, _ и -. Для бинарных данных документация советует base64url. Для startapp ни длина, ни алфавит нигде не заданы, так что переносить на него «те же 64» — недокументированное допущение, а не факт.

Как проверить, что пользователь настоящий

Объект initDataUnsafe называется так не для красоты: это распакованные данные без проверки, и опираться на них при принятии решений нельзя. Доверять можно только строке initData, и только после проверки подписи на сервере.

Алгоритм из официальной документации: собрать все полученные поля кроме hash, отсортировать по алфавиту, склеить как key=value через перевод строки, посчитать HMAC-SHA-256 и сравнить с hash.

import hashlib, hmac
from urllib.parse import parse_qsl

def check(init_data: str, bot_token: str) -> bool:
    pairs = dict(parse_qsl(init_data))
    received = pairs.pop("hash", "")
    check_string = "\n".join(f"{k}={v}" for k, v in sorted(pairs.items()))
    secret = hmac.new(b"WebAppData", bot_token.encode(), hashlib.sha256).digest()
    calc = hmac.new(secret, check_string.encode(), hashlib.sha256).hexdigest()
    return hmac.compare_digest(calc, received)

Обратите внимание на порядок аргументов в строке, где считается secret. В псевдокоде документации написано secret_key = HMAC_SHA256(<bot_token>, "WebAppData"): токен здесь — сообщение, а строка WebAppDataключ. Перепутать их местами — самая частая ошибка в этом месте, и она даёт стабильно неверный hash при полностью правильном остальном коде.

Поле auth_date — Unix-время открытия формы, а не отправки запроса. Документация советует его проверять, но никакого срока годности не называет: окно свежести выбираете вы. Вместе с проверкой подписи это единственная защита от переигрывания старых данных.

Если проверять данные должна третья сторона, у которой нет токена бота, с Bot API 8.0 (17 ноября 2024) для этого есть отдельное поле signature — Ed25519-подпись в base64url, проверяемая опубликованным Telegram публичным ключом. Строка для проверки там собирается иначе: первой идёт bot_id:WebAppData, а из полей исключаются и hash, и signature.

Четыре места, где WebView не браузер

На какой версии всё это

Актуальная версия Bot API на август 2026 — 10.2, опубликована 14 июля 2026. Сам JS-класс WebApp пополняется реже: последняя запись в разделе Recent changes страницы Mini Apps — 3 апреля 2026, Bot API 9.6, добавлен метод requestChat.

Из этого легко сделать неверный вывод, что старый код заведётся как есть. В той же версии 10.2 Telegram запретил вызывать методы Mini App с origin, отличного от домена самого приложения, и включил эту защиту автоматически для всех приложений 20 июля 2026. Запись об этом лежит в общем changelog Bot API, а не в разделе Recent changes страницы Mini Apps, — поэтому привычка «смотрю Recent changes, значит ничего не менялось» именно здесь и промахивается. Если приложение дёргает Telegram.WebApp.* из фрейма или со страницы на другом домене, после этой даты вызовы перестают работать.

Когда приложение писать не нужно

Всё выше описывает, как сделать Mini App вообще. Если задача узкая — принимать записи клиентов, — то большая часть работы окажется не в WebView, а в том, что за ним: расписание, защита от двойной записи на одно окно, напоминания, отмены, предоплата.

Mira — готовое мини-приложение ровно под этот сценарий, для бьюти-мастеров: лента работ, предоплата по СБП, напоминания, клиентская база и аналитика. Если приложение нужно не само по себе, а чтобы клиентки записывались, проще взять готовое.

Вопросы

Нужен ли сервер, чтобы сделать Telegram Mini App?
Для запуска — нет, достаточно HTTPS-хостинга статики: Bot API требует, чтобы адрес приложения был HTTPS-ссылкой. Серверная часть становится обязательной с того момента, когда нужно проверить initData (подпись считается по токену бота, а токен нельзя отдавать в браузер) или что-то хранить.
Чем Mini App отличается от бота?
Бот — это диалог: он получает апдейты и отвечает через Bot API. Mini App — веб-страница, которую Telegram открывает в системном WebView. Одно не заменяет другое: приложение запускается из бота, а бот остаётся каналом уведомлений.
Сколько символов помещается в start-параметр ссылки на бота?
64 символа, алфавит A-Z, a-z, 0-9, _ и -; для бинарных данных документация советует base64url. Для параметра startapp прямых ссылок на Mini App длина нигде не оговорена.

Все статьи · Как сделать телеграм-бота для записи клиентов