Rich Messages ============= *Bot API 10.1 — June 2026, media added in 10.2* A rich message is a document sent as a message. Where an ordinary message is a line of text with entities on top, a rich message has structure: headings, lists, tables, pull quotes, code blocks, collapsible sections, collages, maps and captions — the vocabulary of an Instant View article, composed and sent from your own code. Rich messages are sent by **bots**. ----- Three ways to write one ----------------------- :obj:`~pyrogram.types.InputRichMessage` takes exactly one of ``html``, ``markdown`` or ``blocks``. They map to three different constructors on the wire and they carry media differently, which is the one thing worth understanding before you start. .. code-block:: python from pyrogram.types import InputRichMessage await app.send_rich_message( chat_id="me", rich_text=InputRichMessage( html="
Layer 228 is live.
", ), ) ``markdown`` is the same thing in the other syntax. ``blocks`` is the structured form: .. code-block:: python from pyrogram.types import ( InputRichMessage, InputRichBlockSectionHeading, InputRichBlockParagraph, InputRichBlockList, InputRichBlockListItem, InputRichBlockPreformatted, ) await app.send_rich_message( chat_id="me", rich_text=InputRichMessage(blocks=[ InputRichBlockSectionHeading(text="Release notes", size=2), InputRichBlockParagraph(text="Layer 228 is live."), InputRichBlockList( items=[ InputRichBlockListItem(text="Ephemeral messages"), InputRichBlockListItem(text="Communities"), InputRichBlockListItem(text="Rich message media", has_checkbox=True, is_checked=True), ], ordered=False, ), InputRichBlockPreformatted(text="pip install -U wzgram", language="bash"), ]), ) Passing more than one of the three is not an error: the first set one in the order ``html``, ``markdown``, ``blocks`` wins and the rest are ignored. Setting none raises ``ValueError`` when the message is sent. The block vocabulary -------------------- Every block is a class under :obj:`~pyrogram.types.InputRichBlock`: ============================================ =========================================== Block What it is ============================================ =========================================== ``InputRichBlockParagraph`` a paragraph of text ``InputRichBlockSectionHeading`` a heading, ``size`` 1-6 ``InputRichBlockPreformatted`` a code block with a ``language`` ``InputRichBlockList`` ordered or bulleted, items may have checkboxes ``InputRichBlockBlockQuotation`` a quote wrapping other blocks ``InputRichBlockPullQuotation`` a pull quote with a ``credit`` ``InputRichBlockTable`` rows of ``InputRichBlockTableCell`` ``InputRichBlockDetails`` a collapsible section ``InputRichBlockCollage`` / ``…Slideshow`` grouped media ``InputRichBlockPhoto`` / ``…Video`` a single photo or video, with spoiler and autoplay flags ``InputRichBlockAudio`` / ``…VoiceNote`` an audio file or a voice note ``InputRichBlockAnimation`` a looping video ``InputRichBlockMap`` a map at a geo point and zoom ``InputRichBlockMathematicalExpression`` a formula ``InputRichBlockAnchor`` a named target to link to ``InputRichBlockDivider`` a horizontal rule ``InputRichBlockFooter`` trailing small print ``InputRichBlockThinking`` a model's reasoning, rendered as such ============================================ =========================================== Attaching media --------------- Media in a rich message must **already exist on Telegram**. You pass a file identifier, an ``InputPhoto`` or an ``InputDocument`` — never a local path. Nothing here uploads. How you attach it depends on which of the three forms you used, and :obj:`~pyrogram.types.InputRichMessageMedia` covers both shapes: **html and markdown** — each media entry needs an ``id`` of your choosing, and the text refers to it with a ``tg://`` link: .. code-block:: python from pyrogram.types import InputRichMessage, InputRichMessageMedia await app.send_rich_message( chat_id="me", rich_text=InputRichMessage( html='Here it is: