<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 mini apps using the MTProto API: see <ahref="/bots/webapps">here for an overview of the mini-app side JS API»</a>. </p>
<h3><aclass="anchor"href="#main-mini-apps"id="main-mini-apps"name="main-mini-apps"><iclass="anchor-icon"></i></a>Main Mini Apps</h3>
<p>Main Mini Apps are configured through <ahref="https://t.me/botfather">@botfather</a>. </p>
<p>Once enabled, the <ahref="/constructor/user">user</a>.<code>bot_has_main_app</code> flag will be set, and an "Open App" button should be shown on the bot's profile page. </p>
<p>Clicking on this button should open the Main Mini App, by invoking <ahref="/method/messages.requestMainWebView">messages.requestMainWebView</a>: no URL needs to be passed to the method, because the Main Mini App URL is configured through <ahref="https://t.me/botfather">@botfather</a>. </p>
<p>After invoking <ahref="/method/messages.requestMainWebView">messages.requestMainWebView</a> and obtaining a <ahref="/constructor/webViewResultUrl">webViewResultUrl</a> result, clients should open a webview using the <code>url</code> contained in the returned <ahref="/constructor/webViewResultUrl">webViewResultUrl</a>. </p>
<p>The bot's profile page should also show a list of photos and videos, previewing the features offered by the Main Mini App, see <ahref="#main-mini-app-previews">Main Mini App previews</a> for more info on how to configure and render them. </p>
<p>Main Mini Apps are also featured in the in-app <ahref="/api/search#apps-tab">Mini App Store»</a>. </p>
<p>The Main Mini App should also be directly opened when <ahref="/api/links#main-mini-app-links">clicking on a Main Mini App deep link»</a>; the <code>compact</code> flag of the method must be set if the <code>mode</code> parameter in the link is set and equal to <code>compact</code>; any eventual <code>start_param</code> present in the link must also be passed to the method. </p>
<h4><aclass="anchor"href="#main-mini-app-previews"id="main-mini-app-previews"name="main-mini-app-previews"><iclass="anchor-icon"></i></a>Main Mini App previews</h4>
<p>After enabling a <ahref="#main-mini-apps">main mini app»</a> in <ahref="https://t.me/botfather">@botfather</a>, bots gain the ability to display <strong>localized</strong> preview medias (photos and videos) on their profile page, offering examples of what the app can do. </p>
<p>If a bot has some preview medias, the <ahref="/constructor/botInfo">botInfo</a>.<code>has_preview_medias</code> flag will be set (<ahref="/constructor/botInfo">botInfo</a> is contained in the bot's <ahref="/constructor/userFull">userFull</a>).<br>
Clients should then invoke <ahref="/method/bots.getPreviewMedias">bots.getPreviewMedias</a> to fetch&download the preview medias once the user opens the bot's profile page.<br>
The method will automatically select the correctly localized variant of each preview, according to the language code of the user (as passed to <ahref="/method/initConnection">initConnection</a> when first setting up the client). </p>
<p>Bot owners may edit the preview medias directly through the API. </p>
<p>To check whether the current user can edit the preview medias of a bot, make sure both the <code>bot_can_edit</code> and <code>bot_has_main_app</code> flags of the bot's <ahref="/constructor/user">user</a> constructor are set. </p>
<p>Then, <ahref="/method/bots.getPreviewInfo">bots.getPreviewInfo</a> should be invoked by bot owners to fetch the previously configured preview medias.<br>
Pass an empty string to <code>lang_code</code> when first invoking the method to fetch the previously configured default preview medias (used as fallback if there is no preview for the current user's language), and the list of <code>lang_codes</code> for which there are localized previews; then re-invoke the method to fetch the previously configured previews for each of the returned <code>lang_codes</code>. </p>
<p>(Note: technically non-owners may also invoke <ahref="/method/bots.getPreviewInfo">bots.getPreviewInfo</a>, but it will always behave exactly as <ahref="/method/bots.getPreviewMedias">bots.getPreviewMedias</a>, returning only previews for the current language and an empty <code>lang_codes</code> array, regardless of the passed <code>lang_code</code>, so please only use <ahref="/method/bots.getPreviewMedias">bots.getPreviewMedias</a> if you're not the owner). </p>
<p>Then, use <ahref="/method/bots.addPreviewMedia">bots.addPreviewMedia</a>, <ahref="/method/bots.editPreviewMedia">bots.editPreviewMedia</a>, <ahref="/method/bots.reorderPreviewMedias">bots.reorderPreviewMedias</a>, <ahref="/method/bots.deletePreviewMedia">bots.deletePreviewMedia</a>, uploading medias using <ahref="/method/messages.uploadMedia">messages.uploadMedia</a> as usual to setup fallback and localized previews for existing and new languages. </p>
<p>As specified above, each language can have a distinct set of previews that may be edited and reordered independently.
The default, fallback language has an empty <code>lang_code</code>. </p>
<p>A maximum of <ahref="/api/config#bot-preview-medias-max">bot_preview_medias_max»</a> preview medias may be added per localization key.</p>
<h3><aclass="anchor"href="#keyboard-button-mini-apps"id="keyboard-button-mini-apps"name="keyboard-button-mini-apps"><iclass="anchor-icon"></i></a>Keyboard Button Mini Apps</h3>
<p>Keyboard Button Mini Apps should be opened when the user clicks a <ahref="/constructor/keyboardButtonSimpleWebView">keyboardButtonSimpleWebView</a> contained in a reply keyboard identified by a <ahref="/constructor/replyKeyboardMarkup">replyKeyboardMarkup</a> constructor, by invoking <ahref="/method/messages.requestSimpleWebView">messages.requestSimpleWebView</a> passing the button's <code>url</code> to the <code>url</code> parameter. </p>
<p>After invoking <ahref="/method/messages.requestSimpleWebView">messages.requestSimpleWebView</a> and obtaining a <ahref="/constructor/webViewResultUrl">webViewResultUrl</a> result, clients should open a webview using the <code>url</code> contained in the returned <ahref="/constructor/webViewResultUrl">webViewResultUrl</a>. </p>
<p>Keyboard Button Mini Apps can send data back 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>Upon receiving a <ahref="/api/web-events#web-app-data-send"><code>web_app_data_send</code> JS event»</a><strong>only</strong> from Keyboard Button Mini Apps, clients should invoke <ahref="/method/messages.sendWebViewData">messages.sendWebViewData</a>, passing the following arguments:</p>
<li><code>button_text</code> - Text of the <ahref="/constructor/keyboardButtonSimpleWebView">keyboardButtonSimpleWebView</a> that was pressed to open the simple Mini App</li>
<p>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. 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="#inline-button-mini-apps"id="inline-button-mini-apps"name="inline-button-mini-apps"><iclass="anchor-icon"></i></a>Inline button Mini Apps</h3>
<p>Inline Button Mini Apps 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>When the user clicks on an <ahref="/constructor/keyboardButtonWebView">keyboardButtonWebView</a> inline button contained in an inline keyboard identified by a <ahref="/constructor/replyInlineMarkup">replyInlineMarkup</a> constructor, <ahref="/method/messages.requestWebView">messages.requestWebView</a> should be invoked, passing <ahref="/constructor/keyboardButtonWebView">keyboardButtonWebView</a>.<code>url</code> must be passed to the method's <code>url</code> parameter. </p>
<p>Then, clients should 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 Mini Apps JS library</a>: this <code>query_id</code> can then be passed to the bot (within the Mini App itself, for example via an AJAX query or form submission to the server hosting the Mini App and the bot) and then 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>
<h3><aclass="anchor"href="#menu-button-mini-apps"id="menu-button-mini-apps"name="menu-button-mini-apps"><iclass="anchor-icon"></i></a>Menu button Mini Apps</h3>
<p>Menu button Mini Apps work similarly to <ahref="#inline-button-mini-apps">inline button Mini Apps»</a>: they send messages on behalf of the user to the chat from where the <ahref="/api/bots/menu">bot menu button»</a> was clicked. </p>
<p>Menu button Mini Apps can be opened from 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, and the <ahref="/constructor/botMenuButton">botMenuButton</a>.<code>url</code> field must be passed to the method's <code>url</code> parameter. </p>
<p>The full flow is identical to the flow for <ahref="#inline-button-mini-apps">inline button Mini Apps»</a>, apart from the different flags passed to <ahref="/method/messages.requestWebView">messages.requestWebView</a>, as described above.</p>
<h3><aclass="anchor"href="#attachment-menu-mini-apps"id="attachment-menu-mini-apps"name="attachment-menu-mini-apps"><iclass="anchor-icon"></i></a>Attachment menu Mini Apps</h3>
<p>Attachment menu Mini Apps work similarly to <ahref="#inline-button-mini-apps">inline button Mini Apps»</a>: they send messages on behalf of the user to the chat where the bot's <ahref="/api/bots/attach">attachment menu»</a> was opened. </p>
<p>Attachment menu Mini Apps can be opened from an <ahref="/api/bots/attach">attachment menu entry»</a>: in this case, no special flag should be set when invoking <ahref="/method/messages.requestWebView">messages.requestWebView</a>. </p>
<p>Attachment menu Mini Apps can also be opened from a <ahref="/api/links#bot-attachment-or-side-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, and the <code>compact</code> flag must be set if the <code>mode</code> parameter is set and equal to <code>compact</code>.</p>
<p>The full flow is identical to the flow for <ahref="#inline-button-mini-apps">inline button Mini Apps»</a>, apart from the different flags passed to <ahref="/method/messages.requestWebView">messages.requestWebView</a>, as described above.</p>
<h3><aclass="anchor"href="#inline-mode-mini-apps"id="inline-mode-mini-apps"name="inline-mode-mini-apps"><iclass="anchor-icon"></i></a>Inline mode Mini Apps</h3>
<p>Not to be confused with <ahref="#inline-button-mini-apps">inline button mini apps»</a>. </p>
<p>Inline mode Mini Apps can be used to generate a custom set of inline results in response to a user's <ahref="/api/bots/inline">inline query»</a> via a <ahref="/api/web-events#web-app-switch-inline-query"><code>web_app_switch_inline_query</code> JS event»</a>. </p>
<p>Inline mode Mini Apps can be opened by clicking on an <ahref="/constructor/inlineBotWebView">inlineBotWebView</a> button returned at the 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>Pass the <code>url</code> to <ahref="/method/messages.requestSimpleWebView">messages.requestSimpleWebView</a>, while also setting the <code>from_switch_webview</code> flag. </p>
<p>After invoking <ahref="/method/messages.requestSimpleWebView">messages.requestSimpleWebView</a> and obtaining a <ahref="/constructor/webViewResultUrl">webViewResultUrl</a> result, clients should open a webview using the <code>url</code> contained in the returned <ahref="/constructor/webViewResultUrl">webViewResultUrl</a>. </p>
<p>Once the user has finished making their choices in the Mini App, a <ahref="/api/web-events#web-app-switch-inline-query"><code>web_app_switch_inline_query</code> JS event»</a> should be emitted, containing a JSON object with the following fields:</p>
<li><code>query</code> - The inline query that will be inserted in the chat's input field, after the bot's username.<br>
May be an empty string, in which case just the bot's username will be inserted, triggering an empty inline query.</li>
<li><code>chat_types</code> - An array of strings, containing a combination of <code>users</code>, <code>bots</code>, <code>groups</code>, <code>channels</code>.<br>
If non-empty, the client should prompt the user to choose a specific chat of the specified type(s), then open the chosen chat and inserts the bot's username and the specified inline query in the input field.<br>
The array values specify which types of chats the user will be able to choose from.<br>
<p>Upon receiving a <ahref="/api/web-events#web-app-switch-inline-query"><code>web_app_switch_inline_query</code> JS event»</a> from the Mini App, the client should <ahref="/api/bots/inline">make a new inline query»</a> to the same bot, with the newly specified <code>query</code>, either in the current chat or in the newly chosen chat, as specified by the <code>chat_types</code> field. </p>
<h3><aclass="anchor"href="#side-menu-mini-apps"id="side-menu-mini-apps"name="side-menu-mini-apps"><iclass="anchor-icon"></i></a>Side menu Mini Apps</h3>
<p>Side menu Mini Apps can be opened by clicking on the installed <ahref="/api/bots/attach">side menu entry»</a>. </p>
<p>This action must trigger a <ahref="/method/messages.requestSimpleWebView">messages.requestSimpleWebView</a> query with the <code>from_side_menu</code> flag set: clients should open a webview using the <code>url</code> contained in the returned <ahref="/constructor/webViewResultUrl">webViewResultUrl</a>. </p>
<p>After invoking <ahref="/method/messages.requestSimpleWebView">messages.requestSimpleWebView</a> and obtaining a <ahref="/constructor/webViewResultUrl">webViewResultUrl</a> result, clients should open a webview using the <code>url</code> contained in the returned <ahref="/constructor/webViewResultUrl">webViewResultUrl</a>. </p>
<h3><aclass="anchor"href="#direct-link-mini-apps"id="direct-link-mini-apps"name="direct-link-mini-apps"><iclass="anchor-icon"></i></a>Direct Link Mini Apps</h3>
<p>These links are different from all other Mini App 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 <code>short_name</code>. </p>
<p>Check if <code>bot_username</code> parameter of the link is indeed a bot username, if so then</p>
</li>
<li>
<p>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>.</p>
</li>
<li>
<p>If a <ahref="/constructor/messages.botApp">messages.botApp</a> constructor is returned, open the Mini 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. </p>
<ul>
<li>
<p>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.</p>
</li>
<li>
<p>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: <strong>ask confirmation from the user</strong> with a prompt and a checkbox, and if the user agrees, set the <code>write_allowed</code> flag when invoking <ahref="/method/messages.requestAppWebView">messages.requestAppWebView</a>.</p>
</li>
<li>
<p>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 Mini 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.). </p>
<p>Don't use two prompts if confirmation to open the Mini App is required <em>and</em><code>request_write_access</code> is set, use just one prompt with an optional checkbox for <code>request_write_access</code>. </p>
</li>
<li>
<p>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>.</p>
<p>Finally, open the webview using the <code>url</code> contained in the returned <ahref="/constructor/webViewResultUrl">webViewResultUrl</a>. </p>
<h3><aclass="anchor"href="#outgoing-events-mini-app-to-client"id="outgoing-events-mini-app-to-client"name="outgoing-events-mini-app-to-client"><iclass="anchor-icon"></i></a>Outgoing events: Mini App to client</h3>
<p>Mini 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 Mini App to the client»</a>. </p>
<h3><aclass="anchor"href="#incoming-events-client-to-mini-app"id="incoming-events-client-to-mini-app"name="incoming-events-client-to-mini-app"><iclass="anchor-icon"></i></a>Incoming events: Client to Mini App</h3>
<p>Mini 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 Mini App from the client, if the client invokes 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 (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>
<p>Sent by the client when the user presses the settings button, if it was <ahref="/api/web-events#web-app-setup-settings-button">previously enabled by a <code>web_app_setup_settings_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 Mini 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 Mini App is expanded to its maximum height after the user swiped up or after the Mini 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 Mini 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>Bot Mini 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>, <ahref="/method/messages.requestWebView">messages.requestWebView</a> and <ahref="/method/messages.requestAppWebView">messages.requestAppWebView</a> methods. </p>
<p>This JSON object has the following keys, containing color theme information (hex string, RGB, no alpha) to pass to the Mini App:</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>
<p>Params: a JSON object with the following fields:</p>
<ul>
<li><code>status</code> - <code>allowed</code> or <code>cancelled</code></li>
</ul>
<p>Used by clients to reply to a <ahref="/api/web-events#web-app-request-write-access"><code>web_app_request_write_access</code> event»</a>, indicating whether the user has allowed the bot to send messages to the user (<code>allowed</code>) or not (<code>cancelled</code>). </p>
<p>Params: a JSON object with the following fields:</p>
<ul>
<li><code>status</code> - <code>sent</code> or <code>cancelled</code></li>
</ul>
<p>Used by clients to reply to a <ahref="/api/web-events#web-app-request-phone"><code>web_app_request_phone</code> event»</a>, indicating whether the user has shared their phone number with the bot (<code>allowed</code>) or not (<code>cancelled</code>). </p>
<li><code>unknown</code> - biometrics of an unknown type</li>
</ul>
</li>
<li><code>access_requested</code> - boolean, indicates whether the app has previously requested permission to use biometrics through a <ahref="/api/web-events#web-app-biometry-request-access"><code>web_app_biometry_request_access</code> event»</a></li>
<li><code>access_granted</code> - boolean, indicates whether the user has granted the app permission to use biometrics in response to a <ahref="/api/web-events#web-app-biometry-request-access"><code>web_app_biometry_request_access</code> event»</a>.<br>
If false and <code>access_requested</code> is true, the user has denied the app permission to use biometrics, in which case the app should open a prompt notifying the user that the biometric settings must be changed to use biometrics, and if the user clicks on the in-app confirm button, a <ahref="/api/web-events#web-app-biometry-open-settings">web_app_biometry_open_settings event»</a> must be emitted.</li>
<li><code>token_saved</code> - boolean, whether a token was safely stored on-device by a previous <ahref="/api/web-events#web-app-biometry-update-token">web_app_biometry_update_token event»</a>. </li>
<li><code>device_id</code> - string, a unique device identifier that can be used to match the token to the device.</li>
</ul>
<p>Used by clients to reply to a <ahref="/api/web-events#web-app-biometry-get-info"><code>web_app_biometry_get_info</code> event»</a> or a <ahref="/api/web-events#web-app-biometry-request-access"><code>web_app_biometry_request_access</code> event»</a>. </p>
<p>Params: a JSON object with the following fields:</p>
<ul>
<li><code>status</code> - string, one of:<ul>
<li><code>updated</code> - If the token was successfully updated.</li>
<li><code>removed</code> - If the token was successfully removed.</li>
<li><code>failed</code> - If biometric authentication failed, or the app doesn't have permission to use biometrics (a <ahref="#biometry-info-received">biometry_info_received event»</a> event will also be emitted if the app hasn't previously initialized the state using <ahref="/api/web-events#web-app-biometry-get-info"><code>web_app_biometry_get_info</code> event»</a> or a <ahref="/api/web-events#web-app-biometry-request-access"><code>web_app_biometry_request_access</code> event»</a>). </li>
</ul>
</li>
</ul>
<p>Used by clients to reply to a <ahref="/api/web-events#web-app-biometry-update-token"><code>web_app_biometry_update_token</code> event»</a>. </p>
<p>Params: a JSON object with the following fields:</p>
<ul>
<li><code>status</code> - string, either <code>authorized</code> or <code>failed</code>.<br>
If <code>failed</code>, a <ahref="#biometry-info-received">biometry_info_received event»</a> event will also be emitted if the app hasn't previously initialized the state using <ahref="/api/web-events#web-app-biometry-get-info"><code>web_app_biometry_get_info</code> event»</a> or a <ahref="/api/web-events#web-app-biometry-request-access"><code>web_app_biometry_request_access</code> event»</a>. </li>
<li><code>token</code> - optional string, set if <code>status</code> is <code>authorized</code>, contains the token previously set using the <ahref="/api/web-events#web-app-biometry-update-token"><code>web_app_biometry_update_token</code> event»</a>. </li>
</ul>
<p>Used by clients to reply to a <ahref="/api/web-events#web-app-biometry-request-auth"><code>web_app_biometry_request_auth</code> biometric authentication request»</a>. </p>
<p>Params: a JSON object with the following fields:</p>
<ul>
<li><code>req_id</code> - The <code>req_id</code> from the <code>web_app_invoke_custom_method</code> request</li>
<li><code>result</code> - The JSON data contained in the response of the <ahref="/method/bots.invokeWebViewCustomMethod">bots.invokeWebViewCustomMethod</a> method, if the method call succeeded</li>
<li><code>error</code> - The text of the RPC error, if the method call failed</li>
</ul>
<p>Used by clients to reply to <ahref="/api/web-events#web-app-invoke-custom-method"><code>web_app_invoke_custom_method</code> events»</a>. </p>
<p>Params: a JSON object with the following fields:</p>
<ul>
<li><code>req_id</code> - The <code>req_id</code> from the <code>web_app_read_text_from_clipboard</code> request</li>
<li><code>data</code> - A string with the clipboard contents (optional, if not provided consider the request failed)</li>
</ul>
<p>Used by clients to reply to <ahref="/api/web-events#web-app-read-text-from-clipboard"><code>web_app_read_text_from_clipboard</code> events»</a>. </p>
<p>Params: a JSON object with the following fields:</p>
<ul>
<li><code>data</code> - string with the contents of a scanned QR code.</li>
</ul>
<p>Emitted by clients if a new QR code was scanned by the native QR code scanner opened with a <ahref="/api/web-events#web-app-open-scan-qr-popup"><code>web_app_open_scan_qr_popup</code> event»</a>. </p>
<p>Params: <code>null</code> or an empty object</p>
<p>Emitted by clients if the QR code scanner popup opened with a <ahref="/api/web-events#web-app-open-scan-qr-popup"><code>web_app_open_scan_qr_popup</code> event»</a> was closed by the user or failed to open altogether due to permission issues.</p></div>