Step-by-Step: Create and Edit Telegram Bot Commands

Prerequisites: Token, Privacy Mode, and First Contact
Before any command appears in a chat you must (1) own a bot token from @BotFather, (2) decide whether the bot is private (privacy mode on) or group-aware (privacy mode off), and (3) choose where commands should render: private chat menu, group submenu, or both. Telegram does not charge for API calls, but each token is rate-limited to 30 messages/second global, 1 message/second per chat—design commands accordingly.
Step 1 – Create the Bot and Capture the Token
Open Telegram → Search
@BotFather→ Start.Send
/newbot→ choose display name (public) and username (must end in bot).Copy the HTTP API token; it is shown once and is unrecoverable if lost.
(Optional) Set inline placeholder, description, about text, and profile picture while here—BotFather groups these under
/mybots→ Edit Bot.
Mobile vs Desktop Caveats
iOS/Android: long-press the token message → Copy. Swipe left on the chat to archive it; BotFather will keep the token visible.
Desktop: right-click the token → Copy Link; the link contains the token—strip
https://t.me/prefix before use.
Step 2 – Declare Global Commands via BotFather (Beginner Path)
The fastest way to publish commands is still the interactive wizard.
Return to
@BotFather→/mybots→ select your bot → Edit Bot → Edit Commands.Type commands in plain text: each line is
command - Description. Example:start - Start the bot and see options help - Show available commands poll - Create a quick poll settings - Change language or notificationsSend the list. BotFather replies ✅ Commands updated.
Open any chat with your bot; the “/” icon now surfaces the menu.
Limitations: This method only writes global (all-private-chats) scope; you cannot target groups or languages. For scoped or localized menus, jump to the JSON method below.
Step 3 – Advanced: Upload a JSON Command List with Scopes
Bot API 6.9+ accepts setMyCommands with optional scope and language_code fields, letting you ship different menus for private chats, group admins, or specific languages.
JSON Schema
Key | Type | Limits |
|---|---|---|
commands | Array | 1-100 items |
command | String | 1-32 lowercase ASCII chars, / reserved |
description | String | 3-256 UTF-8 chars |
Example: Multi-Language Private + Group Admin
// english global
[
{"command":"start", "description":"Launch bot"},
{"command":"help", "description":"Get help"}
]
// chinese global
[
{"command":"start", "description":"启动机器人"},
{"command":"help", "description":"查看帮助"}
]
// group admin only (any language)
[
{"command":"ban", "description":"Ban a user"},
{"command":"stats", "description":"Group stats"}
]cURL One-Liner
Replace $TOKEN and save as commands.json:
curl -X POST "https://api.telegram.org/bot$TOKEN/setMyCommands" \
-H "Content-Type: application/json" \
-d @commands.jsonTip: Use jq to validate JSON before hitting the API: jq . commands.json
Step 4 – Test, Debug, and Refresh Cache
Private chat: type
/; the menu should surface instantly.Group chat: promote the bot to admin, then type
/; scoped commands append under a “@your_bot” submenu.If changes do not appear: kill the app (swipe up) and reopen; Telegram caches menus for ~5 min.
Still stale? Call
deleteMyCommandsfirst, then re-upload; cache is invalidated immediately.
Editing Commands Live without Downtime
Production bots often need synonym rotation (e.g., seasonal campaigns). Instead of patching JSON manually, embed an admin-only /reload command in your codebase that reads a local commands.yml, converts to JSON, and POSTs setMyCommands. Because the API call is atomic, users see zero menu flicker.
Python Skeleton (aiogram 3.x)
@dp.message(Command('reload'), AdminFilter())
async def reload_commands(msg: Message):
await bot.set_my_commands(build_commands(), scope=BotCommandScopeDefault())
await msg.reply("✅ Menu refreshed globally")Common Error Codes and Quick Fixes
Code | Meaning | Fix |
|---|---|---|
400 BAD_REQUEST | command too long or caps | 32 lowercase ASCII max |
401 UNAUTHORIZED | token typo or revoked | Regenerate in @BotFather |
415 UNSUPPORTED | wrong Content-Type | application/json only |
Pro Tips for High-Quality Menus
Lead with verbs: setlang, report, track; avoid generic options.
Keep descriptions ≤22 characters so mobile fits without truncation.
Group related commands alphabetically; Telegram shows them in the order uploaded.
Use emoji sparingly (≤1 per description) to avoid Android fallback rectangles.
Provide logical inverse pairs: mute / unmute so users can guess.
Security Checklist
Never commit the token to Git; load via env var.
Limit
/reloadto a whitelist of Telegram user IDs.Validate incoming commands server-side; clients can send arbitrary text after
/.Rotate tokens periodically via
/revokein BotFather if source leaks.
Going Further: Deep-Linking, Mini App Buttons, and In-Chat Web Apps
Commands can include URL-encoded payloads: https://t.me/yourbot?start=payload. Parse /start payload to open a Mini App automatically with web_app keyboard button. Because menus are cached, pair a visible start command with a hidden launch command that only appears via deep-link scope—this keeps the UI clean while allowing marketing campaigns to trigger richer UIs.
Recap
Creating and editing Telegram bot commands is a three-stage pipeline: declare, scope, and cache-bust. Use BotFather for quick global menus; switch to JSON setMyCommands when you need languages, groups, or admin isolation. Test aggressively across mobile and desktop, automate reloads in production, and keep descriptions short, imperative, and emoji-consistent. Follow the security checklist to avoid token leaks and rogue reloaders. With these practices your bot menu stays fast, contextual, and maintenance-free for millions of Telegram users.


