Interactive HTML5 Mini Apps on Telegram can completely replace any website.
They support seamless authorization, integrated payments via multiple payment providers (with Google Pay and Apple Pay out of the box), delivering tailored push notifications to users, and much more.
This article offers a client-side overview of the implementation of bot mini apps using the MTProto API: see here for an overview of the mini-app side JS API ».
Both simple and normal Mini Apps can send web events starting with web_app_
; see the web event documentation for the full list of events that can be sent by the Mini App to the client ».
Mini Apps can also receive events, by exposing a window.Telegram.WebView.receiveEvent("event_name", params)
method.
Here's the full list of events that can be received by a Mini App from the client, by calling the receiveEvent
method.
main_button_pressed
Params: null
Sent by the client when the user presses the main button located at the bottom of the webview, handle this event only if the main button was previously configured by a web_app_setup_main_button
event ».
settings_button_pressed
Params: null
Sent by the client when the user presses the settings button, if it was previously enabled in @BotFather, as specified by the has_settings
flag of attachMenuBot ».
back_button_pressed
Params: null
Sent by the client when the user presses the (OS or UI) back button, if it was previously enabled by a web_app_setup_back_button
event ».
invoice_closed
Params: JSON object with the following fields:
slug
- Invoice identifier (string)status
- One of the following values (string):cancelled
– The user closed the invoice popup without paying, before the call to payments.sendPaymentForm.failed
– The user tried to pay, but the payment failed: the call to payments.sendPaymentForm returned an RPC error and the popup was closed.pending
– The payment is still processing: the bot will receive a further service message about a successful payment. payments.sendPaymentForm was successfully invoked returning payments.paymentVerificationNeeded, the user completed all additional verification forms returned by the method and the invoice popup was closed, but the client hasn't received a messageActionPaymentSent service message yet.failed
event if the user fails additional validation (ie 3-D Secure) returned by payments.paymentVerificationNeeded: the state will remaing pending
. paid
– The invoice was paid successfully: the client completed the payment flow », the invoice popup was closed and a messageActionPaymentSent service message was received by the client.Sent by the client to report the payment status of an invoice obtained from a web_app_open_invoice
event ».
viewport_changed
Params: a JSON object with the following fields:
height
- The current height of the visible area of the Mini App (excluding the bottom main button, if visible) (integer)is_state_stable
- If true, the viewport is currently being resized (animation in progress), more events of this type may be emitted. (boolean)is_expanded
- Whether the Mini App is expanded to its maximum height after the user swiped up or after the Mini App emitted a web_app_expand event (boolean)Emitted when the viewport is changed.
theme_changed
Params: a JSON object with the following fields:
theme_params
: A theme parameters object » (object)Emitted when requested by the Mini App using a web_app_request_theme
event », or when the app theme changes.
popup_closed
Params: a JSON object with an optional button_id
string field.
Emitted when the user presses a button or cancels a popup brought up by a previous web_app_open_popup
event ».
Schema:
replyKeyboardMarkup#85dd99d1 flags:# resize:flags.0?true single_use:flags.1?true selective:flags.2?true persistent:flags.4?true rows:Vector<KeyboardButtonRow> placeholder:flags.3?string = ReplyMarkup;
keyboardButtonSimpleWebView#a0c0505c text:string url:string = KeyboardButton;
messageActionWebViewDataSentMe#47dd8079 text:string data:string = MessageAction;
messageActionWebViewDataSent#b4c38cb5 text:string = MessageAction;
simpleWebViewResultUrl#882f76bb url:string = SimpleWebViewResult;
---functions---
messages.requestSimpleWebView#299bec8e flags:# from_switch_webview:flags.1?true bot:InputUser url:string theme_params:flags.0?DataJSON platform:string = SimpleWebViewResult;
messages.sendWebViewData#dc0242c8 bot:InputUser random_id:long button_text:string data:string = Updates;
Simple Mini Apps can only send data back to the bot through the MTProto API via a web_app_data_send
JS event ».
Simple Mini Apps can be opened from a keyboardButtonSimpleWebView button contained in a reply keyboard identified by a replyKeyboardMarkup constructor, or by clicking on the inlineBotWebView button on top of the inline result list, contained in messages.botResults.switch_webview
, returned by messages.getInlineBotResults.
To open them, users should call messages.requestSimpleWebView passing the original url
, and then open a webview using the url
contained in the returned simpleWebViewResultUrl.
If and only if the Mini App was opened from a keyboardButtonSimpleWebView reply keyboard button, upon receiving a web_app_data_send
JS event » from the Mini App, clients should invoke messages.sendWebViewData, passing the following arguments:
bot
- Bot IDrandom_id
- Unique random ID to avoid resending the same event multiple timesbutton_text
- Text of the keyboardButtonSimpleWebView that was pressed to open the simple Mini Appdata
- Contents of the data
field of the JS event. Always ignore all web_app_data_send
events received from inlineBotWebView Mini Apps, as only keyboardButtonSimpleWebView Mini Apps can send this event.
Make sure to ignore all web_app_data_send
events sent after the first one, messages.sendWebViewData must be called only once. The webview must be closed after invoking the messages.sendWebViewData method.
This will generate a messageActionWebViewDataSent update for the user, and a messageActionWebViewDataSentMe update for the bot, containing the event data.
Schema:
keyboardButtonWebView#13767230 text:string url:string = KeyboardButton;
botMenuButton#c7b57ce6 text:string url:string = BotMenuButton;
webViewResultUrl#c14557c query_id:long url:string = WebViewResult;
inputBotInlineResult#88bf9319 flags:# id:string type:string title:flags.1?string description:flags.2?string url:flags.3?string thumb:flags.4?InputWebDocument content:flags.5?InputWebDocument send_message:InputBotInlineMessage = InputBotInlineResult;
inputBotInlineResultPhoto#a8d864a7 id:string type:string photo:InputPhoto send_message:InputBotInlineMessage = InputBotInlineResult;
inputBotInlineResultDocument#fff8fdc4 flags:# id:string type:string title:flags.1?string description:flags.2?string document:InputDocument send_message:InputBotInlineMessage = InputBotInlineResult;
inputBotInlineResultGame#4fa417f2 id:string short_name:string send_message:InputBotInlineMessage = InputBotInlineResult;
updateWebViewResultSent#1592b79d query_id:long = Update;
webViewMessageSent#c94511c flags:# msg_id:flags.0?InputBotInlineMessageID = WebViewMessageSent;
---functions---
messages.requestWebView#178b480b flags:# from_bot_menu:flags.4?true silent:flags.5?true peer:InputPeer bot:InputUser url:flags.1?string start_param:flags.3?string theme_params:flags.2?DataJSON platform:string reply_to_msg_id:flags.0?int top_msg_id:flags.9?int send_as:flags.13?InputPeer = WebViewResult;
messages.prolongWebView#7ff34309 flags:# silent:flags.5?true peer:InputPeer bot:InputUser query_id:long reply_to_msg_id:flags.0?int top_msg_id:flags.9?int send_as:flags.13?InputPeer = Bool;
messages.sendWebViewResultMessage#a4314f5 bot_query_id:string result:InputBotInlineResult = WebViewMessageSent;
Normal Mini Apps work similarly to inline bots »: they send messages on behalf of the user to the chat from which the query originated.
Normal Mini Apps can be opened from:
url
must be passed to messages.requestWebView.url
.from_bot_menu
flag should be set.start_parameter
should be provided to messages.requestWebView.start_param
, if present.To open them, clients should call messages.requestWebView, and then open a webview using the url
contained in the returned webViewResultUrl.
After loading the webview, until it is closed by a web_app_close event, the user client must invoke messages.prolongWebView every 60 seconds: if the method call returns QUERY_ID_INVALID
, the webview must be closed.
The opened URL's fragment parameters already contain basic information about the user and a query_id
parameter, that is exposed by the bot Mini Apps JS library: this query_id
can then be used by the bot to invoke messages.sendWebViewResultMessage, passing an InputBotInlineResult constructor that will automatically send a message with optionally attached media, and even inline buttons on behalf of the user.
Schema:
inputBotAppID#a920bd7a id:long access_hash:long = InputBotApp;
inputBotAppShortName#908c0407 bot_id:InputUser short_name:string = InputBotApp;
botAppNotModified#5da674b7 = BotApp;
botApp#95fcd1d6 flags:# id:long access_hash:long short_name:string title:string description:string photo:Photo document:flags.0?Document hash:long = BotApp;
messages.botApp#eb50adf5 flags:# inactive:flags.0?true request_write_access:flags.1?true app:BotApp = messages.BotApp;
appWebViewResultUrl#3c1b4f0d url:string = AppWebViewResult;
---functions---
messages.getBotApp#34fdc5c3 app:InputBotApp hash:long = messages.BotApp;
messages.requestAppWebView#8c5a3b3c flags:# write_allowed:flags.0?true peer:InputPeer app:InputBotApp start_param:flags.1?string theme_params:flags.2?DataJSON platform:string = AppWebViewResult;
Another way to open Mini Apps is by using named bot Mini App links ».
These links are different from bot attachment menu deep links », because they don't require the user to install an attachment menu, and a single bot can offer multiple Mini Apps, distinguished by their short_name
.
These links should be handled as follows:
Check if bot_username
parameter of the link is indeed a bot username, if so then
Invoke messages.getBotApp, passing an inputBotAppShortName with the short_name
contained in the appname
query string parameter.
If the client has already encountered an app with this short name from the same bot before, pass the hash
of the cached botApp constructor to messages.getBotApp.
If a messages.botApp constructor is returned and its request_write_access
flag is set, show a prompt to the user, indicating that the bot is asking permission to send messages to the user.
If the user agrees, set the write_allowed
flag when invoking messages.requestAppWebView in the next step.
If a messages.botApp constructor is returned, open the Mini App by invoking messages.requestAppWebView, generating an inputBotAppID constructor from id
and access_hash
of the returned botApp, or from previously cached information if we already met the bot app and botAppNotModified was returned.
If the client has clicked on the link in a Telegram chat, pass the chat's peer information into peer
; otherwise pass the bot's peer information, instead.
If the messages.botApp.inactive
flag is set, ask confirmation from the user before opening the Mini App; the request_write_access
checkbox should be shown in this prompt, if needed.
Confirmation should always be asked, even if the inactive
flag is not set, when opening the link from places where the full link is not visible (i.e. messageEntityTextUrl text links, inline buttons etc.).
If the startapp
query string parameter is present, pass it to start_param
when invoking messages.requestAppWebView.
If the messages.botApp.request_write_access
flag is set, the bot is asking permission to send messages to the user: if the user agrees, set the write_allowed
flag when invoking messages.requestAppWebView.
Finally, open the webview using the url
contained in the returned appWebViewResultUrl.
Since there is no linked inline query, web_app_data_send
events must be ignored.
The bot can, however, write to the user directly if it already has a chat with the user or if it requested permission via request_write_access
and the user granted it with write_allowed
.
Bot Mini Apps can be themed according to the following theme parameters, passed as a JSON object to the theme_params
parameter of the messages.requestSimpleWebView and messages.requestWebView methods.
This JSON object has the following keys, containing color theme information (hex string, RGB, no alpha) to pass to the Mini App:
bg_color
- Background colorsecondary_bg_color
- Secondary background colortext_color
- Text colorhint_color
- Hint text colorlink_color
- Link colorbutton_color
- Button colorbutton_text_color
- Button text color