Telegram Studio

Visual Telegram MarkdownV2 & HTML markup editor, message bubble simulator, TMA InitData decoder, 64-byte callback checker, bot command & deep-link builder, and multi-language payload generator. All processing runs 100% locally in your browser.

Parse Mode:
Allowed tags: b/strong, i/em, u, s/strike/del, code, pre, a[href], span[class=tg-spoiler]. Anything else is stripped in preview and by Telegram.
Message length after formatting is parsed: {{ messageTextLength }} / {{ MESSAGE_TEXT_LIMIT }} characters — sendMessage/editMessageText will reject this.

Unescaped Character Diagnostics ({{ escapeDiagnostics.length }} flagged)

Highlighted characters are MarkdownV2 reserved characters not preceded by \. Some may be intentional formatting (e.g. * around bold text) — this is a raw diagnostic, not an entity-aware parser. Use Auto-Escape Text to escape everything literally.

Paste Telegram Mini App initData string

This inspector decodes the launch parameters so you can read what Telegram sent. It deliberately does not check the signature — verifying initData needs your bot token, and keeping one verified implementation instead of two means there is only one place for the algorithm to be wrong. For HMAC-SHA256 verification, signing, and the step-by-step algorithm trace, use Telegram Webhook Tester.

reply_markup type:

Inline Keyboard Builder (64 UTF-8 Byte Checker)

Row #{{ rIdx + 1 }}
Button #{{ bIdx + 1 }}
UTF-8 Bytes: {{ getByteLength(btn.value) }} / 64
⚠ callback_data is empty — Telegram rejects empty callback_data.
⚠ Exceeds Telegram's 64-byte callback_data limit.
⚠ Button text is empty.

Reply Keyboard Builder

Row #{{ rIdx + 1 }}
Button #{{ bIdx + 1 }}
⚠ Web App button needs a URL.
⚠ Button text is empty.
input_field_placeholder:

Remove Keyboard

No other fields — remove_keyboard is a fixed instruction telling clients to hide whatever custom reply keyboard is currently shown.

Force Reply

input_field_placeholder:

⚠ The method currently selected on the Payload Code Exporter tab ({{ apiMethod }}) only accepts an inline keyboard — its reply_markup type there is InlineKeyboardMarkup only, per the Bot API. Switch back to Inline Keyboard above, or pick a different method, for this selection to take effect in that payload.

Bot Commands

Preset:
Scope:
chat_id:
user_id:
language_code: Telegram falls back to the empty-language list when a user's language has none.
{{ issue }}

Order is preserved exactly as sent — Telegram shows the menu in this order, not alphabetically.

Deep Links

Bot username:
Link type:
Mini App short_name:
Payload as: Encoding:
{{ payloadBytesLabel }}
{{ issue.text }}

Decode a link

Paste any of the four shapes above — the components and the decoded payload appear on the right.

Method & Format

Method:
chat_id:
message_id:
callback_query_id:
Answer text:
{{ callbackAnswerText.length }} / {{ CALLBACK_ANSWER_LIMIT }} characters
Message text ({{ messageTextLength }} / {{ MESSAGE_TEXT_LIMIT }} chars) is edited on the Markup & Preview tab.
{{ apiMethod==='sendPhoto' ? 'Photo URL:' : 'Document URL:' }}

Telegram limits: photo ≤ 10 MB, width + height ≤ 10000px, aspect ratio ≤ 20:1. Not checked here — this field only takes a URL, not the file itself.

Telegram limit: file ≤ 50 MB. Not checked here — this field only takes a URL, not the file itself.

Caption:
{{ caption.length }} / {{ CAPTION_LIMIT }} characters (raw — formatting markup in a MarkdownV2/HTML caption is not subtracted here)

reply_markup: {{ keyboardMode }} (built on the Inline Keyboard & Bytes tab) — ignored: {{apiMethod}} only accepts an inline keyboard.

Format:

Paste a JSON payload shaped for the {{ apiMethod }} method selected above — its fields (chat_id, text, caption, reply_markup, etc.) populate the editor, including the keyboard builder on the Inline Keyboard & Bytes tab.

{{ payloadImportError }}

{{ payloadImportSummary }}

Message Sequence

Queue up the payload above as one step, then repeat for the next message in the flow — export runs them in order with a delay between calls.

