Skip to content

Bots

Bots

Build bots for Min with a Telegram-compatible Bot API: create a bot, get a token, receive updates by webhook or long polling, send messages with inline keyboards.

Min bots speak a subset of the Telegram Bot API: the same URL shape (/bot<token>/<method>), the same {"ok":true,"result":…} envelope and the same object shapes (Message, User, Chat, Update, CallbackQuery, InlineKeyboardMarkup). Many Telegram libraries work after changing the base URL. Everything not listed in the method reference is not implemented and returns 404.

SupportedNot supported (yet)
Text, photo, document, video, audio messages; edit/delete own messagesReply keyboards, force reply, inline mode, Mini Apps, payments
Inline keyboards (callback_data, url) + answerCallbackQueryparse_mode / entities (text is sent as is)
getUpdates long polling, webhooks with secret tokenStickers, polls, voice, locations, contacts via Bot API
Commands (setMyCommands/getMyCommands)@username as chat_id; chat administration methods
Private chats, groups, channelsShowing sendChatAction to users; allowed_updates filtering

1. Create a bot#

Any user can own up to 20 bots. Bot usernames must end with bot (e.g. weather_bot). Two ways:

  • @BotFather — open a chat with @botfather in Min and send /newbot Weather Bot | weather_bot. Other commands: /mybots, /token <username> (new token), /revoke <username> (delete).
  • API — POST /api/bots with your user token (see Bot management).
bash
curl -X POST https://minchatapp.com/api/bots \
  -H "Authorization: Bearer $USER_TOKEN" -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{"name":"Weather Bot","username":"weather_bot"}'
# → {"success":true,"bot":{"id":5,"username":"weather_bot",…},"token":"5:RyNKXZs7Va1LvKqSLj6Snsoy2FplGb1n8FV"}
The token (<bot_id>:<secret>) is shown only once and is stored hashed. Keep it secret; regenerate it with /token or POST /api/bots/{id}/token if it leaks.

2. Call the Bot API#

Methods accept GET or POST with parameters in the query string, application/x-www-form-urlencoded, multipart/form-data (files) or JSON. Nested objects (reply_markup, commands) may be JSON strings.

text
https://minchatapp.com/bot<token>/<method>
https://minchatapp.com/bot/<token>/<method>        (alternative form)
https://minchatapp.com/file/bot<token>/<file_path> (file download)
json
{"ok": true, "result": { … }}
{"ok": false, "error_code": 400, "description": "Bad Request: chat not found"}

error_code is the HTTP status: 400 bad request, 401 bad token, 404 unknown method, 409 getUpdates while a webhook is set, 429 rate limit, 500 server error.

Chats and ids#

