Синтаксис markdown
Тело страницы после frontmatter конвертируется в блоки Гутенберга построчно. Конвертер намеренно простой — он байт в байт совпадает с конвертером первой версии API, чтобы повторная публикация не давала пустого диффа. Поэтому ниже не «markdown вообще», а ровно то, что он понимает.
Абзацы и переносы
Абзац — строки подряд до пустой строки. Каждый перевод строки внутри абзаца становится <br>, а не пробелом. Пишите абзац одной строкой; переносите только там, где нужен настоящий разрыв.
Первый абзац одной строкой.
Второй абзац.
Эта строка уйдёт после <br>.
Выделение, код и ссылки
| Пишете | Получаете |
|---|---|
**жирный** или __жирный__ | <strong> |
*курсив* или _курсив_ | <em> |
| обратные кавычки вокруг текста | <code>, содержимое экранируется |
[текст](/docs/v2/ru/) | ссылка; адрес любой — путь или полный URL |
Звёздочка и подчёркивание ищут пару до конца строки, поэтому идентификаторы вида pages.*, menu.* или _onpress_frame в обычном тексте превратятся в курсив и потеряют символы. Любой код, имя поля или путь заключайте в обратные кавычки — внутри них разметка не работает. Экранирования обратным слешем нет. Автоссылок нет: адрес без […](…) останется текстом. HTML внутри абзаца проходит как есть, без экранирования.
Заголовки
# … ###### и пробел. Уровень сохраняется. При сборке страницы у каждого заголовка H2–H6 появляется якорь id — слаг его текста (## Как это работает → #kak-eto-rabotaet), при повторе с числовым хвостом (-1, -2). На эти якоря ведёт оглавление onpress/outline, и на них можно ссылаться: [см. ниже](#kak-eto-rabotaet).
Нужен ли # Заголовок в теле, зависит от фрейма: если фрейм выводит название страницы сам (биндом page.title), H1 в теле даст второй H1 на странице. Посмотрите разметку фрейма (GET /frames/{slug}) и пишите H1 только туда, где фрейм его не выводит.
Списки
- пункт
- ещё пункт
1. первый
2. второй
Маркеры — - или * и пробел, нумерованные — цифры, точка и пробел; номера не сохраняются, нумерация всегда с единицы. Вложенных списков нет: строка с отступом перед маркером перестаёт быть пунктом и приклеивается к предыдущему. Нужна вложенность — таблица или блок core/html.
Осторожно с абзацем, который начинается с числа и точки: 2026. Год, когда… станет нумерованным списком.
Цитаты
> Первая строка цитаты
> вторая строка
Строки подряд с > — одна цитата, строки соединяются через <br>.
Таблицы
| Поле | Тип | Описание |
|---|---|---|
| `name` | string | заголовок |
| `slug` | string | слаг |
Первая строка — шапка. Строка из дефисов и двоеточий после неё — разделитель, её можно опустить. Выравнивание по двоеточиям не применяется. Вертикальная черта внутри ячейки невозможна даже в обратных кавычках: она всегда делит ячейки. Пишите «или», запятую или |.
Код
Блок кода открывает строка из трёх обратных кавычек, сразу за которыми идёт имя языка (bash, json, html, markdown, php…), и закрывает строка из трёх обратных кавычек без ничего. Пример ниже — блок с языком bash:
curl -s "$API/health"
Язык становится классом language-bash у тега code — по нему работает подсветка. Допустимы латиница, цифры, +, # и -, до 20 символов. Содержимое экранируется целиком, поэтому внутри кода можно писать любую разметку, в том числе <!-- onpress/… -->, — она останется текстом. Вложить один блок кода в другой нельзя: первая же строка из трёх обратных кавычек закроет внешний блок.
Разделитель
Строка из трёх и более -, * или _ — горизонтальная линия.
Картинки

