Bot web apps

Bots can offer users interactive HTML5 web apps to 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 web apps using the MTProto API: see here an overview of the web-app side JS API ».

Outgoing events: Web app to client

Both simple and normal web 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 web app to the client ».

Incoming events: Client to web app

Web 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 web app from the client, by calling the receiveEvent method.


Params: a JSON object containing an optional string phone_number field.

Sent in response to a web_app_request_phone outgoing event, see the docs for more info ».


Params: null

Sent by the client when the user presses the main button, if it was previously configured by a web_app_setup_main_button event ».


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 ».


Params: null

Sent by the client when the user presses the back button, if it was previously enabled by a web_app_setup_back_button event ».


Params: JSON object with the following fields:

  • slug - Invoice identifier (string)
  • status - One of the following values (string):
    • paid – The invoice was paid successfully
    • cancelled – The user closed this invoice without paying
    • failed – The user tried to pay, but the payment was failed
    • pending – The payment is still processing. The bot will receive a service message about a successful payment when the invoice is successfully paid.

Sent by the client to report the payment status of an invoice obtained from a web_app_open_invoice event ».


Params: a JSON object with the following fields:

  • height - The current height of the visible area of the Web App (integer)
  • is_state_stable - The height of the visible area of the Web App in its last stable state (integer)
  • is_expanded - Whether the Web App is expanded to its maximum height (boolean)

Emitted when the viewport is changed.


Params: a JSON object with the following fields:

  • theme_params: a JSON object with the following fields:
    • bg_color - Background color in hex RGB format (string)
    • secondary_bg_color - Secondary background color in hex RGB format (string)
    • text_color - Text color in hex RGB format (string)
    • hint_color - Hint color in hex RGB format (string)
    • link_color - Link color in hex RGB format (string)
    • button_color - Button color in hex RGB format (string)
    • button_text_color - Button text color in hex RGB format (string)

Emitted when requested by the web app using a web_app_request_theme event », or when the app theme changes.


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 ».

Simple web apps


replyKeyboardMarkup#85dd99d1 flags:# resize:flags.0?true single_use:flags.1?true selective:flags.2?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;


messages.requestSimpleWebView#299bec8e flags:# 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 webapps can only send back data to the bot through the MTProto API via a web_app_data_send JS event ».

Simple webapps can only be opened from a keyboardButtonSimpleWebView button contained in a reply keyboard identified by a replyKeyboardMarkup constructor.

To open them, users should call messages.requestSimpleWebView, and then open a webview using the url contained in the returned simpleWebViewResultUrl.

Upon receiving a web_app_data_send JS event » from the web app, clients should invoke messages.sendWebViewData, passing the following arguments:

  • bot - Bot ID
  • random_id - Unique random ID to avoid resending the same event multiple times
  • button_text - Text of the keyboardButtonSimpleWebView that was pressed to open the simple web app
  • data - Contents of the data field of the JS event.

This will generate a messageActionWebViewDataSent update for the user, and a messageActionWebViewDataSentMe update for the bot, containing the event data.

Normal web apps


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;


messages.requestWebView#fc87a53c 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 send_as:flags.13?InputPeer = WebViewResult;

messages.prolongWebView#ea5fbcce flags:# silent:flags.5?true peer:InputPeer bot:InputUser query_id:long reply_to_msg_id:flags.0?int send_as:flags.13?InputPeer = Bool;

messages.sendWebViewResultMessage#a4314f5 bot_query_id:string result:InputBotInlineResult = WebViewMessageSent;

Normal webapps work similarly to inline bots »: they send messages on behalf of the user to the chat from which the query originated.

Normal webapps can be opened from:

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.

The opened URL's fragment parameters already contain basic information about the user and a query_id parameter, that is exposed by the bot web 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.