chat.idMeaning
positivePrivate chat; the id is the peer user id. The bot can message any user by id — the private chat is created on first send.
negativeGroup or channel: -<conversation_id>. The bot must be a member (add it like a user: POST /api/conversations/{id}/members with the bot's user id). In channels the bot needs a staff role to post.

message_id is the global Min message id. file_id values are bot-scoped.

3. Receive updates#

An update is created for every bot member of a chat when someone else sends (message) or edits (edited_message) a message, and when a user presses an inline button (callback_query). Privacy mode: in groups a bot only receives commands (/start, /weather@weather_bot) unless can_read_all_group_messages is enabled for it. In private chats and channels it receives everything.

Long polling#

Call getUpdates in a loop with timeout (up to 50 s) and offset = last update_id + 1 — this confirms and deletes the earlier updates.

Python
import requests

TOKEN = "5:RyNKXZs7Va1LvKqSLj6Snsoy2FplGb1n8FV"
API = f"https://minchatapp.com/bot{TOKEN}"
offset = 0

while True:
    r = requests.get(f"{API}/getUpdates", params={"offset": offset, "timeout": 30}, timeout=40).json()
    for upd in r["result"]:
        offset = upd["update_id"] + 1
        if "message" in upd:
            msg = upd["message"]
            if msg.get("text", "").startswith("/weather"):
                requests.post(f"{API}/sendMessage", json={
                    "chat_id": msg["chat"]["id"],
                    "text": "Pick a city:",
                    "reply_markup": {"inline_keyboard": [[
                        {"text": "Moscow", "callback_data": "city:moscow"},
                        {"text": "Berlin", "callback_data": "city:berlin"},
                    ]]},
                })
        elif "callback_query" in upd:
            cq = upd["callback_query"]
            requests.post(f"{API}/answerCallbackQuery", json={"callback_query_id": cq["id"], "text": "Loading…"})
            requests.post(f"{API}/editMessageText", json={
                "chat_id": cq["message"]["chat"]["id"],
                "message_id": cq["message"]["message_id"],
                "text": f"Weather for {cq['data'].split(':')[1]}: +5°C",
            })

Webhook#

setWebhook with an HTTPS URL switches the bot to push delivery: each update is POSTed as JSON. Answer 2xx quickly; failures are retried 3 times, 30 s apart, and reported by getWebhookInfo. Verify the X-Telegram-Bot-Api-Secret-Token header if you set secret_token. While a webhook is set getUpdates returns 409.

curl
curl -X POST "https://minchatapp.com/bot$BOT_TOKEN/setWebhook" \
  -H 'Content-Type: application/json' \
  -d '{"url":"https://example.com/minchat-webhook","secret_token":"s3cr3t-value"}' 

Example webhook body (message update):

json
{
  "update_id": 1,
  "message": {
    "message_id": 51,
    "date": 1792660008,
    "chat": {
      "id": 1,
      "type": "private",
      "first_name": "Anna",
      "username": "anna_s"
    },
    "from": {
      "id": 1,
      "is_bot": false,
      "first_name": "Anna",
      "last_name": "Smith",
      "username": "anna_s"
    },
    "text": "/weather Moscow"
  }
}

4. Inline keyboards and callbacks#

Attach reply_markup with inline_keyboard — rows of buttons. Each button has text (≤ 64 chars) and either callback_data (≤ 64 bytes) or url. Clients render the keyboard under the message; URL buttons open externally. A press sends a callback_query update; answer it with answerCallbackQuery and usually update the message with editMessageText / editMessageReplyMarkup (an empty inline_keyboard removes the keyboard). Edits are delivered to clients live (MessageUpdated).

sendMessage
{
  "chat_id": 1,
  "text": "Pick a city:",
  "reply_markup": {
    "inline_keyboard": [
      [ {"text": "Moscow", "callback_data": "city:moscow"}, {"text": "Berlin", "callback_data": "city:berlin"} ],
      [ {"text": "Open website", "url": "https://minchatapp.com"} ]
    ]
  }
}

callback_query update:

json
{
  "update_id": 3,
  "callback_query": {
    "id": "1",
    "from": {
      "id": 1,
      "is_bot": false,
      "first_name": "Anna",
      "last_name": "Smith",
      "username": "anna_s"
    },
    "chat_instance": "8",
    "data": "city:moscow",
    "message": {
      "message_id": 50,
      "date": 1792660008,
      "chat": {
        "id": 1,
        "type": "private",
        "first_name": "Anna",
        "username": "anna_s"
      },
      "from": {
        "id": 5,
        "is_bot": true,
        "first_name": "Weather",
        "last_name": "Bot",
        "username": "weather_bot",
        "can_join_groups": true,
        "can_read_all_group_messages": false,
        "supports_inline_queries": false
      },
      "text": "Pick a city:",
      "reply_markup": {
        "inline_keyboard": [
          [
            {
              "text": "Moscow",
              "callback_data": "city:moscow"
            },
            {
              "text": "Berlin",
              "callback_data": "city:berlin"
            }
          ],
          [
            {
              "text": "Open website",
              "url": "https://minchatapp.com"
            }
          ]
        ]
      }
    }
  }
}

5. Commands#

Register commands with setMyCommands; messages starting with / are delivered to bots in groups even in privacy mode. /cmd@your_bot addresses a specific bot.

bash
curl -X POST "https://minchatapp.com/bot$BOT_TOKEN/setMyCommands" \
  -H 'Content-Type: application/json' \
  -d '{"commands":[{"command":"start","description":"Start the bot"},{"command":"weather","description":"Current weather"}]}' 

6. Files#

Send media by multipart upload, by URL (the server downloads up to 50 MB) or by a file_id you received earlier. To download a file, call getFile and fetch /file/bot<token>/<file_path>.

bash
curl -F chat_id=1 -F caption='Forecast map' -F photo=@map.png \
  "https://minchatapp.com/bot$BOT_TOKEN/sendPhoto"

Limits#

WhatLimit
Requests120 per minute per route
Bots per owner20
Text / caption4096 / 1024
Buttonstext ≤ 64, callback_data ≤ 64 bytes
Media by URL50 MB
getUpdateslimit ≤ 100, timeout ≤ 50 s
Webhook delivery3 attempts, 10 s timeout, 30 s backoff
Unconfirmed updates stay in the queue until you confirm them with offset. The app does not yet show bots in a dedicated settings screen — manage them with @BotFather or the endpoints below.

Bot management (user API)#

These endpoints use the owner's Sanctum token, not the bot token.

GET /api/bots #

List my bots

Bearer token

Parameters

No parameters.

Request

curl
curl 'https://minchatapp.com/api/bots' \
  -H "Authorization: Bearer $TOKEN" \
  -H 'Accept: application/json'

Response

HTTP 200
{
  "success": true,
  "bots": [
    {
      "id": 5,
      "name": "Weather Bot",
      "username": "weather_bot",
      "avatar": null,
      "created_at": "2026-10-22T09:06:14+00:00"
    }
  ]
}
POST /api/bots #

Create a bot

Bearer token Rate limit: 10/min

username must end with bot. Up to 20 bots per owner. The token is returned once; a private chat owner ↔ bot is created.

Parameters

Name Type Description
name
required body
string
Display name.
max 64 chars
username
required body
string
Bot username (…bot).
max 32 chars

Request

curl
curl -X POST 'https://minchatapp.com/api/bots' \
  -H "Authorization: Bearer $TOKEN" \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{"name": "Weather Bot", "username": "weather_bot"}'

Response

HTTP 201
{
  "success": true,
  "bot": {
    "id": 5,
    "name": "Weather Bot",
    "username": "weather_bot",
    "avatar": null
  },
  "token": "5:AAEhBP0av28Jc9RIwmZ8P-exampleToken"
}
POST /api/bots/{bot}/token #

Regenerate the bot token

Bearer token Rate limit: 10/min

The old token stops working immediately.

Parameters

Name Type Description
bot
required path
integer
Bot user id.

Request

curl
curl -X POST 'https://minchatapp.com/api/bots/5/token' \
  -H "Authorization: Bearer $TOKEN" \
  -H 'Accept: application/json'

Response

HTTP 200
{
  "success": true,
  "token": "5:AAEhBP0av28Jc9RIwmZ8P-exampleToken"
}
DELETE /api/bots/{bot} #

Delete a bot

Bearer token

Revokes the token and deletes the bot account.

Parameters

Name Type Description
bot
required path
integer
Bot user id.

Request

curl
curl -X DELETE 'https://minchatapp.com/api/bots/5' \
  -H "Authorization: Bearer $TOKEN" \
  -H 'Accept: application/json'

Response

Responds with HTTP 200.

Related: GET /api/username-available?kind=bot checks a bot username; POST /api/messages/{id}/callback is what clients call when a user presses a button.