Telegram bot commands: BotFather, setMyCommands and webhook

The commands users see after typing / in a chat with the bot are set in @BotFather. Send it /setcommands, pick the bot and send the list, one command per line, without the slash:

start - Start the bot
help - What the bot can do
settings - Settings

Rules from the Bot API:

  • a command is 1⁠–⁠32 characters: lowercase English letters, digits and underscores;
  • a description is 1⁠–⁠256 characters;
  • a list holds up to 100 commands.

Telegram asks every bot to support /start, /help and, if the bot has settings, /settings. The first time a user opens a chat with the bot, they see a Start button.

The menu is only a hint. A message can contain any command, including ones you didn't list, and the bot isn't told which list the user saw. Check commands and permissions on your server. In a group with several bots, add the bot's username to the command, /help@my_bot, so it reaches the right one.

Set commands with the API: setMyCommands

TOKEN="123456789:AA..."

curl -s "https://api.telegram.org/bot$TOKEN/setMyCommands" \
  -H 'Content-Type: application/json' \
  -d '{"commands": [
        {"command": "start", "description": "Start the bot"},
        {"command": "help", "description": "What the bot can do"}
      ]}'

The response is {"ok":true,"result":true}. getMyCommands returns the current list, deleteMyCommands deletes it.

language_code and scope give different users different lists:

# The menu for users whose Telegram is in Russian
curl -s "https://api.telegram.org/bot$TOKEN/setMyCommands" \
  -H 'Content-Type: application/json' \
  -d '{"language_code": "ru", "commands": [
        {"command": "start", "description": "Запустить бота"},
        {"command": "help", "description": "Что умеет бот"}
      ]}'

# The menu for administrators of every group: repeat start and help
curl -s "https://api.telegram.org/bot$TOKEN/setMyCommands" \
  -H 'Content-Type: application/json' \
  -d '{"scope": {"type": "all_chat_administrators"}, "commands": [
        {"command": "start", "description": "Start the bot"},
        {"command": "help", "description": "What the bot can do"},
        {"command": "ban", "description": "Ban a user"}
      ]}'

Scope types: default, all_private_chats, all_group_chats, all_chat_administrators, chat, chat_administrators, chat_member. Each user sees one list, not a merge: a list for a scope or a language replaces the whole menu. Telegram picks the list for the narrowest scope that has one, first in the user's language, then without a language, and falls back to default. Pass the same scope and language_code to getMyCommands and deleteMyCommands.

BotFather commands

Command What it does
/newbot create a bot and get its token
/mybots your bots, with buttons to edit each one
/setname change the bot's name
/setdescription the "What can this bot do?" text, up to 512 characters
/setabouttext the text on the bot's profile, up to 120 characters
/setuserpic the profile picture
/setcommands the command menu
/setjoingroups allow or forbid adding the bot to groups
/setprivacy privacy mode: which group messages the bot receives
/setinline inline mode
/token a new token, if the old one leaked
/deletebot delete the bot; there's no undo

BotFather adds commands over time. Type / in the chat with it to see the current list.

Bot API URLs: token and webhook

Every method lives at https://api.telegram.org/bot<token>/METHOD, over GET or POST:

# Check the token: returns the bot's id, name and username
curl -s "https://api.telegram.org/bot$TOKEN/getMe"

# Set a webhook: Telegram sends updates to this HTTPS URL
curl -s "https://api.telegram.org/bot$TOKEN/setWebhook" \
  -d url=https://example.com/telegram/webhook \
  -d secret_token=my-secret-123

# Webhook status: url, pending_update_count, last_error_message
curl -s "https://api.telegram.org/bot$TOKEN/getWebhookInfo"

# Remove the webhook and drop the queued updates
curl -s "https://api.telegram.org/bot$TOKEN/deleteWebhook" \
  -d drop_pending_updates=true
  • A webhook works on ports 443, 80, 88 and 8443, always over HTTPS, port 80 included.
  • With secret_token, every request from Telegram carries the header X-Telegram-Bot-Api-Secret-Token with that value. Compare it to drop requests that didn't come from Telegram.
  • If your server answers with anything other than 2XX, Telegram retries the update and gives up after several attempts.
  • getUpdates doesn't work while a webhook is set: Conflict: can't use getUpdates method while webhook is active; use deleteWebhook to delete the webhook first. To switch to polling, call deleteWebhook.

The token in the URL ends up in shell history and server logs. If it leaks, get a new one with /token in BotFather.

I do product consulting, build MVPs and teach people to build products themselves. Work with me →