Как свёрстать лист: руководство для агента
Это руководство для программы, которая верстает сертификат, грамоту или диплом сама, без человека у экрана: где стоит каждый блок, каким шрифтом и кеглем он набран, как подставляются ФИО и падежи и как до выпуска убедиться, что всё влезло.
Точный список полей — в справочнике макета, здесь — как ими пользоваться. Запросы идут на https://vruchay.ru/api с заголовком Authorization: Bearer $VRUCHAY_TOKEN; рядом назван инструмент MCP, который делает то же самое. … в адресе ниже означает /api/documents/{id}.
Порядок работы
GET /api/layout/catalog— единицы, форматы, шрифты, раскладки, умолчания блоков (MCP:get_layout_catalog).POST /api/documentsсformat,orientation, при желанииpreset(MCP:create_document). Запомнитьidиsheets[0].id.PATCH /api/documents/{id}: мероприятие (eventName,eventDate,eventPlace,eventHours) и постоянные поляconstants(MCP:update_document).- Загрузить таблицу получателей: проверка меряет текст на её самых длинных значениях, а раскладка выбирает строки по её колонкам.
- Фон и картинки (MCP:
upload_imageпо ссылке, файл с диска —get_upload_link). - Свёрстать:
…/sheets/{sheetId}/presetили…/sheets/{sheetId}/elements(MCP:apply_preset,edit_layout,set_layout). …/check→ исправить →…/preview→ посмотреть → исправить (MCP:check_sheet,preview_sheet), пока замечаний нет.POST …/validation— проверка каждой строки таблицы перед выпуском (MCP:validate_recipients).
Редактор в кабинете записывает лист с номером версии и правку агента молча не затрёт: человек с открытым листом увидит «Лист изменили в другом месте» и выберет, чья версия остаётся. Если в редакторе ничего не ждёт записи, свежий лист подхватится сам, когда человек вернётся во вкладку. Чтобы обойтись без выбора, просите не трогать лист, пока агент верстает.
1. Как устроен лист
У документа один или несколько листов (sheets по position): нулевой — лицевая сторона, дальше — оборот и следующие страницы. Лист — это id, position, layout (до 500 блоков), backgroundFileId и version (номер правки). Размер один на документ: pageWidthMm × pageHeightMm, 50…600 мм, по умолчанию 297 × 210 (A4 альбомный).
Координаты — мм от левого верхнего угла: x вправо, y вниз; блок — прямоугольник x, y, w, h. Кегль — в pt: 1 pt ≈ 0,3528 мм.
| pt | 8 | 10 | 12 | 14 | 18 | 24 | 36 | 48 | 72 |
|---|---|---|---|---|---|---|---|---|---|
| мм | 2,8 | 3,5 | 4,2 | 4,9 | 6,4 | 8,5 | 12,7 | 16,9 | 25,4 |
rotation— градусы −360…360, по часовой вокруг центра блока.z— целое −10000…10000, по умолчанию 0; больший поверх. При равных `z` поверх тот, кто позже в массиве.opacity0…1 — прозрачность блока целиком.hidden: true— не печатается, но остаётся в листе; проверка его пропускает, кроме повтораid(duplicate_id): id должен быть единственным среди всех блоков.locked,groupId— замок и группа для мыши в редакторе; на печать и API не влияют.name— подпись в панели слоёв («ФИО», «Рамка»): ставьте, человеку после агента так проще.id— 1–100 символов, уникален на листе. Говорящие id (title,name,qr) удобнее: по ним идёт правка и ими проверка называет блоки.
Отрисовка: белый лист → фон → видимые блоки по возрастанию z. Всё за краем листа обрезается. Текст, не влезший в блок, обрезается по краю блока — высоту считают, а не угадывают (раздел 4).
2. Формат и ориентация
format | Книжный, мм | Альбомный, мм |
|---|---|---|
a3 | 297 × 420 | 420 × 297 |
a4 | 210 × 297 | 297 × 210 |
a5 | 148 × 210 | 210 × 148 |
a6 | 105 × 148 | 148 × 105 |
letter (US Letter) | 216 × 279 | 279 × 216 |
legal (US Legal) | 216 × 356 | 356 × 216 |
square | 210 × 210 | 210 × 210 |
wide (экран 16:9) | 167 × 297 | 297 × 167 |
POST /api/documents принимает format + orientation (portrait | landscape) или pageWidthMm + pageHeightMm. Формат сильнее миллиметров; без `orientation` он альбомный. Одна orientation без format поворачивает миллиметры: {"orientation": "portrait"} даёт 210 × 297.
curl -X POST "https://vruchay.ru/api/documents" \
-H "Authorization: Bearer $VRUCHAY_TOKEN" -H "Content-Type: application/json" \
-d '{"title": "Сертификаты участников", "format": "a4", "orientation": "landscape"}'Ответ — документ с одним пустым листом и колонками name, email. PATCH /api/documents/{id} формата не знает, только миллиметры (раздел 13).
3. Поля печати
Типография срезает до 3 мм с края, офисный принтер не печатает ближе 5 мм. Текст и QR держите в 10–15 мм от края, рамку — в 5–10 мм; фон, полосы и подложки можно тянуть до края. PDF — ровно размер листа, без вылетов: если типография просит вылеты по 3 мм, сделайте лист на 6 мм больше (A4 альбомный — 303 × 216). Проверка сообщает off_page (блок целиком за листом), partly_off_page (текст, QR или ссылка частью за краем) и outside_safe_area (текст или QR ближе 5 мм к краю).
4. Арифметика раскладки
W, H — лист, m — поле, w, h — блок.
- Центр:
x = (W − w) / 2. На всю ширину между полями:x = m,w = W − 2m(A4 альбомный,m = 30:x = 30,w = 237; A4 книжный,m = 20:x = 20,w = 170) иalign: "center". - Две колонки с промежутком
g:c = (W − 2m − g) / 2,x₁ = m,x₂ = m + c + g(A4 альбомный,m = 30,g = 17:c = 110,x₂ = 157); правую выравнивайтеalign: "right". - Стопка:
y₍ᵢ₊₁₎ = yᵢ + hᵢ + отбивка; 1–3 мм внутри группы, 6–15 мм между группами (шапка, имя, текст, подвал). Стопку поднимайте на 2,5 % высоты листа над центром — так делают заготовки: точно по центру она кажется съехавшей вниз. - Строки на одной высоте (должность слева, фамилия справа) — равные
y,hиverticalAlign: "bottom".
Высота текстового блока. Строка — fontSize × lineHeight × 0,3528 мм. Буквы занимают 1,3 кегля, и при lineHeight меньше 1,3 выносные последней строки выступают — измеритель прибавляет выступ:
h ≥ n × fontSize × lineHeight × 0,3528 + max(0, 1,3 − lineHeight) × fontSize × 0,3528
+ strokeWidth + 2 × (padding + borderWidth)n — строк; прибавьте 1–2 мм запаса. Пример 1 ниже (A4 альбомный) посчитан так:
| Блок | Кегль, межстрочный, строк | Расчёт, мм | y | h |
|---|---|---|---|---|
| Логотип | — | — | 16 | 23 |
| Заголовок | 34 pt, 1,2, 1 | 14,4 + 1,2 = 15,6 | 42 | 17 |
| Подводка | 13 pt, 1,3, 1 | 6,0 | 62 | 8 |
| ФИО | 44 pt, 1,15, 2 | 35,7 + 2,3 = 38,0 | 72 | 40 |
| «принял участие» | 17 pt, 1,3, 1 | 7,8 | 113 | 9 |
| Мероприятие | 20 pt, 1,25, 2 | 17,6 + 0,4 = 18,0 | 123 | 19 |
| Место и даты | 13 pt, 1,3, 1 | 6,0 | 143 | 7 |
| Подписи | 11 pt, 1,25, 2 | 9,7 + 0,2 = 9,9 | 163 | 11 |
| Дата и номер | 10 pt, 1,3, 1 | 4,6 | 181 | 6 |
Ширина строки — прикидка: знаков ≈ w / (fontSize × 0,3528 × k), где k — средняя ширина знака строчного русского текста в кеглях: ≈0,5 у PT Sans, Alegreya, Tinos, Marck Script; 0,53–0,57 у PT Serif, Playfair Display, Lora, Roboto, Arimo; 0,57–0,6 у Inter, Montserrat, Merriweather; 0,65–0,75 у Unbounded, Comfortaa; ≈0,4 у Oswald, Caveat. Прописные шире на 20–30 %, letterSpacing прибавляется после каждого знака. Точно меряет проверка листа — по метрикам самих шрифтов.
5. Кегли по ролям
Заготовки задают кегли долями от кегля имени и подбирают его под свободное поле. На A4 альбомном:
| Роль | Пример | Доля | Заготовки | Диапазон, pt |
|---|---|---|---|---|
| Заголовок | «Сертификат», «Диплом» | 0,72 | 31–37 pt, полужирный, разрядка 10 % кегля | 28–40 |
| Подводка | «Награждается», «Настоящим удостоверяется, что» | 0,30 | 13–15 pt, прописные, разрядка 10 % | 12–16 |
| ФИО | %name, %name_dat | 1 | 43–51 pt, межстрочный 1,15, autoFit | 32–54 |
| Команда, город | %team · %city | 0,26 | 11–13 pt | 11–14 |
| Текст, мероприятие | «принял участие», %event | 0,42 | 18–21 pt, мероприятие полужирным | 16–22 |
| Доп. строки | место и даты, «в объёме 72 ч.» | 0,30 | 13 pt | 12–14 |
| Подписи, дата, номер | %signer_role, %date | 0,24 | 10–12 pt | 9–12 |
| Подпись к QR, сноски | «Проверить подлинность» | — | — | 7–9 |
На A4 книжном заготовки крупнее примерно в 1,4 раза, но лист уже: имя крупнее 48 pt держится там только на autoFit, спокойнее 32–40 pt. A5 — кегли × 0,7, A3 — × 1,4; мельче 7 pt не читается. Цвет: заголовок и подводка — акцент, имя — тёмный тон акцента, текст — тёмно-серый или тёмно-коричневый, не чистый чёрный (#8B2020, #7A1E1E, #4A3628).
6. Шрифты
Печатаются только гарнитуры набора (файлы, кириллица, метрики; тот же список — fonts в каталоге):
Группа (id) | Полужирный и курсив | Только полужирный | Только обычное |
|---|---|---|---|
Без засечек (sans) | PT Sans, Inter, Montserrat, Roboto, Open Sans, Arimo, Alegreya Sans | Comfortaa, Oswald | — |
С засечками (serif) | PT Serif, Lora, Playfair Display, Merriweather, Alegreya, Alegreya SC, Tinos | — | Alice |
Крупные жирные (display) | — | Unbounded, Unbounded ExtraBold, Unbounded Black | Dela Gothic One, Rubik Mono One, Ruslan Display |
Рукописные (script) | — | Caveat, Amatic SC | Marck Script, Bad Script, Lobster, Pacifico, Pangolin, Pattaya |
- Файлы есть только у начертаний 400 и 700; недостающий полужирный или курсив браузер нарисует сам, грубо.
fontWeightв разметке кроме 400 и 700 возьмёт ближайший файл. - Arimo и Tinos — замена Arial и Times New Roman с теми же ширинами (самих их нет). Широкие — Montserrat, Merriweather, Unbounded, Comfortaa; узкие — Oswald, Caveat, Amatic SC; Alegreya SC — капитель.
- Иное имя схема пропустит, но печать подставит системный шрифт, а проверка ответит
unknown_font. Пишите имя ровно как в таблице.
| Стиль заготовки | Заголовок, подводка | ФИО | Текст |
|---|---|---|---|
classic | PT Serif | Playfair Display | PT Serif |
solemn | Playfair Display | Playfair Display | Lora |
academic | Lora | Lora | Lora |
strict | PT Sans | PT Sans | PT Sans |
modern | Montserrat | Montserrat | Inter |
script | PT Serif | Marck Script | PT Serif |
handwritten | PT Sans | Caveat | PT Sans |
Не больше двух-трёх гарнитур на лист; рукописные — только для имени, без прописных и разрядки; display — только для короткого заголовка.
7. Блоки
Умолчания — в каталоге (elements); непереданное свойство сервер дописывает и хранит лист полным.
Текст (type: "text")
{"id": "name", "type": "text", "name": "ФИО", "x": 30, "y": 72, "w": 237, "h": 40, "z": 5,
"props": {"text": "%name", "fontFamily": "Playfair Display", "fontSize": 44, "bold": true, "color": "#7A1E1E",
"align": "center", "verticalAlign": "middle", "lineHeight": 1.15, "letterSpacing": 0, "autoFit": true}}| Свойство | Умолчание | Что делает |
|---|---|---|
text | — | Строка: %поле — подстановка, \n — новый абзац; сервер превращает её в doc |
doc | — | Дерево с оформлением кусков (ниже). Нужно одно из text, doc |
fontFamily | PT Sans | Раздел 6 |
fontSize | 16 | pt, до 500 |
color | #000000 | #RRGGBB |
align / verticalAlign | center / middle | left, center, right, justify / top, middle, bottom |
lineHeight | 1.2 | Множитель кегля |
letterSpacing | 0 | Разрядка в pt после каждого знака, может быть < 0 |
bold, italic, underline, uppercase | false | Весь блок; прописные — оформлением, данные не меняются |
strokeWidth, strokeColor | 0, #ffffff | Обводка букв, мм, 0…2 — текст на пёстром фоне |
autoFit | false | Ужимать текст, если не влезает |
padding | 0 | Отступ текста от краёв, мм, 0…50 |
background | null | Заливка блока; null — прозрачный |
borderWidth, borderColor | 0, #000000 | Рамка блока, мм, 0…5 |
shadow | false | Мягкая тень под буквами |
Тексту остаётся w − 2 × (padding + borderWidth) в ширину и столько же меньше в высоту. `autoFit` только ужимает, ступенями: кегль до 65 %, межстрочный до 1,0 (не больше чем на пятую часть), кегль до 50 %, разрядка до −3 % кегля. Слова не рвутся: длинная фамилия ужимает кегль, пока не встанет в ширину целиком. Не влезло и так — обрежется, проверка скажет text_overflow. Включайте его у всего, что приходит из данных; кегль ставьте как для обычного имени, блок — с запасом на две строки.
Дерево текста (doc)
Для разного оформления в одном блоке. Формат — как у ProseMirror:
{"type": "doc", "content": [
{"type": "paragraph", "attrs": {"align": "center"}, "content": [
{"type": "text", "text": "за "},
{"type": "mergeField", "attrs": {"source": "place_word", "format": "upper"},
"marks": [{"type": "bold"}, {"type": "textStyle", "attrs": {"color": "#8A6A22"}}]},
{"type": "text", "text": " место"},
{"type": "hardBreak"},
{"type": "mergeField", "attrs": {"source": "team", "fallback": "в личном зачёте"}, "marks": [{"type": "italic"}]}]},
{"type": "paragraph", "content": [
{"type": "text", "text": "Проверить: vruchay.ru/c/"},
{"type": "mergeField", "attrs": {"source": "code"},
"marks": [{"type": "link", "attrs": {"href": "https://vruchay.ru/c/{{code}}", "underline": false}}]}]}]}- Абзацы идут без отбивки; пустой (
"content": []) — пустая строка.attrs.alignперекрывает выравнивание блока,attrs.indent0…8 — ступени по 6 мм. Внутри:text(непустой),mergeField,hardBreak. - Списки:
bulletList(marker:disc,dash,circle,square),orderedList(numbering:decimal-dot,decimal-paren,upper-roman,lower-alpha;start) изlistItemс абзацами;checked: true/falseрисует ☑/☐. - Марки (до 8):
bold,italic,underline,strike,superscript,subscript;textStyle—color,background,fontFamily,fontSize(pt),fontWeight,letterSpacing,wordSpacing(pt),transform(uppercase,lowercase,smallcaps);link—href(http(s)://, поля{{code}}/%codeкодируются как часть адреса),underline,color. - Марка сильнее блока: кусок со своим
textStyle.fontFamilyне сменит гарнитуру вслед за блоком. До 1000 узлов, списки до 6 уровней.
Картинка (type: "image")
props.fileId — картинка блока, загруженная в документ (MCP: upload_image, target: "element"; файл с диска — get_upload_link с тем же target, см. раздел 9 «Фон»):
curl -X POST "https://vruchay.ru/api/documents/$DOCUMENT_ID/assets" \
-H "Authorization: Bearer $VRUCHAY_TOKEN" -F "file=@logo.png"
curl -X POST "https://vruchay.ru/api/documents/$DOCUMENT_ID/assets/from-url" \
-H "Authorization: Bearer $VRUCHAY_TOKEN" -H "Content-Type: application/json" \
-d '{"url": "https://example.ru/logo.png"}'Ответ: {"fileId": "…", "url": "…", "widthPx": 800, "heightPx": 320}.
{"id": "logo", "type": "image", "x": 30, "y": 18, "w": 40, "h": 16, "props": {"fileId": "9b8a7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d"}}- Фон сюда не годится: его
fileIdотвергается с 404 «Картинка не найдена». - Картинка вписывается без обрезки и искажения, лишнее место пустое. Держите пропорции:
h = w × heightPx / widthPx. - PNG или JPEG до 20 МБ; подпись и печать — PNG с прозрачностью. Для печати —
w / 25,4 × 300пикселей по ширине. По ссылке — только публичныйhttp(s)на портах 80/443, до 3 переадресаций, 15 с, 30 загрузок в минуту. - Без файла лист не напечатается (
missing_image). - Картинки ложатся в PDF почти без сжатия: фон и картинки листа вместе держите до 4 МБ (
heavy_images), иначе почта может не принять PDF вложением.
QR-код (type: "qr")
{"id": "qr", "type": "qr", "x": 244, "y": 151, "w": 30, "h": 30, "props": {"template": "", "color": "#1A1D20",
"background": "#ffffff", "dotStyle": "square", "cornerStyle": "square", "cornerColor": null, "margin": 0, "level": "M"}}- Пустой
template— адрес проверки экземпляраhttps://vruchay.ru/c/K7M2-9QXR-4TVB. ПриverifyEnabled: falseтакой QR не печатается. - Свой
template— до 5000 знаков, поля только%поле, значения без кодирования (в адрес — поля без пробелов и кириллицы:https://example.ru/check?c=%code).http(s)-текст в PDF ещё и кликается. - Квадрат от 15 мм, лучше 20–30; рисуется по меньшей стороне.
backgroundnull— прозрачная подложка, только на светлом однотонном;dotStylesquare/rounded/dots;cornerStylesquare/rounded/circle;cornerColor(null— как точки);margin0…4 — белое поле в клетках внутри блока (код мельче);levelL/M/Q/H— запас 7/15/25/30 %, выше — гуще узор. Точки тёмные на светлом; подпись — отдельным блоком под QR.
Ссылка (type: "link")
Невидимая кликабельная область в PDF: props.url — http(s)://, до 2000 знаков, поля {{code}}/%code кодируются; не вышло адреса — область без ссылки. Поверх картинки — z больше.
{"id": "logo-link", "type": "link", "x": 30, "y": 18, "w": 40, "h": 16, "z": 2, "props": {"url": "https://edu.example.ru"}}Фигура (type: "shape")
{"id": "frame", "type": "shape", "x": 8, "y": 8, "w": 281, "h": 194, "z": 0, "locked": true,
"props": {"kind": "rect", "fill": null, "stroke": "#8B2020", "strokeWidth": 0.8, "radius": 2, "dash": 0}}kind: rect (умолчание), ellipse (вписан в блок), line; fill — null или цвет; stroke — #000000; strokeWidth — мм, 0…20, по умолчанию 0,5; radius — скругление, мм; dash — штрих пунктира, мм (0 — сплошная).
- Линия горизонтальна и идёт через середину блока: от
(x, y + h/2)до(x + w, y + h/2); толщина —strokeWidth,hставьте 1–2 мм. Вертикальная —rotation: 90(раздел 16). strokeWidthпечатается ровно в миллиметрах: линия подписи — 0,3–0,5 мм, рамка грамоты — 0,5–1,5 мм. Тонкая линия может быть и залитым прямоугольником без обводки:{"kind": "rect", "fill": "#33383F", "strokeWidth": 0}приh = 0.3.- Подложка под текстом — меньший
zиopacity0,8–0,9.
8. Поля подстановки
Поле — ключ латиницей. В text и шаблоне QR — %ключ, в дереве — mergeField с attrs.source, в адресе ссылки — {{ключ}} или %ключ. GET …/fields перечисляет колонки, постоянные, мероприятие, служебные, поля каждого листа и unknown — те, что взять неоткуда.
Сила: служебные < постоянные поля документа < колонки строки.
Колонки — name, email (всегда) и заведённые при импорте (place, team, city…). Из surname, firstname, patronymic считаются падежи, сокращение, латиница и пол, но %name печатает только колонку name — заполняйте её или ставьте на лист части.
Постоянные поля — одно значение на всех (подписант, номер приказа); список целиком заменяет прежний. Ключ ^[a-z][a-z0-9_]{0,39}$, до 30 полей, значение до 1000 знаков, служебные ключи заняты.
curl -X PATCH "https://vruchay.ru/api/documents/$DOCUMENT_ID" \
-H "Authorization: Bearer $VRUCHAY_TOKEN" -H "Content-Type: application/json" \
-d '{"constants": [{"key": "signer_role", "title": "Должность подписанта", "value": "Директор"},
{"key": "signer_name", "title": "Подписант", "value": "А. А. Петров"}]}'Мероприятие — поля документа: eventName → %event, eventDate → %event_date, eventPlace → %event_place, eventHours → %hours; даты — свободным текстом.
| Служебное поле | Пример |
|---|---|
date / date_long / date_iso / date_en — дата выдачи | 12.10.2026 / 12 октября 2026 г. / 2026-10-12 / 12 October 2026 |
year, number (строка с 1), total | 2026, 3, 120 |
reg_number — сквозной номер за год | 142/2026 |
code — проверочный код (тот, что в QR) | K7M2-9QXR-4TVB |
org, valid_until (конец срока, если задан) | АНО «Пример», 12.10.2027 |
event, event_date, event_place, hours | мероприятие |
name_dat / name_gen / name_short | Петровой Анне Сергеевне / Петровой Анны Сергеевны / Петрова А. С. |
name_lat_gost / name_lat_icao (как в загранпаспорте) | Petrova Anna Sergeevna |
place_word — из колонки place | второе |
hours_word — из hours | семьдесят два часа |
- Дата —
issueDateдокумента, иначе день выпуска, по Москве. - Падеж выбирает фраза: после «Награждается», «Объявляется» —
%name_dat; в «подтверждает, что …» —%name. place_wordзнает места 1–10 («1», «1 место», «1-е»), иное — как в ячейке.hours_word— только из чистого числа: пишитеeventHours: "72".- Своя колонка (
name_dat,place_word) сильнее вычисленного.
Род. В тексте макета: награждён(а), занял(а), участник(-ца), победитель(-ница), прошёл(ла) — скобка вплотную, 1–5 букв; или пара принял|приняла — для любых слов. Пол — из колонки пол/pol/gender/sex (м, муж, male, ж, жен, female…), иначе угадывается по ФИО. Не угадался — скобка остаётся как есть, пара печатается «принял/приняла». Незнакомое («студент(ка)») не трогается — пишите пару.
Печать значения. Подставленное значение проходит типографику: кавычки → «ёлочки», « - » → « — », «17-19» → «17–19», неразрывные пробелы в инициалах, после «г.», «ул.», «№», перед «%» и после однобуквенных предлогов; code и reg_number — буква в букву. Текст макета не трогается — набирайте сразу с «ёлочками» и тире. У mergeField: format none/upper/lower/title, fallback — если значения нет. Пустое поле — пустота; абзац только из полей и разделителей (, ; : · • – — -) при всех пустых полях исчезает, разделитель между двумя полями уходит, если одно пусто (%event_place, %event_date без даты → «г. Москва»). Абзац со словами остаётся.
9. Фон
Картинка бланка под всеми блоками, своя у каждого листа (MCP: upload_image по ссылке или get_upload_link для файла с диска, target: "background"):
BG="https://vruchay.ru/api/documents/$DOCUMENT_ID/sheets/$SHEET_ID/background"
curl -X POST "$BG" -H "Authorization: Bearer $VRUCHAY_TOKEN" -F "file=@blank.jpg"
curl -X POST "$BG/from-url" -H "Authorization: Bearer $VRUCHAY_TOKEN" \
-H "Content-Type: application/json" -d '{"url": "https://example.ru/blank.jpg"}'
curl -X DELETE "$BG" -H "Authorization: Bearer $VRUCHAY_TOKEN" # убрать фонБланк с диска через MCP. Агенту с оболочкой (Claude Code, Codex, Cursor) файл передать аргументом нельзя — инструменты ходят JSON-ом. Вместо этого:
get_upload_link {"documentId": "…", "target": "background", "sheetId": "…"}— в ответе готовая команда с одноразовой ссылкой на 15 минут;- выполнить её, подставив путь:
curl -sS -F "file=@бланк.png" "https://vruchay.ru/api/v1/uploads/u.…"; - curl ответит
{"target": "background", "fileId": "…", "widthPx": 3508, "heightPx": 2480}— фон уже на листе; дальшеpreview_sheet.
Токена в команде нет: пропуск — сама ссылка, она принимает один файл. Файл не того типа ссылку не тратит — повторите с верным; за следующим файлом — новая ссылка. Логотип, подпись и печать — так же, с target: "element": полученный fileId ставится в props.fileId блока image.
- PNG или JPEG до 20 МБ; ответ —
fileId,url,widthPx,heightPx. - Заполняет лист с обрезкой, а не растягивается: лишнее по краям срезается поровну. Пропорции картинки — как у листа.
- Пикселей = мм / 25,4 × dpi; для печати 300, ниже 150 мылит. A4 альбомный — 3508 × 2480 (минимум 1754 × 1240), книжный — 2480 × 3508, A5 альбомный — 2480 × 1748, A3 альбомный — 4961 × 3508.
- Фон — не блок: нет в
layout, нетz, егоfileIdне годится дляimage.
10. Готовые раскладки
Вид (kind) + набор шрифтов и цветов (style, раздел 6): award (грамота за место: %name_dat, «за %place_word место»), winner, laureate, participant, school, thanks, letter (все с %name_dat), attendee (%name, «принял|приняла участие»), course, graduate (%name, «в объёме %hours»).
curl -X POST "https://vruchay.ru/api/documents/$DOCUMENT_ID/sheets/$SHEET_ID/preset" \
-H "Authorization: Bearer $VRUCHAY_TOKEN" -H "Content-Type: application/json" \
-d '{"kind": "attendee", "style": "modern", "titlePrinted": false,
"palette": {"ink": "#2B2F33", "accent": "#2C6E63"}, "mode": "replace"}'То же при создании: POST /api/documents с "preset": {"kind": "award", "style": "classic"}.
titlePrinted(по умолчаниюfalse) — заголовок уже на бланке: своего не будет, текст начнётся с 34 % высоты листа.palette—ink,accent,nameInk(иначеaccent); в MCP — то же поле уapply_presetи уpresetвcreate_document.mode:replace(умолчание) илиappend(совпавшим id — суффикс;zблоков заготовки идут с нуля, под блоками с большимz).- Строки с полями, которых нет в колонках на момент применения (
%team,%city,%place_wordбезplace), выбрасываются; колонкиresult,category,coach,event_place,numberдобавляются «подпись: значение» (до двух). Сначала таблица, потом заготовка. - Подвал:
%signer_roleслева,%signer_nameсправа,%date · %orgпо центру. Если постоянныхsigner_role,signer_nameнет, заготовка заводит их с образцами «Руководитель» и «И. О. Фамилия» — замените. - При включённой проверке подлинности заготовка ставит QR проверки (
preset-{kind}-qr) в правый нижний угол поля, 20–30 мм (на A4 — 23): фамилия подписанта кончается левее него, строка даты сужается с обеих сторон, стопка поднимается. Если QR уже есть на другом листе (или на этом приappend), второй не ставится. Убрали QR — проверка напомнитverify_without_qr. - Ответ —
{sheet, problems}; id —preset-{kind}-0,-1…,-signer-role,-signer-name,-issued; у всехautoFit.
11. Правка по частям и версии
PATCH …/sheets/{sheetId}/elements (MCP: edit_layout):
{"baseVersion": 7, "remove": ["old-note"],
"update": [{"id": "name", "h": 40, "props": {"fontSize": 40, "autoFit": true}},
{"id": "event", "props": {"text": "за участие в %event"}}],
"add": [{"id": "qr", "type": "qr", "x": 244, "y": 151, "w": 30, "h": 30, "props": {}},
{"type": "text", "y": 190, "w": 120, "h": 6, "props": {"text": "Рег. № %reg_number", "fontSize": 9}}]}- Порядок:
remove,update,add; убрать и добавить тот же id — заменить блок (так меняют его вид). add: безid— UUID, безz— поверх всех, безx/y— по центру листа, безw/h— текст 120 × 20, картинка 50 × 50, QR 30 × 30, ссылка 80 × 10, прямоугольник 60 × 40, овал 40 × 40, линия 80 × 2. Занятый id — 400.update: верхние поля заменяются,propsсливаются на один уровень; новыйprops.textилиprops.docзаменяет весь текст;typeиidне меняются.- До 500 операций каждого вида. Ответ —
{sheet, problems}; ошибка схемы называет блок по id (name.props.fontSize: …).
Лист целиком — PATCH …/sheets/{sheetId} с {"layout": [...], "baseVersion": 7} (MCP: set_layout, он же сразу проверяет).
Версии. version растёт с каждым сохранением. Если лист изменился после baseVersion, ответ — 409 {"message": "Лист изменился (версия 9, а правка сделана к версии 7)…", "version": 9}: перечитайте (GET /api/documents/{id}, MCP: get_document с full: true) и повторите. Без baseVersion правка по частям сама ложится на свежий лист (до 4 попыток), а лист целиком пишется поверх. Новые картинки должны быть картинками организации — иначе 404 «Картинка не найдена».
12. Проверка и превью
POST …/sheets/{sheetId}/check (MCP: check_sheet): без тела — сохранённый лист, {"layout": [...]} — черновик. Ответ — {version, problems} (code, severity, elementId, message); скрытые блоки пропускаются — кроме повтора id, он ищется среди всех. Текст примеряется к образцу «Иванов Иван» и к самым длинным значениям каждой колонки; код — «WWWW-WWWW-WWWW», номер — «1000/2026», мероприятие и постоянные — из документа.
| Код | Что значит → что делать | |
|---|---|---|
duplicate_id | error | Два блока с одним id → развести, сохранив лист целиком |
off_page | error | Блок целиком за листом → поправить x, y |
partly_off_page | warning | Текст, QR или ссылка частью за краем → внутрь |
outside_safe_area | warning | Текст или QR ближе 5 мм к краю → 10–15 мм |
text_overflow | error | Не влезает → больше h/w, меньше fontSize, autoFit |
unknown_field | error | Поля нет у документа → исправить имя или завести поле |
unknown_font | warning | Гарнитуры нет в наборе → раздел 6 |
missing_image | error | Нет картинки с таким fileId → загрузить в …/assets |
small_qr | warning | QR меньше 15 мм → 20–30 мм |
qr_not_square | warning | w и h QR различаются больше 0,5 мм → квадрат |
qr_hidden_without_verify | warning | Пустой QR при выключенной проверке → verifyEnabled или template |
verify_without_qr | warning | Проверка включена, а QR проверки нет ни на одном листе → добавить QR с пустым template или выключить verifyEnabled |
empty_text | warning | Ни текста, ни полей → заполнить или убрать |
overlap | warning | QR на тексте или QR; текст на тексте больше чем на 1/5 меньшего → развести |
heavy_images | warning | Фон и картинки вместе тяжелее 4 МБ — PDF не уйдёт вложением → JPEG, до 3500 px по длинной стороне |
Наложения фигур, картинок и ссылок — не ошибка: рамка под текстом обычна.
Превью — POST …/sheets/{sheetId}/preview (MCP: preview_sheet), тело {} или {"rowId": "…"}: JPEG сохранённого листа таким, каким его напечатает выпуск, 96 dpi (A4 альбомный — 1123 × 794). Данные — первая строка таблицы или образец «Иванов Иван» (прочие колонки — заголовок в скобках); код — PRVW-0000-0000, номер — 0/2026. Квоту не тратит, 30 в минуту на организацию; 503 — повторить, 422 — лист не печатается (не загрузилась картинка или шрифт). Посмотрите и строку с самым длинным ФИО.
Вся таблица — POST …/validation (MCP: validate_recipients) меряет каждый блок с полями на каждой строке; overflow там — текст, обрезанный у конкретного человека. Коды — в справочнике проверки.
13. Смена размера листа
PATCH /api/documents/{id} с pageWidthMm, pageHeightMm и resize (MCP: update_document) — для всех листов:
keep(умолчание) — блоки не трогаются и могут оказаться за краем;scale— всё пропорционально сmin(W₂/W₁, H₂/H₁), по центру: координаты, размеры (от 5 мм), кегли (от 4 pt), разрядка, отступы, рамки, обводки, кегли в разметке;reposition— размеры и кегли прежние, центр блока на той же доле листа.
Фон ляжет с обрезкой; под другие пропорции загрузите новый. Потом — проверка и превью.
14. Несколько листов
POST …/sheets — пустой лист в конец (MCP: add_sheet); DELETE …/sheets/{sheetId} — убрать (MCP: delete_sheet), единственный нельзя (404 «Нельзя удалить единственный лист документа»). Лист — следующая страница PDF; в JPEG листы идут один под другим. Размер общий, фон и блоки свои.
15. Полные примеры
Проходят схему и проверку без замечаний при заполненном мероприятии и постоянных signer_role, signer_name. fileId — заглушки, подставьте свои из …/assets. Отправляются как {"layout": [...]} (PATCH …/sheets/{sheetId}, MCP: set_layout).
Пример 1. Сертификат участника, A4 альбомный
Поля 30 мм, проверка подлинности включена, высоты — из таблицы раздела 4. Логотип по центру (297 − 23) / 2 = 137; подпись к QR под ним по центру (236 + 46/2 = 244 + 30/2); линия подписи на высоте 172 + 2/2 = 173 — у низа строк подписи, прижатых книзу.
[
{"id": "frame", "type": "shape", "name": "Рамка", "x": 8, "y": 8, "w": 281, "h": 194, "z": 0, "locked": true,
"props": {"kind": "rect", "stroke": "#8B2020", "strokeWidth": 0.8, "radius": 2}},
{"id": "frame-inner", "type": "shape", "name": "Рамка тонкая", "x": 11, "y": 11, "w": 275, "h": 188, "z": 1, "locked": true,
"props": {"kind": "rect", "stroke": "#8B2020", "strokeWidth": 0.3, "radius": 1}},
{"id": "logo", "type": "image", "name": "Логотип", "x": 137, "y": 16, "w": 23, "h": 23, "z": 2,
"props": {"fileId": "3f2a9c1e-5b7d-4e8a-9c0f-1a2b3c4d5e6f"}},
{"id": "title", "type": "text", "name": "Заголовок", "x": 30, "y": 42, "w": 237, "h": 17, "z": 3,
"props": {"text": "Сертификат", "fontFamily": "PT Serif", "fontSize": 34, "bold": true, "uppercase": true,
"letterSpacing": 3.4, "lineHeight": 1.2, "color": "#8B2020"}},
{"id": "lead", "type": "text", "name": "Подводка", "x": 30, "y": 62, "w": 237, "h": 8, "z": 4,
"props": {"text": "Настоящим удостоверяется, что", "fontFamily": "PT Serif", "fontSize": 13, "uppercase": true,
"letterSpacing": 1.3, "lineHeight": 1.3, "color": "#4A3628"}},
{"id": "name", "type": "text", "name": "ФИО", "x": 30, "y": 72, "w": 237, "h": 40, "z": 5,
"props": {"text": "%name", "fontFamily": "Playfair Display", "fontSize": 44, "bold": true,
"lineHeight": 1.15, "autoFit": true, "color": "#7A1E1E"}},
{"id": "participation", "type": "text", "name": "Участие", "x": 30, "y": 113, "w": 237, "h": 9, "z": 6,
"props": {"text": "принял|приняла участие в мероприятии", "fontFamily": "PT Serif", "fontSize": 17,
"lineHeight": 1.3, "color": "#4A3628"}},
{"id": "event", "type": "text", "name": "Мероприятие", "x": 30, "y": 123, "w": 237, "h": 19, "z": 7,
"props": {"text": "%event", "fontFamily": "PT Serif", "fontSize": 20, "bold": true, "lineHeight": 1.25,
"autoFit": true, "color": "#4A3628"}},
{"id": "event-meta", "type": "text", "name": "Место и даты", "x": 30, "y": 143, "w": 237, "h": 7, "z": 8,
"props": {"text": "%event_place, %event_date", "fontFamily": "PT Serif", "fontSize": 13, "lineHeight": 1.3,
"color": "#4A3628"}},
{"id": "signer-role", "type": "text", "name": "Должность", "x": 30, "y": 163, "w": 75, "h": 11, "z": 9,
"props": {"text": "%signer_role", "fontFamily": "PT Serif", "fontSize": 11, "align": "left",
"verticalAlign": "bottom", "lineHeight": 1.25, "autoFit": true, "color": "#4A3628"}},
{"id": "sign-line", "type": "shape", "name": "Линия подписи", "x": 110, "y": 172, "w": 45, "h": 2, "z": 10,
"props": {"kind": "line", "stroke": "#4A3628", "strokeWidth": 0.3}},
{"id": "signer-name", "type": "text", "name": "Подписант", "x": 160, "y": 163, "w": 60, "h": 11, "z": 11,
"props": {"text": "%signer_name", "fontFamily": "PT Serif", "fontSize": 11, "align": "left",
"verticalAlign": "bottom", "lineHeight": 1.25, "color": "#4A3628"}},
{"id": "issued", "type": "text", "name": "Дата и номер", "x": 30, "y": 181, "w": 190, "h": 6, "z": 12,
"props": {"text": "Выдан %date · рег. № %reg_number", "fontFamily": "PT Serif", "fontSize": 10, "align": "left",
"lineHeight": 1.3, "color": "#6B5B4E"}},
{"id": "qr", "type": "qr", "name": "QR проверки", "x": 244, "y": 151, "w": 30, "h": 30, "z": 13,
"props": {"template": "", "color": "#4A3628"}},
{"id": "qr-caption", "type": "text", "name": "Подпись к QR", "x": 236, "y": 182, "w": 46, "h": 6, "z": 14,
"props": {"text": "Проверить подлинность", "fontFamily": "PT Sans", "fontSize": 8, "lineHeight": 1.3,
"color": "#6B5B4E"}}
]«Константинопольская Александра Владимировна» встаёт при 44 pt в две строки; длиннее — ужмёт autoFit.
Пример 2. Диплом за место, A4 книжный
Рамка и герб напечатаны на фоне-бланке; поля 20 мм, в таблице есть place. Имя в дательном, место словом прописными (format: "upper"), линия подписи — тонкий залитый прямоугольник, номер и код по бокам от QR. Подписи слоёв (name) в примерах 2 и 3 опущены для краткости.
[
{"id": "title", "type": "text", "x": 20, "y": 52, "w": 170, "h": 24, "z": 2,
"props": {"text": "Диплом", "fontFamily": "Lora", "fontSize": 48, "bold": true, "uppercase": true,
"letterSpacing": 4.8, "lineHeight": 1.2, "color": "#2F4A6B"}},
{"id": "lead", "type": "text", "x": 20, "y": 84, "w": 170, "h": 9, "z": 3,
"props": {"text": "Награждается", "fontFamily": "Lora", "fontSize": 15, "uppercase": true,
"letterSpacing": 1.5, "lineHeight": 1.3, "color": "#2F4A6B"}},
{"id": "name", "type": "text", "x": 20, "y": 96, "w": 170, "h": 34, "z": 4,
"props": {"text": "%name_dat", "fontFamily": "Lora", "fontSize": 36, "bold": true, "lineHeight": 1.15,
"autoFit": true, "color": "#1F2A38"}},
{"id": "place", "type": "text", "x": 20, "y": 134, "w": 170, "h": 14, "z": 5,
"props": {"fontFamily": "Lora", "fontSize": 26, "bold": true, "lineHeight": 1.2, "color": "#2F4A6B",
"doc": {"type": "doc", "content": [{"type": "paragraph", "content": [
{"type": "text", "text": "за "},
{"type": "mergeField", "attrs": {"source": "place_word", "format": "upper"},
"marks": [{"type": "textStyle", "attrs": {"color": "#8A6A22", "letterSpacing": 2}}]},
{"type": "text", "text": " место"}]}]}}},
{"id": "event", "type": "text", "x": 20, "y": 152, "w": 170, "h": 26, "z": 6,
"props": {"text": "%event", "fontFamily": "Lora", "fontSize": 18, "lineHeight": 1.3, "autoFit": true, "color": "#33383F"}},
{"id": "event-meta", "type": "text", "x": 20, "y": 180, "w": 170, "h": 7, "z": 7,
"props": {"text": "%event_place, %event_date", "fontFamily": "Lora", "fontSize": 12, "italic": true,
"lineHeight": 1.3, "color": "#33383F"}},
{"id": "signer-role", "type": "text", "x": 20, "y": 234, "w": 70, "h": 11, "z": 8,
"props": {"text": "%signer_role", "fontFamily": "Lora", "fontSize": 11, "align": "left",
"verticalAlign": "bottom", "lineHeight": 1.25, "autoFit": true, "color": "#33383F"}},
{"id": "sign-line", "type": "shape", "x": 93, "y": 244.7, "w": 34, "h": 0.3, "z": 9,
"props": {"kind": "rect", "fill": "#33383F", "strokeWidth": 0}},
{"id": "signer-name", "type": "text", "x": 130, "y": 234, "w": 60, "h": 11, "z": 10,
"props": {"text": "%signer_name", "fontFamily": "Lora", "fontSize": 11, "align": "right",
"verticalAlign": "bottom", "lineHeight": 1.25, "color": "#33383F"}},
{"id": "reg", "type": "text", "x": 20, "y": 262, "w": 66, "h": 10, "z": 11,
"props": {"text": "Рег. № %reg_number\nот %date_long", "fontFamily": "Lora", "fontSize": 9, "align": "left",
"lineHeight": 1.3, "color": "#33383F"}},
{"id": "qr", "type": "qr", "x": 92, "y": 252, "w": 26, "h": 26, "z": 12, "props": {"template": "", "color": "#1F2A38"}},
{"id": "code", "type": "text", "x": 124, "y": 262, "w": 66, "h": 10, "z": 13,
"props": {"text": "Код проверки\n%code", "fontFamily": "Lora", "fontSize": 9, "align": "right",
"lineHeight": 1.3, "color": "#33383F"}}
]Второе место печатается «за ВТОРОЕ место»; без place останется «за место» — перед выпуском проверьте, что колонка заполнена у всех.
Пример 3. Сертификат об обучении, A4 альбомный
Montserrat и Inter, выключка влево, полоса у левого края (фигуре до края можно), ссылка поверх логотипа, часы цифрами и прописью при eventHours: "72" — «в объёме 72 ч. (семьдесят два часа)». При пустом hours останется «в объёме ч. ()»: абзац со словами не схлопывается.
[
{"id": "band", "type": "shape", "x": 0, "y": 0, "w": 14, "h": 210, "z": 0, "locked": true,
"props": {"kind": "rect", "fill": "#2C6E63", "strokeWidth": 0}},
{"id": "logo", "type": "image", "x": 30, "y": 18, "w": 40, "h": 16, "z": 1,
"props": {"fileId": "9b8a7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d"}},
{"id": "logo-link", "type": "link", "x": 30, "y": 18, "w": 40, "h": 16, "z": 2, "props": {"url": "https://edu.example.ru"}},
{"id": "title", "type": "text", "x": 30, "y": 44, "w": 200, "h": 18, "z": 3,
"props": {"text": "Сертификат", "fontFamily": "Montserrat", "fontSize": 36, "bold": true, "align": "left",
"lineHeight": 1.2, "color": "#1A1D20"}},
{"id": "subtitle", "type": "text", "x": 30, "y": 62, "w": 200, "h": 9, "z": 4,
"props": {"text": "о повышении квалификации", "fontFamily": "Montserrat", "fontSize": 16, "align": "left",
"lineHeight": 1.2, "color": "#2C6E63"}},
{"id": "lead", "type": "text", "x": 30, "y": 80, "w": 237, "h": 7, "z": 5,
"props": {"text": "Настоящий сертификат подтверждает, что", "fontFamily": "Inter", "fontSize": 12,
"align": "left", "lineHeight": 1.3, "color": "#2B2F33"}},
{"id": "name", "type": "text", "x": 30, "y": 89, "w": 237, "h": 28, "z": 6,
"props": {"text": "%name", "fontFamily": "Montserrat", "fontSize": 30, "bold": true, "align": "left",
"lineHeight": 1.15, "autoFit": true, "color": "#1A1D20"}},
{"id": "body", "type": "text", "x": 30, "y": 121, "w": 237, "h": 30, "z": 7,
"props": {"fontFamily": "Inter", "fontSize": 14, "align": "left", "verticalAlign": "top", "lineHeight": 1.35,
"autoFit": true, "color": "#2B2F33",
"doc": {"type": "doc", "content": [
{"type": "paragraph", "content": [
{"type": "text", "text": "прошёл|прошла обучение по дополнительной профессиональной программе"}]},
{"type": "paragraph", "content": [{"type": "mergeField", "attrs": {"source": "event"}, "marks": [{"type": "bold"}]}]},
{"type": "paragraph", "content": [
{"type": "text", "text": "в объёме "}, {"type": "mergeField", "attrs": {"source": "hours"}},
{"type": "text", "text": " ч. ("}, {"type": "mergeField", "attrs": {"source": "hours_word"}},
{"type": "text", "text": ")"}]}]}}},
{"id": "issued", "type": "text", "x": 30, "y": 160, "w": 120, "h": 7, "z": 8,
"props": {"text": "Дата выдачи: %date_long", "fontFamily": "Inter", "fontSize": 11, "align": "left",
"lineHeight": 1.3, "color": "#2B2F33"}},
{"id": "signer-role", "type": "text", "x": 30, "y": 175, "w": 90, "h": 11, "z": 9,
"props": {"text": "%signer_role", "fontFamily": "Inter", "fontSize": 10, "align": "left",
"verticalAlign": "bottom", "lineHeight": 1.25, "autoFit": true, "color": "#2B2F33"}},
{"id": "sign-line", "type": "shape", "x": 125, "y": 185.7, "w": 40, "h": 0.3, "z": 10,
"props": {"kind": "rect", "fill": "#2B2F33", "strokeWidth": 0}},
{"id": "signer-name", "type": "text", "x": 170, "y": 175, "w": 60, "h": 11, "z": 11,
"props": {"text": "%signer_name", "fontFamily": "Inter", "fontSize": 10, "align": "left",
"verticalAlign": "bottom", "lineHeight": 1.25, "color": "#2B2F33"}},
{"id": "reg", "type": "text", "x": 30, "y": 190, "w": 120, "h": 6, "z": 12,
"props": {"text": "Рег. № %reg_number", "fontFamily": "Inter", "fontSize": 9, "align": "left",
"lineHeight": 1.3, "color": "#6B7075"}},
{"id": "qr", "type": "qr", "x": 247, "y": 158, "w": 30, "h": 30, "z": 13,
"props": {"template": "", "color": "#1A1D20", "dotStyle": "rounded", "cornerStyle": "rounded", "cornerColor": "#2C6E63"}},
{"id": "code", "type": "text", "x": 240, "y": 189, "w": 44, "h": 6, "z": 14,
"props": {"text": "%code", "fontFamily": "Inter", "fontSize": 8, "letterSpacing": 0.5, "lineHeight": 1.3,
"color": "#6B7075"}}
]16. Рецепты правок
Тела PATCH …/sheets/{sheetId}/elements (MCP: edit_layout) для листа из примера 1.
Сдвинуть всё на 10 мм вниз — пакетного сдвига нет: прибавьте 10 к y всех блоков, кроме рамок, одним запросом. Сдвигайте всё содержимое, иначе сдвинутое наедет на оставшееся.
{"update": [{"id": "logo", "y": 26}, {"id": "title", "y": 52}, {"id": "lead", "y": 72}, {"id": "name", "y": 82},
{"id": "participation", "y": 123}, {"id": "event", "y": 133}, {"id": "event-meta", "y": 153},
{"id": "signer-role", "y": 173}, {"id": "sign-line", "y": 182}, {"id": "signer-name", "y": 173},
{"id": "issued", "y": 191}, {"id": "qr", "y": 161}, {"id": "qr-caption", "y": 192}]}Сменить шрифт везде — {"id": "title", "props": {"fontFamily": "Lora"}} для каждого текстового блока. Куски со своим textStyle.fontFamily это не меняет: перепишите их doc или замените текст строкой props.text.
Длинные ФИО — блок на всю ширину, высота на две строки, плотный межстрочный, автоподбор. Имя в дательном — "text": "%name_dat" и подводка «Награждается».
{"update": [{"id": "name", "x": 30, "w": 237, "h": 40, "props": {"fontSize": 44, "lineHeight": 1.15, "autoFit": true}}]}Текст на пёстром фоне — подложка с меньшим z, либо background и padding у блока (отступ съедает высоту — блок выше), либо белая обводка букв strokeWidth 0,2–0,4 мм.
{"add": [{"id": "name-plate", "type": "shape", "x": 26, "y": 70, "w": 245, "h": 44, "z": 4, "opacity": 0.85,
"props": {"kind": "rect", "fill": "#FFFFFF", "strokeWidth": 0, "radius": 3}}],
"update": [{"id": "event-meta", "y": 142, "h": 9, "props": {"background": "#FFF8EE", "padding": 1}}]}Два блока в ряд по колонкам раздела 4: равные y, h, verticalAlign: "bottom", левый влево, правый вправо.
{"add": [
{"id": "org-note", "type": "text", "x": 30, "y": 192, "w": 110, "h": 6,
"props": {"text": "%org", "fontFamily": "PT Serif", "fontSize": 9, "align": "left", "verticalAlign": "bottom"}},
{"id": "date-note", "type": "text", "x": 157, "y": 192, "w": 110, "h": 6,
"props": {"text": "%event_date", "fontFamily": "PT Serif", "fontSize": 9, "align": "right", "verticalAlign": "bottom"}}]}Подпись картинкой — линию заменить залитым прямоугольником (убрать и добавить тот же id), сверху PNG подписи с прозрачностью:
{"remove": ["sign-line"], "add": [
{"id": "sign-line", "type": "shape", "x": 110, "y": 173.7, "w": 45, "h": 0.3, "z": 10,
"props": {"kind": "rect", "fill": "#4A3628", "strokeWidth": 0}},
{"id": "signature", "type": "image", "x": 115, "y": 158, "w": 35, "h": 17.5, "z": 15,
"props": {"fileId": "5d6e7f80-91a2-4b3c-8d4e-5f6a7b8c9d0e"}}]}Вертикальная линия 30 мм с центром в (232; 166): линия w = 30, h = 2, повёрнутая на 90° вокруг центра, — x = 232 − 15, y = 166 − 1. И ссылка на страницу проверки поверх подписи к QR:
{"add": [
{"id": "divider", "type": "shape", "x": 217, "y": 165, "w": 30, "h": 2, "rotation": 90,
"props": {"kind": "line", "stroke": "#8B2020", "strokeWidth": 0.3}},
{"id": "qr-link", "type": "link", "x": 236, "y": 182, "w": 46, "h": 6, "z": 20, "props": {"url": "https://vruchay.ru/c/{{code}}"}}]}Мелочи: скрыть, не удаляя, — {"update": [{"id": "logo", "hidden": true}]}; поверх всех — {"update": [{"id": "qr", "z": 100}]}; начать заново — …/preset с "mode": "replace".
Оборот — POST …/sheets (MCP: add_sheet), затем блоки на новый лист:
{"add": [
{"id": "back-title", "type": "text", "x": 30, "y": 30, "w": 237, "h": 12,
"props": {"text": "Программа мероприятия", "fontFamily": "PT Serif", "fontSize": 22, "bold": true, "color": "#8B2020"}},
{"id": "back-list", "type": "text", "x": 40, "y": 50, "w": 217, "h": 60,
"props": {"fontFamily": "PT Serif", "fontSize": 13, "align": "left", "verticalAlign": "top", "lineHeight": 1.5,
"doc": {"type": "doc", "content": [{"type": "orderedList", "content": [
{"type": "listItem", "content": [{"type": "paragraph", "content": [{"type": "text", "text": "Пленарное заседание"}]}]},
{"type": "listItem", "content": [{"type": "paragraph", "content": [{"type": "text", "text": "Работа секций"}]}]}]}]}}}]}17. Особенности MCP
Инструменты повторяют REST (названы выше), с отличиями: get_document с full: true отдаёт макет в sheets[].elements; upload_image берёт картинку по ссылке, а файл с диска приносит curl по ссылке из get_upload_link; set_layout сразу возвращает замечания проверки; поля документа — в get_document (fields); проверка черновика и удаление фона — через api_request; это руководство и справочник — get_guide с темами layout и layout_reference.