Фреймы
Эндпоинты фреймов с параметрами, ответами и кодами отказов; что такое фрейм и как он устроен, написано на странице «Фреймы», фреймы для markdown страницы — на странице «Фреймы в markdown», готовые фреймы с параметрами — на странице «Фреймы с данными».
Во всех примерах API — https://onpress.pro/api/v2, SITE — id сайта (SITE_ID), OP_TOKEN — токен op_XXXX.
Эндпоинты фреймов сайта — под $API/site/$SITE/frames, эндпоинты библиотеки системных фреймов — под $API/frames/system, без сайта в адресе. {ref} — id фрейма (16 шестнадцатеричных символов) или его слаг. Все эндпоинты записи принимают два общих поля: bake: false — не пересобирать фрейм после записи (по умолчанию пересобирается), dry_run: true — проверить и показать результат без записи; ?dry_run=1 в строке запроса значит то же. Эндпоинт без пробного прогона на dry_run отвечает 400 dry_run_not_supported и ничего не пишет. Ответ записи несёт bake — сборку самого фрейма — и rebaked — пересборку фреймов, которые его вкладывают. Поля type, default, is_default и name во входе не записываются: ответ называет их в warnings (type_ignored, default_ignored, name_ignored); поле url отвечает 400 frame_url_not_supported, потому что адрес принадлежит странице, а не фрейму.
Без комментариев
Разметка фрейма и его файлы уходят в браузер как есть, поэтому комментариев в них быть не должно: ни <!-- … --> в разметке, ни // и /* … */ в скриптах, ни /* … */ в стилях, ни кириллицы в коде вне строк. Загрузка и замена разметки, запись файла и запись стилей сайта с комментарием отклоняются целиком с 422 comments_not_allowed, проверка идёт и при dry_run. Например, разметка с заметкой во второй строке:
{
"success": false,
"error": "comments_not_allowed",
"message": "frame markup has 1 comment(s) at line(s) 2: code that goes to the browser carries no comments …",
"kind": "html",
"count": 1,
"found": [{ "kind": "html", "line": 2, "text": "<!-- note -->" }]
}
kind — что проверялось: html (разметка), css или js (файл). found — до 20 находок: kind (html, line — //, block — /* */, cyrillic), номер строки и сама находка. Комментариями не считаются и пропускаются:
| Что | Пример |
|---|---|
| вызовы фреймов и блоки с закрытием | <!-- frame/site__header /-->, <!-- frame/faq --> … <!-- /frame -->, <!-- onpress/repeat {"source":"faq"} --> … <!-- /onpress/repeat --> |
| разделители блоков | <!-- wp:paragraph --> … <!-- /wp:paragraph --> |
| лицензионные заголовки | /*! MIT License … */ |
| условные комментарии | <!--[if lt IE 9]> |
| адреса и строки | "https://example.com", '// не комментарий' |
GET /frames
Возвращает фреймы сайта без разметки, по слагу. Например, найти шапку:
| Параметр | Тип | По умолчанию | Описание |
|---|---|---|---|
block | string | — | только фреймы блока: ?block=article — article, article__faq, article__faq--simple |
q | string | — | подстрока слага без учёта регистра |
include_html | bool | false | 1 — с разметкой каждого фрейма |
curl -s -H "Authorization: Bearer $OP_TOKEN" "$API/site/$SITE/frames?q=site__header"
{
"success": true,
"site_id": "ef7QmgyEc",
"home_url": "https://onpress.pro",
"total": 1,
"frames": [
{
"id": "e03387e61a3ba78a",
"slug": "site__header",
"block": "site",
"element": "header",
"modifier": null,
"bytes": 2220,
"sha256": "…",
"inserts": { "op/repeat": 3, "op/lang-switcher": 1 },
"sources": ["header", "menu.header", "item.children"],
"frame_refs": [],
"binds": 9,
"created_at_gmt": "2026-09-25 19:23:41",
"updated_at_gmt": "2026-10-02 20:00:36",
"updated_by": 1,
"option": "onpress_frame_e03387e61a3ba78a",
"origin": "custom",
"system_update_available": false,
"resettable": true,
"system_slug_match": true,
"preview_page": { "id": 4854, "url": "/page-example/", "link": "https://onpress.pro/page-example/" }
}
],
"registry_option": "onpress_frames",
"page_meta": "_onpress_frame",
"slug_pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*(?:__[a-z0-9]+(?:-[a-z0-9]+)*)?(?:--[a-z0-9]+(?:-[a-z0-9]+)*)?$",
"realm": "prod",
"instance_realm": "prod",
"writable": true
}
| Поле фрейма | Смысл |
|---|---|
inserts, sources, frame_refs, binds | блоки во фрейме и сколько каждого, имена данных, которые он читает, вложенные фреймы, число биндов |
pages | id страниц, у которых это их фрейм; нет страниц — поля нет |
preview_page | первая опубликованная страница, где фрейм выводится: своя, страница фрейма, который его вкладывает (по цепочке), или страница, вставившая его в тело; {id, url, link}. Фрейм нигде не выводится — поля нет |
files | опись файлов фрейма: {имя: {sha256, bytes, type}}; без файлов поля нет |
renders_on_request | true — фрейм зависит от страницы и собирается на каждом запросе |
origin, system_version, resettable и другие | происхождение, см. системные и свои фреймы |
realm, instance_realm, writable — служебные признаки сервиса; у вашего сайта writable: true, а false значит, что запись получит 409 wrong_realm. ?type= не фильтрует и возвращает предупреждение type_ignored.
POST /frames
Загружает новый фрейм и сразу собирает его. Разметка хранится байт в байт, слаг приводится к нижнему регистру. Правка системного фрейма делает его своим. Например, загрузить карточку с названием сайта и меню шапки:
| Поле | Тип | По умолчанию | Описание |
|---|---|---|---|
slug | string | — | обязательно; блок__элемент--модификатор |
html | string | — | обязательно; разметка фрейма; до 2 МБ |
bake | bool | true | собрать фрейм после записи |
dry_run | bool | false | проверить и вернуть отчёт без записи |
curl -s -X POST "$API/site/$SITE/frames" \
-H "Authorization: Bearer $OP_TOKEN" -H 'Content-Type: application/json' \
-d '{"slug": "docs-proba__kartochka", "html": "<section class=\"proba-card\"><h2 data-op-bind='\''{\"text\":\"site.name\"}'\''></h2><!-- frame/menu {\"place\":\"header\"} /--></section>"}'
{
"success": true,
"applied": true,
"updated": false,
"site_id": "SITE_ID",
"frame": {
"id": "20a39aa2b124c145",
"slug": "docs-proba__kartochka",
"bytes": 122,
"inserts": { "op/frame": 1 },
"sources": [],
"frame_refs": ["menu"],
"binds": 1,
"block": "docs-proba",
"element": "kartochka",
"modifier": null,
"origin": "custom",
"…": "…"
},
"inserts": { "op/frame": 1 },
"inserts_total": 1,
"sources": [],
"frame_refs": ["menu"],
"binds": 1,
"renders_on_request": false,
"has_doctype": false,
"has_body": false,
"warnings": [],
"bake": {
"status": "ok",
"response": { "baked": true, "frame_id": "20a39aa2b124c145", "bytes": 685, "blocks": 1, "inserts": 1, "unknown_inserts": [], "unresolved": [], "warnings": [], "pages": [], "…": "…" }
}
}
Отчёт о разметке: inserts, inserts_total, sources, frame_refs (ссылки по id приведены к слагам), binds, renders_on_request и live_reasons, has_doctype, has_body, warnings, frame_refs_missing. Ключи inserts называют блоки коротким именем, в разметке пишите полную форму onpress/…. Поля bake.response описаны на странице «Сборка». dry_run отвечает тем же отчётом с dry_run: true, applied: false и без bake.
| Код | HTTP | Когда | Что делать |
|---|---|---|---|
invalid_slug | 400 | слаг не по шаблону; pattern, hint | возьмите слаг по hint |
frame_html_required | 400 | нет html | пришлите разметку строкой |
frame_url_not_supported | 400 | в теле url | уберите url; адрес задаёт страница |
frame_ref_conflict | 400 | в вызове фрейма два ключа ссылки называют разные фреймы; conflicts | оставьте один ключ; слаг и id одного фрейма конфликтом не считаются |
comments_not_allowed | 422 | в разметке комментарий; found | уберите комментарии, см. без комментариев |
slug_taken | 409 | слаг занят; id занявшего фрейма | замените его разметку PUT /frames/{ref} |
frame_too_large | 413 | разметка больше 2 МБ; max_bytes | вынесите стили и скрипты в файлы фрейма |
wrong_realm | 409 | сайт обслуживает другой сервис | повторите запрос к onpress.pro |
GET /frames/{ref}
Возвращает один фрейм с разметкой. Ответ {success, frame}: всё, что в элементе списка, плюс html — разметка, которую фрейм выводит сейчас (у системного — версия библиотеки), referenced_by — слаги фреймов, которые его вкладывают, и pages — id страниц на этом фрейме. Содержимое файлов здесь не отдаётся, его читают эндпоинтом файла. Например, шапка сайта:
curl -s -H "Authorization: Bearer $OP_TOKEN" "$API/site/$SITE/frames/site__header"
| Код | HTTP | Когда | Что делать |
|---|---|---|---|
frame_not_found | 404 | фрейма нет на сайте | проверьте {ref} в GET /frames |
frame_ref_required | 400 | пустой {ref} | назовите фрейм id или слагом |
GET /frames/{ref}/preview
Возвращает собранный фрейм целым HTML-документом в оформлении сайта — так его показывает консоль. Страницу, данные которой подставляются во фрейм, задаёт page; без него берётся главная, а если её нет — первая опубликованная страница. Например, шапка на странице «Данные»:
| Параметр | Тип | По умолчанию | Описание |
|---|---|---|---|
page | string | — | id или путь страницы-контекста |
format | string | — | json — ответ {html, context_page_id, frame: {id, slug}} вместо text/html |
curl -s -H "Authorization: Bearer $OP_TOKEN" "$API/site/$SITE/frames/site__header/preview?page=6261"
Без format=json ответ — text/html, id страницы-контекста приходит заголовком X-OnPress-Context-Page-Id. Отказы — как у GET /frames/{ref}.
PUT /frames/{ref}
Заменяет разметку фрейма целиком; тело и отчёт — как у загрузки, ответ с updated: true и rebaked — пересборкой фреймов, которые вкладывают этот (по цепочке). Файлы фрейма замена разметки не трогает, slug в теле переименовывает фрейм с теми же последствиями, что у PATCH. Новая разметка у системного фрейма делает его своим; разметка, совпавшая с системной с точностью до пробелов, оставляет его системным. Например:
curl -s -X PUT "$API/site/$SITE/frames/docs-proba__kartochka" \
-H "Authorization: Bearer $OP_TOKEN" -H 'Content-Type: application/json' \
-d '{"html": "<section class=\"proba-card\"><h2 data-op-bind='\''{\"text\":\"site.name\"}'\''></h2></section>"}'
| Код | HTTP | Когда | Что делать |
|---|---|---|---|
frame_not_found | 404 | фрейма нет | создайте его POST /frames |
invalid_slug, frame_html_required, frame_url_not_supported, frame_ref_conflict | 400 | как у загрузки | — |
comments_not_allowed | 422 | в разметке комментарий | уберите комментарии |
slug_taken | 409 | новый слаг занят | выберите другой |
frame_too_large | 413 | больше 2 МБ | вынесите стили и скрипты в файлы |
PATCH /frames/{ref}
Переименовывает фрейм: slug — единственное поле, которое правит PATCH. Во фреймах сайта вызовы старого слага переписываются на новый: <!-- frame/<старый> --> становится <!-- frame/<новый> -->. Страницы хранят id фрейма и не меняются. Новый слаг у системного фрейма делает его своим. Например:
| Поле | Тип | По умолчанию | Описание |
|---|---|---|---|
slug | string | — | обязательно; новый слаг |
bake, dry_run | bool | true, false | как у загрузки |
curl -s -X PATCH "$API/site/$SITE/frames/docs-proba__kartochka" \
-H "Authorization: Bearer $OP_TOKEN" -H 'Content-Type: application/json' \
-d '{"slug": "docs-proba__card"}'
{
"success": true,
"site_id": "SITE_ID",
"frame": { "id": "20a39aa2b124c145", "slug": "docs-proba__card", "block": "docs-proba", "element": "card", "modifier": null, "…": "…" },
"renamed": {
"from": "docs-proba__kartochka",
"to": "docs-proba__card",
"frames_updated": ["docs-proba--lending"],
"details": [ { "id": "53c0ec423d1f7072", "slug": "docs-proba--lending", "replaced": 1 } ]
},
"bake": { "status": "ok" },
"rebaked": { "status": "ok", "…": "…" }
}
Если переименование переписало ссылку в системных фреймах сайта, они тоже становятся своими: renamed.details[].detached и предупреждение system_frames_detached.
| Код | HTTP | Когда | Что делать |
|---|---|---|---|
nothing_to_update | 400 | в теле нет slug | пришлите {"slug": "…"}; разметка меняется PUT |
gone | 410 | в теле только default или is_default | привяжите фрейм к странице POST /frames/{ref}/assign |
invalid_slug | 400 | слаг не по шаблону; pattern, hint | возьмите слаг по hint |
frame_url_not_supported | 400 | в теле url | уберите url |
slug_taken | 409 | слаг занят | выберите другой |
frame_not_found | 404 | фрейма нет | проверьте {ref} |
DELETE /frames/{ref}
Удаляет разметку, файлы фрейма и его запись. У страниц фрейма снимается привязка (unbound_pages) и статическая копия — они начинают отвечать 404. Фреймы, которые его вкладывают, не правятся, а называются в referenced_by. Например:
| Поле или параметр | Тип | По умолчанию | Описание |
|---|---|---|---|
bake | bool | true | пересобрать затронутое; в теле или в строке запроса |
dry_run | bool | false | ничего не удалять и вернуть план; в теле или ?dry_run=1 |
curl -s -X DELETE "$API/site/$SITE/frames/docs-proba__faq" -H "Authorization: Bearer $OP_TOKEN"
{
"success": true,
"site_id": "SITE_ID",
"deleted": { "id": "838a082b034be249", "slug": "docs-proba__faq", "block": "docs-proba", "element": "faq", "modifier": null },
"unbound_pages": [],
"referenced_by": ["docs-proba"],
"warnings": ["frames docs-proba still insert \"docs-proba__faq\"; the insert prints nothing until a frame with this slug appears again"],
"bake": { "status": "ok" }
}
С dry_run ответ — план: would_delete.files и would_delete.unbound_pages — что уйдёт с сайта, referenced_by — фреймы с вызовом этого фрейма, affected_frames — они же вместе с теми, кто вкладывает их, body_pages — страницы, чьё тело вызывает фрейм по имени, library.kept: true — у системного фрейма библиотека и другие сайты не затрагиваются.
| Код | HTTP | Когда | Что делать |
|---|---|---|---|
frame_not_found | 404 | фрейма нет | проверьте {ref} |
POST /frames/{ref}/assign
Ставит фрейм на страницу или снимает его и сразу пересобирает статическую копию страницы. Например, поставить фрейм на страницу proba-api:
| Поле | Тип | По умолчанию | Описание |
|---|---|---|---|
page | number, string | — | обязательно; id, слаг или путь страницы |
clear | bool | false | true — снять фрейм со страницы |
bake | bool | true | пересобрать страницу |
curl -s -X POST "$API/site/$SITE/frames/docs-proba--lending/assign" \
-H "Authorization: Bearer $OP_TOKEN" -H 'Content-Type: application/json' \
-d '{"page": "proba-api"}'
{
"success": true,
"site_id": "SITE_ID",
"frame": { "id": "53c0ec423d1f7072", "slug": "docs-proba--lending", "…": "…" },
"page": { "id": 6428, "title": "Проба API", "url": "/proba-api/" },
"meta_key": "_onpress_frame",
"cleared": false,
"static": { "published": true, "url": "/proba-api", "bytes": 15368, "reason": "", "was_published": true, "was_url": "/proba-api", "removed": false, "changed": false },
"republish": { "status": "ok", "…": "…" }
}
static — что стало со статической копией страницы (поля как у PATCH /pages/{ref}), republish.status — ok, skipped, unavailable или failed; неудача пересборки не отменяет привязку. Фрейм страницы так же задают свойство frame в POST /pages/markdown и поле frame в PATCH /pages/{ref}.
| Код | HTTP | Когда | Что делать |
|---|---|---|---|
page_required | 400 | нет page | назовите страницу |
dry_run_not_supported | 400 | передан dry_run | прочитайте страницу GET /pages/{ref} |
frame_not_found | 404 | фрейма нет | проверьте {ref} |
page_not_found | 404 | страницы нет | проверьте page |
ambiguous_page | 409 | слаг у нескольких страниц | назовите страницу id или путём |
POST /frames/rebake
Пересобирает фреймы, которые зависят от названного изменения. Обычно пересборка идёт сама после записи фрейма, меню, данных или страницы; вручную её запускают после правки мимо API и после обновления платформы. За вызов собирается не больше 25 фреймов, остальные приходят в skipped. Например, после правки набора faq:
| Поле | Тип | Кого пересобрать |
|---|---|---|
all | bool | все фреймы сайта и статические копии страниц |
menus | bool | зависящие от меню |
pages | bool | выводящие страницы, их заголовки и адреса |
languages | bool | зависящие от языка |
settings | bool | зависящие от настроек сайта |
datasets | array | читающие эти наборы |
frames | array | вкладывающие эти фреймы (слаги или id) |
curl -s -X POST "$API/site/$SITE/frames/rebake" \
-H "Authorization: Bearer $OP_TOKEN" -H 'Content-Type: application/json' \
-d '{"datasets": ["faq"]}'
{
"success": true,
"site_id": "SITE_ID",
"changed": { "datasets": ["faq"] },
"rebaked": {
"status": "ok",
"total": 2,
"baked": 0,
"not_needed": 2,
"frames": [
{ "id": "838a082b034be249", "slug": "docs-proba__faq", "because": ["datasets: faq"], "bake": "not_needed", "detail": "renders on request from the source, has no baked copy" }
],
"cache": { "status": "ok", "scope": "pages", "pages": [6428], "…": "…" }
}
}
Поля rebaked описаны на странице «Сборка». С {"all": true} ответ несёт ещё static — пересборку статических копий страниц, в которых заморожены шапка и подвал на момент сборки: status (ok, partial с failures, failed, у сайта без копий — nothing с reason), total, baked и pages — [{id, published, url}]; за вызов — не больше 200 страниц, остальные в skipped, предел в cap.
| Код | HTTP | Когда | Что делать |
|---|---|---|---|
nothing_to_rebake | 400 | тело пустое | назовите изменение или пришлите {"all": true} |
dry_run_not_supported | 400 | передан dry_run | — |
Файлы фрейма
Рядом с разметкой фрейм держит свои стили и скрипты — файлы .css и .js. Опись — поле files фрейма в списке и в карточке: {имя: {sha256, bytes, type}}, type — css или js. Имя файла передаётся параметром name: адрес с именем в пути (…/files/style.css) перехватывает раздача статики, и он отвечает 404. Имя — латиница в нижнем регистре, цифры, - и _, до 48 символов, расширение .css или .js; файл — до 512 КБ; у фрейма — до 16 файлов. Картинки и шрифты в файлы не кладут — они живут в медиатеке, а в css на них ссылаются по адресу.
Стили всех фреймов страницы собираются в один блок <style id="op-frames-css"> в конце <head>, после стилей сайта: фрейм, выведенный несколько раз, приносит свои правила один раз, в порядке появления фреймов на странице, файлы одного фрейма — по имени. На страницу уходит сжатая копия css, её размер — поле minified_bytes в ответе записи; блок <style> прямо в разметке, наоборот, повторяется с каждым выводом фрейма и не сжимается. Метки кусков css пишутся сторожами :root{--имя:1} … :root{--имя-end:1}. Скрипты подключаются файлом: один <script> на фрейм перед </body>, адрес с хешем содержимого, поэтому правка видна сразу.
Правка файла у системного фрейма делает его своим, как правка разметки; возврат к системному возвращает и файлы. Каждая запись пересобирает страницы фрейма (bake: false — не пересобирать) и сбрасывает их кэш — поле cache, как у сброса к системному; dry_run: true показывает фрейм после правки без записи.
GET /frames/{ref}/files
Возвращает файл фрейма с содержимым. Например, style.css:
curl -s -H "Authorization: Bearer $OP_TOKEN" "$API/site/$SITE/frames/docs-proba__files/files?name=style.css"
{
"success": true,
"site_id": "SITE_ID",
"frame": { "id": "380c8a1119a967ca", "slug": "docs-proba__files" },
"file": { "name": "style.css", "sha256": "…", "bytes": 32, "type": "css", "content": ".docs-proba-files{padding:32px}\n" }
}
| Код | HTTP | Когда | Что делать |
|---|---|---|---|
frame_not_found | 404 | фрейма нет | проверьте {ref} |
file_not_found | 404 | файла нет | возьмите имя из files фрейма |
invalid_file_name | 400 | имя не по правилам | style.css, script.js |
PUT /frames/{ref}/files
Создаёт файл или заменяет его содержимое целиком. Например, записать style.css:
| Поле | Тип | По умолчанию | Описание |
|---|---|---|---|
content | string | — | обязательно; текст файла |
bake, dry_run | bool | true, false | как у загрузки фрейма |
curl -s -X PUT "$API/site/$SITE/frames/docs-proba__files/files?name=style.css" \
-H "Authorization: Bearer $OP_TOKEN" -H 'Content-Type: application/json' \
-d '{"content": ".docs-proba-files{padding:24px}\n"}'
{
"success": true,
"applied": true,
"site_id": "SITE_ID",
"frame": { "id": "380c8a1119a967ca", "slug": "docs-proba__files", "files": { "style.css": { "sha256": "…", "bytes": 32, "type": "css" } }, "origin": "custom", "…": "…" },
"file": { "name": "style.css", "sha256": "…", "bytes": 32, "type": "css", "created": true, "minified_bytes": 31 },
"pages": [],
"bake": { "status": "ok", "…": "…" },
"cache": { "status": "nothing", "reason": "no page prints this frame — there is no page cache to drop" }
}
file.created — true, если файла не было; file.minified_bytes — размер сжатой копии у .css (нет поля — css не разобрался и идёт на страницу как записан).
| Код | HTTP | Когда | Что делать |
|---|---|---|---|
invalid_file_name | 400 | имя не по правилам; pattern, hint | style.css, script.js |
file_content_required | 400 | нет content | пришлите текст строкой |
comments_not_allowed | 422 | в файле комментарий | уберите комментарии, см. без комментариев |
too_many_files | 400 | у фрейма уже 16 файлов; max_files | объедините файлы |
file_too_large | 413 | больше 512 КБ; bytes, max_bytes | картинки и шрифты — в медиатеку |
frame_not_found | 404 | фрейма нет | проверьте {ref} |
PATCH /frames/{ref}/files
Переименовывает файл: {"name": "<новое имя>"}, расширение можно сменить в пределах .css и .js. Ответ — как у записи, вместо file — renamed: {from, to}. Например:
curl -s -X PATCH "$API/site/$SITE/frames/docs-proba__files/files?name=script.js" \
-H "Authorization: Bearer $OP_TOKEN" -H 'Content-Type: application/json' \
-d '{"name": "main.js"}'
| Код | HTTP | Когда | Что делать |
|---|---|---|---|
file_not_found | 404 | файла нет | — |
file_exists | 409 | новое имя занято другим файлом | выберите другое |
invalid_file_name | 400 | имя не по правилам | — |
DELETE /frames/{ref}/files
Удаляет файл фрейма. Ответ — как у записи, вместо file — deleted: "<имя>"; bake и dry_run можно передать в теле или в строке запроса. Например:
curl -s -X DELETE "$API/site/$SITE/frames/docs-proba__files/files?name=main.js" -H "Authorization: Bearer $OP_TOKEN"
| Код | HTTP | Когда | Что делать |
|---|---|---|---|
file_not_found | 404 | файла нет | — |
invalid_file_name | 400 | имя не по правилам | — |
Системные и свои фреймы
У платформы есть общая библиотека фреймов — системные фреймы. Новый сайт получает их системными: фрейм выводит версию библиотеки, и её обновление сразу доходит до сайта. Как только разметку, файлы или слаг фрейма поменяли на сайте — эндпоинтами выше, в консоли или агентом, — фрейм становится своим: правки сайта остаются, обновления библиотеки его больше не касаются. Вернуть его к библиотеке можно в любой момент.
Обновление из библиотеки, возврат, сброс и отвязка сразу сбрасывают кэш страниц, где фрейм выводится, — поле cache:
| Поле | Смысл |
|---|---|
status | ok — кэш сброшен; nothing — фрейм не выводится ни на одной странице; failed — не удалось, причина в reason |
scope | pages — страницы поштучно; site — весь сайт: страниц больше 400 или их не удалось счесть |
pages | сколько страниц сброшено (у сброса к системному — их id) |
Происхождение фрейма
Поля в каждом фрейме списка и карточки. Например, шапка, которую поправили на сайте:
| Поле | Смысл |
|---|---|
origin | system — фрейм выводит версию библиотеки; custom — свой фрейм сайта |
system_version | у системного — версия библиотеки; у своего, отошедшего от системного, — версия, от которой отошёл; у фрейма, заведённого на сайте, поля нет |
system_updated_at_gmt | когда в библиотеке вышла текущая версия |
customized_at_gmt | когда фрейм стал своим |
system_update_available | в библиотеке вышла версия новее той, от которой фрейм отошёл |
resettable | фрейм свой, а в библиотеке есть его версия — его можно вернуть |
system_slug_match | в библиотеке есть фрейм с тем же слагом, но другим id |
{
"id": "cb7ccee58078d1fb",
"slug": "site__header",
"origin": "custom",
"system_version": 1,
"system_updated_at_gmt": "2026-09-30 08:11:25",
"customized_at_gmt": "2026-09-13 11:34:34",
"system_update_available": false,
"resettable": true
}
Правка системного фрейма
Замена разметки, переименование и правка файлов системного фрейма делают его своим: origin: "custom", system_version — версия, от которой он отошёл, customized_at_gmt — когда. Новые версии библиотеки его пропускают. Разметка и файлы, совпавшие с библиотекой, при том же слаге оставляют фрейм системным.
POST /frames/{ref}/detach
Делает системный фрейм своим без правки: разметка и файлы те же, но обновления библиотеки до него больше не доходят. Ответ — {"detached": true, "frame": …, "cache": …}, у фрейма, который уже свой, — already_custom: true; dry_run: true показывает результат без записи. Например:
curl -s -X POST "$API/site/$SITE/frames/site__header/detach" \
-H "Authorization: Bearer $OP_TOKEN" -H 'Content-Type: application/json' -d '{}'
| Код | HTTP | Когда | Что делать |
|---|---|---|---|
frame_not_found | 404 | фрейма нет | — |
frame_not_system | 409 | у фрейма нет версии в библиотеке | — |
system_frame_missing | 409 | версия в библиотеке есть, а её разметки нет | повторите позже |
POST /frames/{ref}/revert
Ставит своему фрейму текущую версию библиотеки: фрейм снова системный, страницы пересобираются, кэш сбрасывается. Подходит фрейм, у которого в библиотеке есть версия с тем же id (он отошёл от системного) или с тем же слагом (его id заменяется библиотечным, привязки страниц переезжают на новый id, ссылки по слагу не рвутся). Такие фреймы в списке отмечены resettable: true, второй случай — ещё и system_slug_match: true. Своя разметка и файлы перед возвратом сохраняются в снимок — поле snapshot. Например, вернуть шапку автора:
curl -s -X POST "$API/site/$SITE/frames/author__head/revert" \
-H "Authorization: Bearer $OP_TOKEN" -H 'Content-Type: application/json' -d '{}'
{
"success": true,
"site_id": "SITE_ID",
"reverted": true,
"mode": "reset",
"frame": { "id": "308bb11594598ddd", "slug": "author__head", "origin": "system", "system_version": 7, "…": "…" },
"bake": { "status": "ok", "…": "…" },
"cache": { "status": "nothing" },
"snapshot": { "option": "onpress_frames_reverted_308bb11594598ddd", "saved_at_gmt": "2026-10-02 19:03:59" }
}
mode — reset (тот же id, дальше как у сброса) или replace (тот же слаг: frame.id уже библиотечный, replaced — прежний фрейм, pages_moved — сколько страниц переехало, installed — вложенные фреймы, которых на сайте не было). Фрейм уже системный — reverted: false, already_system: true. dry_run: true отвечает revert: {mode, version, library_id} без записи.
| Код | HTTP | Когда | Что делать |
|---|---|---|---|
frame_not_found | 404 | фрейма нет | — |
frame_not_system | 409 | в библиотеке нет фрейма ни с этим id, ни с этим слагом | — |
system_frame_missing | 409 | версия в библиотеке есть, а её разметки нет | повторите позже |
POST /frames/{ref}/reset
Выбрасывает правки сайта: фрейм снова выводит версию библиотеки вместе с её файлами и дальше получает её обновления. Заодно пересобираются он сам и фреймы, которые его вкладывают, и сбрасывается кэш их страниц. В отличие от возврата, своя версия в снимок не сохраняется. Например:
| Поле | Тип | По умолчанию | Описание |
|---|---|---|---|
bake | bool | true | пересобрать фрейм и страницы |
dry_run | bool | false | показать фрейм после сброса без записи |
curl -s -X POST "$API/site/$SITE/frames/site__header/reset" \
-H "Authorization: Bearer $OP_TOKEN" -H 'Content-Type: application/json' -d '{}'
{
"success": true,
"site_id": "SITE_ID",
"reset": true,
"frame": { "id": "cb7ccee58078d1fb", "slug": "site__header", "origin": "system", "system_version": 1, "system_update_available": false, "resettable": false, "…": "…" },
"pages": [6191, 6238],
"bake": { "status": "ok", "…": "…" },
"rebaked": { "status": "ok", "…": "…" },
"cache": { "status": "ok", "scope": "pages", "pages": [6191, 6238], "…": "…" }
}
Фрейм уже системный — 200 с reset: false и already_system: true, ничего не пишется. pages — страницы на этом фрейме, rebaked приходит, если фрейм кто-то вкладывает.
| Код | HTTP | Когда | Что делать |
|---|---|---|---|
frame_not_found | 404 | фрейма нет | — |
frame_not_system | 409 | фрейм заведён на сайте, версии в библиотеке нет; id, slug | — |
system_frame_missing | 409 | версия в библиотеке есть, а её разметки нет | повторите позже |
Меню, языки и соцсети
Системные фреймы с открытой версткой для меню, переключателя языков, соцсетей, колонок подвала и копирайта. Обзор и примеры — в готовых фреймах. Фрейм ставится на сайт через POST /site/{site_id}/frames/system/{слаг}/install и правится через PUT /frames/{слаг}. Параметры передает вызов: <!-- frame/menu {"place":"footer_1","depth":1} /-->, внутри они читаются как frame.<ключ>.
| Фрейм | Данные | Параметры вызова | Пусто, если |
|---|---|---|---|
menu | menu.header, атрибут повторителя place берет область из frame.place; вложенные пункты — item.children до трех уровней | place (header, footer_1, footer_2, footer_3), lang, depth (1 — без вложенных) | в области нет меню |
menu--dropdown | как у menu | те же и label (по умолчанию «Меню») | в области нет меню |
lang-switcher | pages.translations | hide_current | страница на одном языке |
lang-switcher--dropdown | pages.translations | hide_current, label (aria-label у summary) | страница на одном языке |
socials | site.socials | — | ссылок нет |
footer-columns | menu.footer_1–menu.footer_3, заголовок колонки — бинд menu.<область>.name | — | колонка без меню не выводится |
copyright | site.year, site.name | — | — |
Разметка по умолчанию использует классы op-menu, op-lang-switcher__*, op-socials__*, op-footer-columns__*, op-copyright. У пункта меню один класс menu-item и нет id, ссылки ведут по адресу от корня сайта, сеть у ссылки на соцсеть — в атрибуте data-network.
Инструкция howto
Системный фрейм howto выводит шаги инструкции из слота парного вызова и печатает разметку HowTo в своей верстке. Обзор и пример — в фреймах в markdown. Поставьте фрейм на сайт через POST /site/{site_id}/frames/system/howto/install и правьте его через PUT /frames/howto, как любой фрейм. Например, вызов в теле страницы:
<!-- frame/howto {"title":"Как подключить домен"} -->
### Шаг 1. Купите домен
Подойдет любой регистратор.
### Шаг 2. Подключите домен в консоли
Откройте раздел «Сайты» и нажмите «Подключить домен».
<!-- /frame -->
| Данные | Параметры вызова | Пусто, если |
|---|---|---|
slot.faq: уровень первого заголовка в слоте — уровень шага, текст до следующего такого заголовка — описание; item.n — номер шага с единицы | title — заголовок H2 над шагами и name в HowTo, без него name — название страницы; anchor — id секции, по умолчанию howto | в слоте нет ни одного шага с текстом |
Разметка по умолчанию — секция ophowto с элементами ophowto__title, ophowto__steps, ophowto__step, ophowto__name и ophowto__text, за ней <script type="application/ld+json">. Узел HowTo собирают повторитель <!-- onpress/repeat {"source":"slot.faq","separator":","} --> и блоки <!-- onpress/value {"bind":"item.q_text","format":"json"} /-->: шаг HowToStep получает position из item.n, name из item.q_text и text из item.a_text. Скриптов два, с условиями if и unless по frame.title, и страница выводит только один из них. На странице с вызовом howto платформа не добавляет второй HowTo в общий граф страницы.
GET /frames/system
Возвращает все фреймы библиотеки по слагу, без разметки. Сайт в адресе не нужен. Например:
curl -s -H "Authorization: Bearer $OP_TOKEN" "$API/frames/system"
{
"success": true,
"total": 146,
"registry_option": "onpress_system_frames",
"frames": [
{ "id": "ee9afc3aa470d85a", "slug": "about__backers", "block": "about", "element": "backers", "modifier": null, "version": 1, "bytes": 566, "sha256": "…", "updated_at_gmt": "2026-10-02 19:17:18", "updated_by": 1, "source_blog": 268 }
]
}
version растёт на единицу с каждой новой версией. У фрейма с файлами есть files (опись, как у фрейма сайта) и bundle_sha256 — общий хеш разметки и файлов: по нему видно, что версия поменялась, даже если разметка та же.
GET /frames/system/{ref}
Возвращает один фрейм библиотеки по id или слагу: поля из списка и html — разметку текущей версии. Например:
curl -s -H "Authorization: Bearer $OP_TOKEN" "$API/frames/system/site__header"
| Код | HTTP | Когда | Что делать |
|---|---|---|---|
system_frame_not_found | 404 | такого фрейма в библиотеке нет | возьмите слаг из GET /frames/system |
POST /frames/system/{ref}/install
Ставит фрейм библиотеки на ваш сайт, если его там ещё нет: на сайте появляется системный фрейм с тем же id, разметкой и файлами библиотеки, страницы, где он выводится, пересобираются, кэш сбрасывается. Фреймы, которые он вкладывает и которых на сайте нет, ставятся следом — строками в sites с because: "inserted by <слаг>". Сайт задаёт путь /site/$SITE/frames/system/{ref}/install; без него сайт называют полем site_id в теле. Ставить фрейм можно только на свой сайт. Например, поставить фрейм steps:
| Поле | Тип | По умолчанию | Описание |
|---|---|---|---|
site_id | string | — | id сайта, если его нет в пути |
replace_custom | bool | false | у сайта свой фрейм с тем же слагом — заменить его системным: id меняется, привязки страниц переезжают, ссылки по слагу продолжают работать |
bake | bool | true | пересобрать страницы |
dry_run | bool | false | только отчёт |
curl -s -X POST "$API/site/$SITE/frames/system/steps/install" \
-H "Authorization: Bearer $OP_TOKEN"
{
"success": true,
"applied": true,
"frame": { "id": "9038cbb9b1c256a4", "slug": "steps", "version": 3, "…": "…" },
"sites": [
{ "slug": "steps", "status": "installed", "bake": "ok", "cache": { "status": "nothing" } }
]
}
sites[].status | Смысл |
|---|---|
installed | фрейм поставлен |
replaced | свой фрейм с тем же слагом заменён системным; replaced: {id, slug}, pages_moved |
already_system | фрейм уже стоит системным, ничего не сделано |
already_custom | фрейм с этим id стоит своим; вернуть его к библиотеке — POST /frames/{ref}/reset |
| Код | HTTP | Когда | Что делать |
|---|---|---|---|
blog_required | 400 | сайт не назван | укажите сайт в пути или в site_id |
site_not_found | 404 | сайта нет или он не ваш | проверьте site_id |
system_frame_not_found | 404 | такого фрейма в библиотеке нет | возьмите слаг из GET /frames/system |
system_frame_missing | 409 | версия в библиотеке есть, а её разметки нет | повторите позже |
slug_taken_by_custom | 409 | у сайта свой фрейм с этим слагом; id, library_id | повторите с replace_custom: true |
slug_taken_by_system | 409 | слаг занят другим системным фреймом | — |