{{ sIdx + 1 }}. {{ step.method }}
Delay between messages: ms
Escaped Output (Safe for {{parseMode}})
{{ escapedOutput }}
Telegram Chat Live Bubble Preview
AlexRel Bot
{{ currentTime }}
{{ btn.text || 'Button' }}
⌨️ Custom keyboard (shown at the chat input, not attached to this message)
{{ btn.text || 'Option' }} {{ btn.request === 'contact' ? '📞' : btn.request === 'location' ? '📍' : btn.request === 'poll' ? '📊' : '🌐' }}
⌨️ Custom keyboard removed — the default letter-keyboard is shown instead.
↩️ Reply interface forced — input field shows "{{ forceReplyPlaceholder || 'Reply to ' + 'AlexRel Bot' }}"
Parsed Key-Value Claims
KeyParsed Value
{{ item.key }}
{{ item.value }}
{{ item.value }}
No valid initData parsed yet. Enter a query string on the left.
Live reply_markup JSON
{{ JSON.stringify(buildReplyMarkupJson(), null, 2) || '(no buttons yet)' }}
Warnings
  • {{ issue }}
How to apply this command list {{ botFatherPossible ? 'BotFather or API' : 'API only' }}
{{ botFatherSteps }}
Command list
{{ commandExport || '(add commands on the left)' }}
Deep links
{{ deepLinksText }}
{{ primaryLink }}

{{ qrError }}

Decoded link

Paste a deep link on the left to take it apart.

{{ decodedLink.error }}

{{ row.key }}{{ row.value }}
Payload Exporter
{{ generatedCodeSnippet }}
Sequence Exporter ({{ messageSequence.length }} step{{ messageSequence.length === 1 ? '' : 's' }})
{{ sequenceSnippet }}
About Telegram Studio

Telegram Studio covers the outbound half of Telegram development — everything you compose and send. Escape MarkdownV2 & HTML special characters with an XSS-safe allowlist-sanitized preview, decode TMA initData fields, inspect 64-byte callback limits, build BotFather command lists and start/startapp deep links, and generate production-ready, method-aware code snippets.

Keyboards. The Bot API's reply_markup field accepts exactly four shapes, and this tab builds all of them: an inline keyboard attached to one message, a persistent reply keyboard shown at the chat's input area (with per-button contact/location/poll/Web-App requests), a remove-keyboard instruction, and a force-reply instruction. Only two of the seven exportable methods — editMessageText and editMessageReplyMarkup — accept an inline keyboard here; the other three shapes only make sense on a fresh message, and the tab tells you when your current selection would be silently ignored rather than sending an invalid payload.

Payloads. The Payload Code Exporter is two-way: paste a JSON payload for the selected method and "Load into editor" fills in every field it recognises, including reconstructing whichever of the four keyboard shapes its reply_markup carries. Queue several payloads into a message sequence to export a delayed, sequential script (cURL, aiogram, pyTelegramBotAPI, PHP or JS) instead of one call at a time. Text (1–4096 chars after entities parsing), caption (0–1024 chars) and callback-answer text (0–200 chars) are checked against Telegram's own documented limits.

Command scopes. A bot does not have one command list, it has one list per (scope, language_code) pair, and Telegram resolves the most specific scope that matches before falling back to the empty-language list. The catch nobody documents in the same place: BotFather's /setcommands prompt cannot set either of them. It always writes the default, all-languages list. Anything narrower — a groups-only list, an admins-only list, a Russian list — has to go through the setMyCommands API, which is why this tab tells you which of the two routes your current settings actually allow.

Deep links. ?start= opens the bot chat with a payload, ?startapp= launches the Mini App, https://t.me/<bot>/<app>?startapp= launches a specific named Mini App, and ?startattach= opens the attachment menu. All of them share one hard rule: the payload may be at most 64 characters from A–Z a–z 0–9 _ - only. That character set is exactly base64url, and exactly not standard base64 — which emits +, / and = and produces a link Telegram silently refuses to carry. Encoding is base64url here by default for that reason, and the builder shows the encoded length because base64 inflates a payload by roughly a third, so 64 characters of link is closer to 47 bytes of data.

Companion tools: for the inbound half — decoding webhook Update payloads, replaying them to your endpoint, and cryptographically verifying initData or Login Widget callbacks — use Telegram Webhook Tester. To charge for any of it, build the createInvoiceLink payload with Telegram Stars Invoice.

Privacy: 100% Client-side. All processing happens in your browser. Bot tokens, keys, and message contents are never transmitted to external servers.
{{ toast.msg }}