SMeetBot API

SMeet Bot API

Mini Apps

A Mini App is a web page of the bot's author that opens inside SMeet, in a window over the chat with the bot. The bot opens it with a button, and a form the person sends from it reaches the bot as an update in the same queue as messages.

What a Mini App is#

A bot can have one Mini App. Its owner creates it in My bots (SMeet BotFather opens the screen), gives it a production and a test address on their own HTTPS server, and sends it for review; only the reviewed configuration runs in production. Before every launch SMeet shows the person who made the Mini App and what it will receive, and before every form it sends to the bot SMeet asks the person to confirm.

The page and its server are the author's. The bot's program does not have to serve them: it receives what the person sent and answers in the chat, as with any message.

The page, the person's sign-in, the author's server and publishing are described in the SMeet Mini Apps documentation: SDK, Sign-in, Your server and Publishing. This page covers what the bot sees.

getMe says whether this server runs Mini Apps and describes the bot's own:

JSON
{
  "capabilities": ["chat_context", "access_revisions", "message_revisions", "initiator_conditions",
                   "strict_conditions", "mini_apps"],
  "mini_app": {"id": "1000", "status": "active", "published": true}
}
  • mini_apps in capabilities means the mini_app button and the mini_app_submission update work on this server now. When it is missing (an older server, or Mini Apps switched off on the platform), send the same text without the button.
  • mini_app is present while the bot has a Mini App. status is active, paused_by_owner or blocked_by_admin; published says whether a reviewed configuration is live. id is the app_id of the bot's submissions.

The mini_app button#

A button with mini_app opens the bot's Mini App instead of sending a press to the bot:

JSON
{
  "chat_id": "70051",
  "text": "Fill in the request form. It opens in SMeet: press the button below.",
  "reply_markup": {
    "inline_keyboard": [
      [{"text": "Open the form", "mini_app": {"start_param": "vpn-access"}}],
      [{"text": "Help", "callback_data": "help"}]
    ]
  }
}
  • A button has exactly one of callback_data, url and mini_app. mini_app is an object; start_param is optional, up to 64 characters of A-Z a-z 0-9 _ -, and is handed to the Mini App when it opens. Empty means no parameter.
  • Only in a private chat of a bot that has a Mini App, and not a deleted one. In a group, topic or channel, or from a bot without a Mini App, sendMessage, editMessageText, sendPhoto and sendDocument answer 400 INVALID_REQUEST with "field": "reply_markup" and "reason": "mini_app_not_configured". A wrong start_param gets INVALID_REQUEST with "field": "reply_markup" and no reason.
  • The button opens the published configuration only. While there is none, or while the Mini App is paused or blocked, the person sees that it is not available; the button stays in the chat.
  • The bot is not told about a press. The result of the call shows the button back as you wrote it, except that an empty start_param comes back as no start_param.
  • Apps without Mini App support (SMeet for Android, the desktop app and older versions of the web app and of SMeet for iOS) show the button inactive and explain nothing. Say in the text where the form opens, as in the example.

The same window opens from a link to the bot's profile with startapp: https://messenger.scrile.com/u/<bot_username>?startapp=<param>, where param follows the rule of start_param and may be empty. A person who opens the link without the bot started is offered to start it and open the Mini App in one step.

The mini_app_submission update#

When a person confirms a form, the bot receives:

JSON
{
  "update_id": "4380",
  "event_id": "2c7a1e94-3b6f-4d1a-8e2b-6f0c9d3a5b17",
  "type": "mini_app_submission",
  "date": "2026-10-10T12:00:00Z",
  "mini_app_submission": {
    "id": "6f1c9a2e-8a8f-4d6b-9a4e-6d2f0a3b7c11",
    "app_id": "1000",
    "environment": "production",
    "client_request_id": "f3b0c9e2-form-1",
    "from": {"id": "103", "display_name": "Dmitry", "username": "dmitry", "is_bot": false},
    "chat": {"id": "5501", "type": "private", "access_revision": "1088"},
    "data": {"subject": "VPN", "priority": "high"},
    "date": "2026-10-10T12:00:00Z"
  }
}
  • SMeet fixes app_id, environment, from and chat: the page cannot choose them. from.id is the same person as from.id of their messages, and chat.id is your private chat with them.
  • data is the form exactly as the page sent it: a JSON object of up to 16 KiB. Check it as you check any input from people.
  • One submission is one update. The page names each form with a requestId, which arrives as client_request_id; when the page sends the same form again (after a network error, a reload, a second window), SMeet gives back the same submission and writes no second update. Deduplicate your own work by event_id, as for every update (Deduplication).
  • id is the submission as the person's app and the Mini App know it.
  • Submissions arrive through getUpdates or your webhook in queue order, with the same confirmation, FAILED list and 7-day retention as every update (Receiving updates).

Answering the person#

Answer in chat.id. The submission's chat.access_revision is the revision of the conversation when the person opened the Mini App; pass it as expected_access_revision, and build the key from the update:

HTTP
POST /bot-api/v1/sendMessage HTTP/1.1
Host: messenger.scrile.com
Authorization: Bearer sbt1_EXAMPLE_REPLACE_WITH_YOUR_TOKEN
Idempotency-Key: 2c7a1e94-3b6f-4d1a-8e2b-6f0c9d3a5b17:reply
Content-Type: application/json

{"chat_id": "5501", "text": "Request SM-1042 is created. We will answer here.",
 "expected_access_revision": "1088"}
  • 403 BOT_STOPPED_OR_BLOCKED: the person stopped or blocked the bot. 409 ACCESS_REVISION_CONFLICT: the conversation changed since the form was sent (Stop and Start again, for example). Both are final for this answer: do not retry it, and if the work matters, wait for the person to come back (Conditional requests).
  • A server without access_revisions in capabilities does not check the condition: an older server skips the parameter and does the action without it, and a 1.2.0 server with conditions switched off refuses the call with 400 INVALID_REQUEST and "field": "expected_access_revision". Send the condition only when the capability is listed (Capabilities, rule 1).
  • The person may have revoked the Mini App's access from its menu. Submissions the bot has not received yet are then cancelled, and the conversation itself stays: the bot may still answer what it has received.

When a submission is cancelled#

A submission that has not reached the bot is cancelled, and a long polling program sees it in skipped with the reason cancelled, when:

What happenedCancelled
The person stopped or blocked the bot, or left the bot's spaceAs every update of that chat
The person chose Revoke access in the Mini App's menuPending and FAILED submissions of that conversation
The owner deleted the Mini AppPending and FAILED submissions of the Mini App
The platform blocked the Mini App or all Mini Apps of its ownerPending and FAILED submissions of the Mini App
The organisation switched bots offEvery pending and FAILED update of the bot, at once (Operations)
The organisation switched Mini Apps offWhen SMeet is about to hand the submission over
A test submission whose sender is no longer the owner, an accepted tester or a reviewerWhen SMeet is about to hand the submission over

The same checks run when the owner retries a FAILED submission: one that may no longer reach the bot is cancelled instead of replayed. A pause by the owner, a technical pause of the platform, a closed window and an expired login of the page cancel nothing; the submission waits in the queue as other updates do.

What the person sees#

The Mini App's page can ask SMeet where its submission stands:

StatusMeans
pendingIn the bot's queue, not confirmed yet
deliveredThe bot confirmed it: a long polling offset past it, or a 2xx of your webhook
failedIt is in the owner's FAILED list
cancelledIt was cancelled before delivery, or the owner skipped it
expiredIt was not confirmed within 7 days, or it is older than that

So confirm a submission only once it is stored on your side: the page may tell the person "delivered" from then on.

The test environment#

"environment": "test" comes from the test address of the Mini App, which only the owner, testers who accepted an invitation and the reviewers of an open review can open. Production and test never share a submission, even with the same requestId. Keep test submissions out of your real work: a test form should not create a real request.