# Formatting: PostFrog Rich Markdown

`exact` posts are written in **PostFrog Rich Markdown**: GitHub Flavored Markdown plus a short list of HTML tags, rendered by Telegram as a rich message (Bot API rich messages). The same markup comes back in `get_batch` (`post.text`) for every mode. There is also a simpler **Markdown mode** (`format: "markdown"`), described at the end.

Only what is listed here renders. Do not write Telegram HTML (`<a href>`, `<blockquote expandable>`), MarkdownV2 escapes or entities: PostFrog converts the markup itself.

## Rich Markdown reference (`format: "rich"`, the default)

| What | How |
|---|---|
| Title | `# Title` - one per post, on the first line (optional for short posts) |
| Sections | `## Section`, `### Subsection` |
| Bold, italic, strikethrough | `**bold**`, `*italic*`, `~~strike~~` |
| Highlight | `==highlighted==` |
| Spoiler | `||hidden until tapped||` |
| Underline | `<u>underlined</u>` |
| Sub and superscript | `H<sub>2</sub>O`, `x<sup>2</sup>` |
| Inline code, code block | `` `code` ``, a fenced block with a language: ```` ```python ```` … ```` ``` ```` |
| Link | `[text](https://example.com)` |
| Lists | `- item`, `1. item`, checkbox `- [ ] item` |
| Table | GFM table, alignment with `|:---|---:|` |
| Quote | `> quoted text` |
| Pull quote with author | `<aside>Quote text<cite>Author</cite></aside>` (no markup inside) |
| Divider | `---` on its own line, between big sections |
| Collapsible block | `<details><summary>Title</summary>Hidden text</details>` |
| Footnote | `text[^1]` and at the end `[^1]: note` |
| Formula | `$inline$` or `$$block$$` |
| Photo or video | a line `%%MEDIA:0%%` (see `media`) |
| Collage | a line `%%MEDIA:0,1,2%%` - three or more media in one line |
| Buttons in the text | `<tg-button …>` and `<tg-button-row>` (see `buttons`) |
| Author's custom emoji | `🔥{ce:ID}`: a Unicode emoji followed by `{ce:ID}`. Only IDs the author gave you; otherwise plain Unicode emoji |

Also accepted as plain HTML: `<b>`, `<i>`, `<s>`, `<code>`, `<pre>`, `<br>`, `<blockquote>`. Prefer the Markdown forms above. Any other tag produces the warning `unknown_tag` and does not render.

### Rules that matter

1. **Blank line between paragraphs.** A single line break inside a paragraph becomes a space, so lines without a blank line between them merge into one paragraph. Lists, quotes and tables keep their own line breaks.
2. **Markdown is not parsed inside block HTML tags** except `<details>` (and media collages). Inside `<aside>`, `<blockquote>` or a `<tg-button-row>` write plain text.
3. Put block elements (`%%MEDIA%%` lines, button rows, `<details>`, `<aside>`, tables, `---`) on their own lines with blank lines around them.
4. **Length:** an `exact` Rich post may have up to **31,268 characters including markup**. Telegram's own limit is 32,768; PostFrog keeps the rest for media links and its footer. Longer is the error `too_long`.
5. Avoid the em dash and the en dash: they trigger the warning `long_dash` (PostFrog's house style). Use a hyphen with spaces, a colon or a comma.
6. Links you put in the post are kept as they are. A bare URL works, but a short phrase link `[Sign up](https://…)` or a button reads better.
7. No Telegram MarkdownV2 escaping: write `Price: 5.99!` and `well-known`, not `5\.99\!` or `well\-known`. Normal Markdown rules still apply (`*word*` is italic).

### A good Rich post

- A short title (`#`), paragraphs of 2-4 lines, `##` subheadings only in long posts.
- **Bold** for the key phrase or number of a paragraph (at most one per paragraph), ==highlight== for one or two main phrases of the whole post.
- Three or more items: a list. Comparisons and numbers: a table. The closing thought: a `>` quote or bold.
- Long optional parts (specs, sources, a FAQ) go into `<details>`.
- Markup should serve the text: keep it under roughly a third of the post.
- Buttons only where there is a reason: a real link, a promo code, a call to subscribe (see `buttons`).

## Markdown mode (`format: "markdown"`)

For authors whose channel uses classic Telegram formatting. Write **plain Markdown**; PostFrog converts it to Telegram MarkdownV2 and escapes it itself.

- Works: `**bold**`, `*italic*`, `__underline__`, `~~strike~~`, `||spoiler||`, `` `code` ``, ```` ``` ```` blocks, `> quote`, `[text](url)`, lists as lines starting with `•` or `1.`.
- Not available: headings (use a bold line as the title), tables (warning `markdown_table`), `<details>`, formulas, any HTML tag (warning `markdown_html_tag`).
- **Buttons:** rows of buttons go under the post as a keyboard (warning `buttons_below_post`); buttons inside a line become a link or `code` (warning `inline_buttons_as_links`).
- **Media** always sits on top of the post (as a caption post or an album); `%%MEDIA:n%%` lines are ignored (warning `media_placeholder_ignored`).
- **Length:** 4,096 characters, and only 1,024 when the post has media (it becomes a caption). Measured **after** conversion to MarkdownV2, which adds escape characters: leave some headroom. Over the limit is the error `too_long`; shorten it or switch to Rich.

Use Markdown mode only when the author's default is `markdown` (`defaults.format` in `get_account`) or the author asked for it. Otherwise use Rich.

## Cheat sheet: how to…

| Task | Rich Markdown |
|---|---|
| Title + lead | `# Title` blank line `Lead paragraph` |
| Photo as the cover | first line `%%MEDIA:0%%`, or no placeholder at all (media without a placeholder goes on top) |
| Photo after the second paragraph | a `%%MEDIA:1%%` line between the paragraphs, blank lines around |
| Three photos as a collage | a line `%%MEDIA:0,1,2%%` |
| Button to a site at the end | `buttons: [[{"text": "Open the site", "url": "https://…"}]]` |
| Button inside a sentence | `Sign up today <tg-button type="url" url="https://…">Sign up</tg-button>` |
| Promo code you can copy | `<tg-button type="copy_text" text="FROG20">Copy the promo code</tg-button>` |
| Hidden answer | `||the answer||` |
| Long details folded | `<details><summary>Specs</summary>…</details>` |
| Source line at the end | `Source: [Example News](https://…)` |
| Quote with author | `<aside>Ship it.<cite>Jane Doe</cite></aside>` |
| Checklist | `- [ ] Buy clay` per line |