Картинка должна стоять одна на строке:  и текст станет абзацем. Текст в квадратных скобках — alt. Текст в кавычках после адреса — подпись (figcaption), в ней работает выделение и ссылки. Пустой адрес — картинка выбрасывается.
Адрес берите из поля path ответа загрузки медиа: путь от корня сайта работает на любом домене сайта и переживает смену домена. Картинка из медиатеки сайта при сборке превращается в <picture> с размерами и WebP-копиями; чужой адрес выводится как есть.
Сырые блоки Гутенберга
Строка, начинающаяся с <!-- wp:, — начало блока; конвертер забирает его целиком до парного закрытия и кладёт в тело байт в байт. Так вставляется всё, чего нет в markdown:
<!-- wp:html -->
<div class="promo"><a class="btn" href="/tarify/">Выбрать тариф</a></div>
<!-- /wp:html -->
<!-- wp:paragraph {"className":"lead"} -->
<p class="lead">Абзац с классом.</p>
<!-- /wp:paragraph -->
Блоки плагина onpress-blocks — <!-- wp:onpress/… --> (кнопки, карточки, шаги) — тоже пишутся так. Не путайте их со вставками: wp:onpress/… — блок плагина, onpress/… без wp: — вставка (см. синтаксис вставок). Можно передать их и отдельно — полем blocks в теле запроса, они встанут после текста. ACF-блоки v2 не пишет (501 acf_block_unsupported).
Вставки onpress/ в теле страницы
Строки <!-- onpress/… --> проходят в тело так же, как блоки Гутенберга, и выполняются при сборке. В теле страницы полезны:
Фрейм внутри текста — общий блок, который правится в одном месте; после его правки кэш страницы сбрасывается сам:
<!-- onpress/frame {"name":"article__cta"} /-->
Фрейм с параметрами и слотом — вызов передаёт фрейму атрибуты (frame.<ключ> внутри) и markdown между открытием и закрытием. Содержимое слота остаётся в теле как есть: выгрузка и повторная заливка его не меняют, а перед вызовом выгрузка ставит пустую строку после заголовка. Подробно — параметры и слот.
## FAQ
<!-- onpress/frame {"name":"faq"} -->
### Сколько стоит?
Бесплатно на старте.
<!-- /onpress/frame -->
Значение — onpress/value выводит один токен без тега. В теле страницы ставьте его только отдельной строкой: внутри строки абзаца выгрузка в markdown его теряет.
<!-- onpress/value {"bind":"page.data.rating"} /-->
Вопросы и ответы прямо в тексте — аккордеон и разметка FAQPage из обычного markdown:
## Частые вопросы
<!-- onpress/faq -->
### Сколько стоит?
Бесплатно на старте.
### Нужна ли карта?
Нет.
<!-- /onpress/faq -->
Врезка. Внутри onpress/callout текст не конвертируется из markdown — пишите HTML или блоки Гутенберга:
<!-- onpress/callout {"variant":"warning","title":"Внимание"} -->
<p>Перевыпуск токена <strong>сразу</strong> отключает старый.</p>
<!-- /onpress/callout -->
Список из данных — повторитель по набору или источнику прямо в тексте:
<!-- wp:html -->
<ul class="team">
<!-- onpress/repeat {"source":"team"} -->
<li><strong data-op-bind='{"text":"item.name"}'></strong> — <span data-op-bind='{"text":"item.role"}'></span></li>
<!-- /onpress/repeat -->
</ul>
<!-- /wp:html -->
Все вставки и их атрибуты — в справочнике вставок. Вставки, которые выводят части оболочки (onpress/docs-nav, onpress/outline, onpress/pager, onpress/share-ai), место имеют во фрейме, а не в тексте.
Одна тонкость: пустая строка разрывает сырой блок только снаружи. Внутри парной вставки или блока пустые строки допустимы — конвертер собирает всё до закрывающего комментария с тем же именем.
Обратная выгрузка
GET /pages/{ref}/markdown превращает блоки обратно в markdown тем же конвертером: абзацы, заголовки, списки, таблицы, код, картинки с подписями () возвращаются в markdown, а сырые блоки Гутенберга и вставки onpress/ — байт в байт как были. Поэтому публиковать можно и выгруженный файл, не боясь потерь.
Как проверить результат
Ответ публикации несёт поле blocks — разобранные блоки, в которые превратилось тело. Непонятно, что получилось, — опубликуйте черновиком (status: draft) и посмотрите blocks и preview_url.