Appearance
Mini Apps
A Mini App is an ordinary HTTPS page that GROM opens inside itself — a WebView on mobile, a sandboxed iframe on web. The page gets a signed description of who opened it, a bridge for talking to the client, and the host app's theme. Everything else is your own stack.
Not implemented
Mini Apps do not run yet — there is no client runtime and no SDK to include. See implementation status.
The pages in this section describe what is specified, not what answers. No sample here has been executed against a running client, because there is not one.
What you actually get
Three things a plain web page cannot do on its own:
- Identity without a login screen. The launch context (
initData) names the user who opened the app, signed with your bot token. No password, no OAuth round trip. This is the whole point, and it is also the one part you can get catastrophically wrong — see Validating initData. - A bridge to the client. Your page can drive the native chrome: main button, back button, haptics, popups, theme, viewport, biometrics, storage. See Events.
- A way back into the chat. Depending on how the app was opened, it can post a message as the user, or hand data back to the bot. Which one you get is decided by the launch surface, not by you at runtime.
When not to build one
A Mini App is a web app with a distribution channel attached. That is worth something, and it is not free — you are now maintaining a frontend, a backend, TLS, and a signature check that is load-bearing for authentication.
Chat wins for anything short. A yes/no confirmation, a status lookup, a three-field form: inline buttons and a text reply are faster to build, faster to open, work offline-ish, and cannot leak a user's identity through a verification bug you did not write carefully.
A Mini App wins when the interaction has real state — a catalogue with a cart, a seat map, a calendar, a canvas, anything with more than a handful of screens. If you find yourself simulating pagination with editMessageText, that is the signal.
The opening modes
The TZ heads this section seven modes and then lists eight. The likeliest reading is that the menu button is a variant of the inline-button flow rather than a mode of its own, but the TZ does not say that, so all eight are listed here as written.
Which mode is in play determines the launch context you receive and the routes back into the chat, so it is not a cosmetic distinction.
| Mode | Opened from | Client request | Way back into the chat |
|---|---|---|---|
| Main Mini App | Open App on the bot's profile | requestMainWebView | Your own backend |
| Keyboard button | A reply-keyboard button | requestSimpleWebView | web_app_data_send → WebAppData on a message |
| Inline button | An inline-keyboard button | requestWebView | query_id → answerWebAppQuery |
| Menu button | The chat's menu button | requestWebView + from_bot_menu | Same flow as the inline button |
| Attachment menu | The 📎 attachment menu | requestWebView | Your own backend |
| Inline mode | A button above inline results | requestSimpleWebView + from_switch_webview | web_app_switch_inline_query |
| Side menu | An installed entry in the burger menu | requestSimpleWebView + from_side_menu | Your own backend |
| Direct link | A code.casinoplaneta.info URL | requestAppWebView | Your own backend |
Those request*WebView names are client-to-server protocol calls, not HTTP Bot API methods you invoke. They appear here because the TZ specifies them by name and because which one fires is what decides the row above.
Main Mini App
Enabled through @GromCore, which sets a bot_has_main_app flag and puts an Open App button on the bot's profile. The URL is not passed at launch — the platform holds it. The loading screen is drawn from the bot's stored background_color, background_dark_color, header_color, header_dark_color and a placeholder_path SVG silhouette, so the app appears to start before it has loaded.
Bots with a main app can carry a gallery of preview media on their profile, up to 12 items per language by default, with the user's language picked automatically. Editing them requires both bot_can_edit and bot_has_main_app.
Keyboard button
The only mode that can call web_app_data_send. The payload is capped at 4096 bytes, is accepted exactly once — later events are ignored and the WebView is force-closed — and surfaces as a service message: the user sees only the button text, the bot receives text plus data.
WARNING
That data arrives through the client, so nothing about the transport authenticates it. If the payload matters, verify it against state your backend already holds, or have the page talk to your backend directly and treat the bot message as a notification.
Inline and menu button
These return a query_id in the launch context. While the WebView is open the client re-arms that id every 60 seconds; if the server answers QUERY_ID_INVALID the WebView closes. Your bot spends the id once via answerWebAppQuery, which posts as the user, not as the bot.
Unresolved contradiction
The answerWebAppQuery reference states that a menu-button app has no query_id. B13.4 states that the menu button runs the same requestWebView flow as the inline button, which does produce one. Both cannot be true. Neither has been tested against a client, because there is not one. Treat this as unresolved rather than as documentation.
Inline mode
Launched from a button above inline results. On finishing, the app emits web_app_switch_inline_query with a query string and a chat_types array drawn from users, bots, groups, channels. A non-empty array opens a chat picker; an empty one reuses the current chat.
Direct link
The most flexible mode, and the only one where a single bot can host several distinct apps, told apart by short_name:
https://code.casinoplaneta.info/<botname>/<appname>?startapp=<param>&mode=fullscreenThe client resolves the bot, fetches the app definition — passing a cache hash so an unchanged app returns botAppNotModified — and then opens it against a peer. A link clicked inside a chat launches against that chat; anywhere else, against the bot itself.
Confirmation before opening follows fixed rules: a single dialog carrying a checkbox where write access is requested, never two stacked dialogs. Confirmation is always shown when the app is opened from somewhere the full URL is not visible — a text link, an inline button — and bots on the whitelisted_bots list skip it entirely.
Deep links
Every entry point has a URL form on code.casinoplaneta.info:
| Link | Opens |
|---|---|
code.casinoplaneta.info/<bot>?startapp=<param>&mode=compact|fullscreen | Main Mini App |
code.casinoplaneta.info/<bot>/<appname>?startapp=<param> | Direct link Mini App |
code.casinoplaneta.info/<bot>?attach=&startattach=<param> | Attachment menu |
start_param is 1–64 characters from A-Za-z0-9_-. Anything longer or outside that set is not a link you can rely on. That cap and the other numbers on this page are collected under Limits — all of them specified targets, none of them enforced by anything running.
Today these open a web page offering a download rather than the app itself: the server half of Android App Links is published, the client half is not. Status has the detail.
Next
- Quickstart — the smallest page that would work
- Validating initData — read this before you write an authorization check
- Events — the bridge, in both directions
- Design — theme, viewport, safe area
- JavaScript SDK — the surface, and what is missing from it