<divclass="dev_page_bread_crumbs"><ulclass="breadcrumb clearfix"><li><ahref="/api">API</a></li><iclass="icon icon-breadcrumb-divider"></i><li><ahref="/api/bots%2Fwebapps">Bot web apps</a></li></ul></div>
<h1id="dev_page_title">Bot web apps</h1>
<divid="dev_page_content"><!-- scroll_nav -->
<p>Bots can offer users interactive <ahref="/bots/webapps">HTML5 web apps</a> to completely replace <strong>any website</strong>. </p>
<p>They support <ahref="https://telegram.org/blog/privacy-discussions-web-bots#meet-seamless-web-bots">seamless authorization</a>, <ahref="https://core.telegram.org/bots/payments">integrated payments</a> via multiple payment providers (with <em>Google Pay</em> and <em>Apple Pay</em> out of the box), delivering tailored push notifications to users, and <ahref="https://core.telegram.org/bots">much more</a>.</p>
<p>This article offers a client-side overview of the implementation of bot web apps using the MTProto API: see <ahref="/bots/webapps">here for an overview of the web-app side JS API »</a>. </p>
<h3><aclass="anchor"href="#outgoing-events-web-app-to-client"id="outgoing-events-web-app-to-client"name="outgoing-events-web-app-to-client"><iclass="anchor-icon"></i></a>Outgoing events: Web app to client</h3>
<p>Both <ahref="#simple-web-apps">simple</a> and <ahref="#normal-web-apps">normal</a> web apps can <em>send</em> web events starting with <code>web_app_</code>; see the <ahref="/api/web-events">web event documentation for the full list of events that can be <em>sent</em> by the web app to the client »</a>. </p>
<h3><aclass="anchor"href="#incoming-events-client-to-web-app"id="incoming-events-client-to-web-app"name="incoming-events-client-to-web-app"><iclass="anchor-icon"></i></a>Incoming events: Client to web app</h3>
<p>Web apps can also <em>receive</em> events, by exposing a <code>window.Telegram.WebView.receiveEvent("event_name", params)</code> method. </p>
<p>Here's the full list of events that can be <em>received</em> by a web app from the client, by calling the <code>receiveEvent</code> method. </p>
<p>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 <ahref="/api/web-events#web-app-setup-main-button">previously configured by a <code>web_app_setup_main_button</code> event »</a>.</p>
<p>Sent by the client when the user presses the settings button, if it was previously enabled in <ahref="https://t.me/BotFather">@BotFather</a>, as specified by the <code>has_settings</code> flag of <ahref="/constructor/attachMenuBot">attachMenuBot »</a>. </p>
<p>Sent by the client when the user presses the (OS or UI) back button, if it was <ahref="/api/web-events#web-app-setup-back-button">previously enabled by a <code>web_app_setup_back_button</code> event »</a>.</p>
<li><code>status</code> - One of the following values (string):<ul>
<li><code>cancelled</code>– The user closed the invoice popup without paying, before the call to <ahref="/method/payments.sendPaymentForm">payments.sendPaymentForm</a>.</li>
<li><code>failed</code>– The user tried to pay, but the payment failed: the call to <ahref="/method/payments.sendPaymentForm">payments.sendPaymentForm</a> returned an RPC error and the popup was closed.</li>
<li><code>pending</code>– The payment is still processing: the bot will receive a further service message about a successful payment. <ahref="/method/payments.sendPaymentForm">payments.sendPaymentForm</a> was successfully invoked returning <ahref="/constructor/payments.paymentVerificationNeeded">payments.paymentVerificationNeeded</a>, the user completed all additional verification forms returned by the method and the invoice popup was closed, but the client hasn't received a <ahref="/constructor/messageActionPaymentSent">messageActionPaymentSent</a> service message yet.<br>
Note that eventual errors will not be sent as a <code>failed</code> event if the user fails additional validation (ie 3-D Secure) returned by <ahref="/constructor/payments.paymentVerificationNeeded">payments.paymentVerificationNeeded</a>: the state will remaing <code>pending</code>. </li>
<li><code>paid</code>– The invoice was paid successfully: the client completed the <ahref="/api/payments">payment flow »</a>, the invoice popup was closed and a <ahref="/constructor/messageActionPaymentSent">messageActionPaymentSent</a> service message was received by the client.</li>
</ul>
</li>
</ul>
<p>Sent by the client to report the <ahref="/api/payments">payment status</a> of an invoice obtained from a <ahref="/api/web-events#web-app-open-invoice"><code>web_app_open_invoice</code> event »</a>. </p>
<p>Params: a JSON object with the following fields:</p>
<ul>
<li><code>height</code> - The current height of the visible area of the Web App (excluding the bottom <ahref="#main-button-pressed">main button</a>, if visible) (integer)</li>
<li><code>is_state_stable</code> - If true, the viewport is currently being resized (animation in progress), more events of this type may be emitted. (boolean)</li>
<li><code>is_expanded</code> - Whether the Web App is expanded to its maximum height after the user swiped up or after the web app emitted a <ahref="/api/web-events#web-app-expand">web_app_expand</a> event (boolean)</li>
<p>Params: a JSON object with the following fields:</p>
<ul>
<li><code>theme_params</code>: A <ahref="#theme-parameters">theme parameters object »</a> (object)</li>
</ul>
<p>Emitted when requested by the web app using a <ahref="/api/web-events#web-app-request-theme"><code>web_app_request_theme</code> event »</a>, or when the app theme changes. </p>
<p>Params: a JSON object with an optional <code>button_id</code> string field.</p>
<p>Emitted when the user presses a button or cancels a popup brought up by a previous <ahref="/api/web-events#web-app-open-popup"><code>web_app_open_popup</code> event »</a>. </p>
<h3><aclass="anchor"href="#simple-web-apps"id="simple-web-apps"name="simple-web-apps"><iclass="anchor-icon"></i></a>Simple web apps</h3>
<p>Simple webapps can only send back data to the bot through the MTProto API via a <ahref="/api/web-events#web-app-data-send"><code>web_app_data_send</code> JS event »</a>. </p>
<p>Simple webapps can be opened from a <ahref="/constructor/keyboardButtonSimpleWebView">keyboardButtonSimpleWebView</a> button contained in a reply keyboard identified by a <ahref="/constructor/replyKeyboardMarkup">replyKeyboardMarkup</a> constructor, or by clicking on the <ahref="/constructor/inlineBotWebView">inlineBotWebView</a> button on top of the inline result list, contained in <ahref="/constructor/messages.botResults">messages.botResults</a>.<code>switch_webview</code>, returned by <ahref="/method/messages.getInlineBotResults">messages.getInlineBotResults</a>. </p>
<p>To open them, users should call <ahref="/method/messages.requestSimpleWebView">messages.requestSimpleWebView</a> passing the original <code>url</code>, and then open a webview using the <code>url</code> contained in the returned <ahref="/constructor/simpleWebViewResultUrl">simpleWebViewResultUrl</a>. </p>
<p>If and only if the webapp was opened from a <ahref="/constructor/keyboardButtonSimpleWebView">keyboardButtonSimpleWebView</a> reply keyboard button, upon receiving a <ahref="/api/web-events#web-app-data-send"><code>web_app_data_send</code> JS event »</a> from the web app, clients should invoke <ahref="/method/messages.sendWebViewData">messages.sendWebViewData</a>, passing the following arguments:</p>
<li><code>random_id</code> - Unique random ID to avoid resending the same event multiple times</li>
<li><code>button_text</code> - Text of the <ahref="/constructor/keyboardButtonSimpleWebView">keyboardButtonSimpleWebView</a> that was pressed to open the simple web app</li>
<li><code>data</code> - Contents of the <code>data</code> field of the JS event. </li>
<p>Always ignore all <code>web_app_data_send</code> events received from <ahref="/constructor/inlineBotWebView">inlineBotWebView</a> webapps, as only <ahref="/constructor/keyboardButtonSimpleWebView">keyboardButtonSimpleWebView</a> webapps can send this event.<br>
Make sure to ignore all <code>web_app_data_send</code> events sent after the first one, <ahref="/method/messages.sendWebViewData">messages.sendWebViewData</a> must be called only once.<br>
The webview must be closed after invoking the <ahref="/method/messages.sendWebViewData">messages.sendWebViewData</a> method. </p>
<p>This will generate a <ahref="/constructor/messageActionWebViewDataSent">messageActionWebViewDataSent</a> update for the user, and a <ahref="/constructor/messageActionWebViewDataSentMe">messageActionWebViewDataSentMe</a> update for the bot, containing the event data. </p>
<h3><aclass="anchor"href="#normal-web-apps"id="normal-web-apps"name="normal-web-apps"><iclass="anchor-icon"></i></a>Normal web apps</h3>
<p>Normal webapps work similarly to <ahref="/api/bots/inline">inline bots »</a>: they send messages on behalf of the user to the chat from which the query originated.</p>
<p>Normal webapps can be opened from:</p>
<ul>
<li>A <ahref="/constructor/keyboardButtonWebView">keyboardButtonWebView</a> button contained in an inline keyboard identified by a <ahref="/constructor/replyInlineMarkup">replyInlineMarkup</a> constructor: in this case, <ahref="/constructor/keyboardButtonWebView">keyboardButtonWebView</a>.<code>url</code> must be passed to <ahref="/method/messages.requestWebView">messages.requestWebView</a>.<code>url</code>.</li>
<li>A <ahref="/constructor/botMenuButton">botMenuButton</a><ahref="/api/bots/menu">menu button »</a>: in this case, the <ahref="/method/messages.requestWebView">messages.requestWebView</a>.<code>from_bot_menu</code> flag should be set.</li>
<li>An <ahref="/api/bots/attach">attachment menu »</a>: in this case, no special flag should be set, unless the attachment menu is opened via a with a <ahref="/api/links#bot-attachment-menu-links">bot attachment menu deep link</a>, in which case the <code>start_parameter</code> should be provided to <ahref="/method/messages.requestWebView">messages.requestWebView</a>.<code>start_param</code>, if present.</li>
</ul>
<p>To open them, clients should call <ahref="/method/messages.requestWebView">messages.requestWebView</a>, and then open a webview using the <code>url</code> contained in the returned <ahref="/constructor/webViewResultUrl">webViewResultUrl</a>. </p>
<p>After loading the webview, until it is closed by a <ahref="/api/web-events#web-app-close">web_app_close event</a>, the user client must invoke <ahref="/method/messages.prolongWebView">messages.prolongWebView</a> every 60 seconds: if the method call returns <code>QUERY_ID_INVALID</code>, the webview must be closed. </p>
<p>The opened URL's fragment parameters already contain basic information about the user and a <code>query_id</code> parameter, that is exposed by the <ahref="/bots/webapps">bot web apps JS library</a>: this <code>query_id</code> can then be used <strong>by the bot</strong> to invoke <ahref="/method/messages.sendWebViewResultMessage">messages.sendWebViewResultMessage</a>, passing an <ahref="/type/InputBotInlineResult">InputBotInlineResult</a> constructor that will automatically send a message with optionally attached media, and even inline buttons on behalf of the user. </p>
<p>Another way to open web apps is by using <ahref="/api/links#named-bot-web-app-links">named bot web app links »</a>. </p>
<p>These links are different from <ahref="/api/links#bot-attachment-menu-links">bot attachment menu deep links »</a>, because they don't require the user to install an attachment menu, and a single bot can offer multiple web apps, distinguished by their <code>short_name</code>. </p>
<p>These links should be handled as follows: </p>
<ul>
<li>Check if <code>bot_username</code> parameter of the link is indeed a bot username, if so then</li>
<li>Invoke <ahref="/method/messages.getBotApp">messages.getBotApp</a>, passing an <ahref="/constructor/inputBotAppShortName">inputBotAppShortName</a> with the <code>short_name</code> contained in the <code>appname</code> query string parameter.
If the client has already encountered an app with this short name from the same bot before, pass the <code>hash</code> of the cached <ahref="/constructor/botApp">botApp</a> constructor to <ahref="/method/messages.getBotApp">messages.getBotApp</a>.</li>
<li>If a <ahref="/constructor/messages.botApp">messages.botApp</a> constructor is returned and its <code>request_write_access</code> flag is set, show a prompt to the user, indicating that the bot is asking permission to send messages to the user.<br>
If the user agrees, set the <code>write_allowed</code> flag when invoking <ahref="/method/messages.requestAppWebView">messages.requestAppWebView</a> in the next step.</li>
<li>If a <ahref="/constructor/messages.botApp">messages.botApp</a> constructor is returned, open the web app by invoking <ahref="/method/messages.requestAppWebView">messages.requestAppWebView</a>, generating an <ahref="/constructor/inputBotAppID">inputBotAppID</a> constructor from <code>id</code> and <code>access_hash</code> of the returned <ahref="/constructor/botApp">botApp</a>, or from previously cached information if we already met the bot app and <ahref="/constructor/botAppNotModified">botAppNotModified</a> was returned. <ul>
<li>If the client has clicked on the link in a Telegram chat, pass the chat's peer information into <code>peer</code>; otherwise pass the bot's peer information, instead.</li>
<li>If the <ahref="/constructor/messages.botApp">messages.botApp</a>.<code>inactive</code> flag is set, <strong>ask confirmation from the user</strong> before opening the web app; the <code>request_write_access</code> checkbox should be shown in this prompt, if needed.<br>
Confirmation should <strong>always</strong> be asked, even if the <code>inactive</code> flag is not set, when opening the link from places where the full link is not visible (i.e. <ahref="/constructor/messageEntityTextUrl">messageEntityTextUrl</a> text links, inline buttons etc.). </li>
<li>If the <code>startapp</code> query string parameter is present, pass it to <code>start_param</code> when invoking <ahref="/method/messages.requestAppWebView">messages.requestAppWebView</a>.</li>
<li>If the <ahref="/constructor/messages.botApp">messages.botApp</a>.<code>request_write_access</code> flag is set, the bot is asking permission to send messages to the user: if the user agrees, set the <code>write_allowed</code> flag when invoking <ahref="/method/messages.requestAppWebView">messages.requestAppWebView</a>.</li>
</ul>
</li>
</ul>
<p>Finally, open the webview using the <code>url</code> contained in the returned <ahref="/constructor/appWebViewResultUrl">appWebViewResultUrl</a>. </p>
<p>Since there is no linked inline query, <code>web_app_data_send</code> events must be ignored.<br>
The bot can, however, write to the user directly if it already has a chat with the user or if it requested permission via <code>request_write_access</code> and the user granted it with <code>write_allowed</code>. </p>
<p>Bot web apps can be themed according to the following theme parameters, passed as a JSON object to the <code>theme_params</code> parameter of the <ahref="/method/messages.requestSimpleWebView">messages.requestSimpleWebView</a> and <ahref="/method/messages.requestWebView">messages.requestWebView</a> methods. </p>