APIНачало работы

Сценарии целиком

Шесть типовых задач от начала до конца: какие инструменты MCP вызывать, в каком порядке и с какими аргументами, и то же самое запросами REST. Порядок и подводные камни объяснены в AGENTS.md, вёрстка — в руководстве по вёрстке.

Как читать:

  • строка MCP — инструмент {аргументы}; строка REST — МЕТОД /путь {тело}, всегда с заголовком Authorization: Bearer vru_…;
  • <documentId>, <sheetId>, <jobId> и подобные берутся из ответов предыдущих шагов;
  • шаг «показать человеку» — не вызов, а пауза: дождитесь согласия, прежде чем тратить квоту или слать письма.

Школьная олимпиада: грамоты за места

Задача: призёрам олимпиады — грамоты «за первое (второе, третье) место», ФИО в дательном падеже, школа под именем.

Порядок важен: сначала таблица, потом раскладка. Готовая раскладка верстает строку «за %place_word место», только если у документа уже есть колонка place, а %place_word («первое») считается из неё сама.

text
get_account {}
create_project {"name": "Олимпиада по математике 2026"}
create_document {"title": "Грамота призёра — олимпиада по математике", "projectId": "<projectId>", "format": "a4", "orientation": "portrait"}
import_recipients {"documentId": "<documentId>", "columns": ["name", "email", "place", "team"], "titles": ["ФИО", "Почта", "Место", "Школа"], "rows": [["Иванова Мария Сергеевна", "ivanova@example.com", "1", "Школа № 5"], ["Петров Олег Иванович", "", "2", "Лицей № 2"]], "mode": "replace"}
apply_preset {"documentId": "<documentId>", "sheetId": "<sheetId>", "kind": "award", "style": "classic", "titlePrinted": false}
update_document {"documentId": "<documentId>", "eventName": "Городская олимпиада по математике", "eventDate": "14 ноября 2026", "constants": [{"key": "signer_role", "title": "Должность подписанта", "value": "Директор"}, {"key": "signer_name", "title": "Подписант", "value": "А. В. Смирнова"}]}
check_sheet {"documentId": "<documentId>", "sheetId": "<sheetId>"}
preview_sheet {"documentId": "<documentId>", "sheetId": "<sheetId>"}
validate_recipients {"documentId": "<documentId>"}
edit_recipients {"documentId": "<documentId>", "applySuggestions": true}   — однозначные исправления, если они есть
— показать человеку: сколько грамот выйдет и сколько останется —
start_generation {"documentId": "<documentId>", "format": "pdf"}
get_job {"jobId": "<jobId>", "waitSeconds": 60}
get_download_link {"jobId": "<jobId>", "format": "zip", "name": "%place место %name"}
text
GET   /api/auth/me
GET   /api/org/usage
POST  /api/projects {"name": "Олимпиада по математике 2026"}
POST  /api/documents {"title": "…", "projectId": "<projectId>", "format": "a4", "orientation": "portrait"}
POST  /api/documents/<documentId>/recipients/import {"columns": […], "titles": […], "rows": […], "mode": "replace"}
POST  /api/documents/<documentId>/sheets/<sheetId>/preset {"kind": "award", "style": "classic", "titlePrinted": false}
PATCH /api/documents/<documentId> {"eventName": "…", "eventDate": "…", "constants": […]}
POST  /api/documents/<documentId>/sheets/<sheetId>/check {}
POST  /api/documents/<documentId>/sheets/<sheetId>/preview {}
POST  /api/documents/<documentId>/validation {}
POST  /api/documents/<documentId>/validation/fix {"fixes": [{"rowId": "…", "column": "name", "value": "…"}]}   — suggestion из замечаний
POST  /api/documents/<documentId>/generate {"format": "pdf"}
GET   /api/jobs/<jobId>                       — раз в 2–3 секунды до done
POST  /api/jobs/<jobId>/archive-link {"format": "zip", "name": "%place место %name"}

Если грамоты печатаются на готовых бланках с напечатанным словом «Грамота» — titlePrinted: true, а бланк загрузите фоном: upload_image {"documentId": "<documentId>", "sheetId": "<sheetId>", "target": "background", "url": "https://example.ru/blank.jpg"}. Участникам без места подойдёт отдельный документ с раскладкой participant.

Протокол и бланк лежат файлами на диске, а у агента есть оболочка (Claude Code, Codex, Cursor) — вместо import_recipients и upload_image:

text
get_upload_link {"documentId": "<documentId>", "target": "recipients", "mode": "replace"}
curl -sS -F "file=@протокол.xlsx" "<ссылка из ответа>"    — в оболочке; ответ: imported, columns
get_upload_link {"documentId": "<documentId>", "target": "background", "sheetId": "<sheetId>"}
curl -sS -F "file=@бланк.jpg" "<ссылка из ответа>"        — фон сразу на листе
preview_sheet {"documentId": "<documentId>", "sheetId": "<sheetId>"}

Курс с часами: сертификат и выдача из учебной платформы

Задача: каждому, кто закончил курс, учебная платформа сама выдаёт сертификат с объёмом часов и отправляет его письмом.

Подготовка (один раз, вместе с человеком):

text
get_account {}
create_document {"title": "Сертификат — курс «Основы SQL»", "format": "a4", "preset": {"kind": "course", "style": "modern", "titlePrinted": false}}
update_document {"documentId": "<documentId>", "eventName": "«Основы SQL»", "eventHours": "72 академических часа", "constants": [{"key": "signer_role", "title": "Должность подписанта", "value": "Руководитель программы"}, {"key": "signer_name", "title": "Подписант", "value": "И. П. Орлов"}]}
check_sheet {"documentId": "<documentId>", "sheetId": "<sheetId>"}
preview_sheet {"documentId": "<documentId>", "sheetId": "<sheetId>"}
set_mail_template {"documentId": "<documentId>", "subject": "%name, ваш сертификат о прохождении курса", "bodyHtml": "<p>Здравствуйте, %name!</p><p>Поздравляем с окончанием курса. Сертификат — во вложении.</p>", "attachGeneratedFile": true}
get_document {"documentId": "<documentId>"}

Последний вызов показывает в fields.issue, какие поля принимает выдача. Раскладка course печатает «прошёл» или «прошла» по ФИО и строку «в объёме %hours» из eventHours.

Выдача — запрос платформы на каждого выпускника:

bash
curl -X POST "https://vruchay.ru/api/documents/$DOCUMENT_ID/issue" \
  -H "Authorization: Bearer $VRUCHAY_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: sql-course-enrollment-48213" \
  -d '{"data":{"name":"Иванова Анна Сергеевна"},"email":"ivanova@example.com","send":true}'

То же через MCP — для проверки на одном человеке: issue_document {"documentId": "<documentId>", "data": {"name": "Иванова Анна Сергеевна"}, "email": "ivanova@example.com", "send": true, "idempotencyKey": "sql-course-enrollment-48213"}.

  • Ключ — идентификатор записи о завершении курса: повтор после обрыва связи вернёт тот же сертификат, а не выпустит второй.
  • 201 — сохраните в карточке ученика file.code, file.verifyUrl и file.recipientUrl. 202 — опрашивайте statusUrl раз в 1–2 секунды.
  • Вебхуков нет: готовность узнаётся из ответа или опросом.
  • Предел — 120 выдач в минуту на организацию; 429 — подождать и повторить.

Конференция: сертификат участника, форма по ссылке и письма

Задача: зарегистрированным заранее — сертификаты письмом после конференции; тем, кто пришёл без регистрации, — ссылка или QR на экране, по которым сертификат забирают сами.

text
create_project {"name": "Конференция «Цифровая школа» 2026"}
create_document {"title": "Сертификат участника", "projectId": "<projectId>", "format": "a4", "preset": {"kind": "attendee", "style": "strict", "titlePrinted": false}}
update_document {"documentId": "<documentId>", "eventName": "Конференция «Цифровая школа»", "eventDate": "20–21 ноября 2026", "eventPlace": "Казань"}
check_sheet {"documentId": "<documentId>", "sheetId": "<sheetId>"}
preview_sheet {"documentId": "<documentId>", "sheetId": "<sheetId>"}
set_mail_template {"documentId": "<documentId>", "subject": "Сертификат участника конференции", "bodyHtml": "<p>%name, спасибо, что были с нами! Сертификат — во вложении.</p>"}
import_recipients {"documentId": "<documentId>", "columns": ["name", "email"], "rows": [["Соколова Елена Павловна", "sokolova@example.com"]], "mode": "replace"}
validate_recipients {"documentId": "<documentId>"}
edit_recipients {"documentId": "<documentId>", "applySuggestions": true}   — однозначные исправления, если они есть
— показать человеку: сколько сертификатов выйдет —
start_generation {"documentId": "<documentId>"}
get_job {"jobId": "<jobId>", "waitSeconds": 60}
get_mail {"documentId": "<documentId>"}
— показать человеку: сколько писем уйдёт (audience.willSend), кому нет и с какого адреса —
send_mail {"documentId": "<documentId>"}
— показать человеку настройки формы —
setup_form {"kind": "link", "name": "Конференция — без регистрации", "documentIds": ["<documentId>"], "formFields": ["name"], "singleFilePerEmail": true}
text
POST  /api/projects {"name": "…"}
POST  /api/documents {"title": "Сертификат участника", "projectId": "<projectId>", "format": "a4", "preset": {"kind": "attendee", "style": "strict", "titlePrinted": false}}
PATCH /api/documents/<documentId> {"eventName": "…", "eventDate": "…", "eventPlace": "…"}
POST  /api/mail/templates/<documentId> {"subject": "…", "bodyHtml": "…"}
POST  /api/documents/<documentId>/recipients/import {"columns": ["name", "email"], "rows": […], "mode": "replace"}
POST  /api/documents/<documentId>/generate {}
GET   /api/jobs/<jobId>
POST  /api/mailing/send {"documentIds": ["<documentId>"], "kind": "transactional", "source": "table"}
POST  /api/integrations/tilda {"kind": "link", "name": "…", "documentIds": ["<documentId>"], "formFields": ["name"]}

Ответ формы несёт connect.formUrl — ссылку https://vruchay.ru/f/…: её показывают на экране или превращают в QR. «Один документ в руки» считается по заявкам самой формы: повторная заявка с того же адреса получает уже выданный формой сертификат — после кода на этот адрес. Тому, кому сертификат ушёл письмом по списку, форма выпустит второй, — поэтому ссылку показывают тем, кто пришёл без регистрации. Каждая новая заявка тратит документ из остатка. Для формы нужны права «Полный доступ».

Новое мероприятие по прошлому шаблону

Задача: оформление прошлогодней грамоты — новому турниру, без старых получателей.

Из шаблона организации (isTemplate: true):

text
list_documents {"templates": true}
create_document {"title": "Грамота — турнир 2027", "templateId": "<templateId>", "projectId": "<projectId>"}

Из обычного документа — копией «под новое мероприятие»: копируются размер листа, листы с макетами и фоном, колонки, проект и настройки проверки; получатели, мероприятие, даты и срок — нет.

text
list_documents {"search": "турнир 2026"}
create_document {"copyOf": "<sourceId>", "title": "Грамота — турнир 2027"}
update_document {"documentId": "<newId>", "eventName": "Турнир по шахматам 2027", "eventDate": "март 2027"}
get_document {"documentId": "<newId>"}
edit_layout {"documentId": "<newId>", "sheetId": "<sheetId>", "baseVersion": 1, "update": [{"id": "<id текстового блока>", "props": {"text": "за победу в турнире"}}]}
check_sheet {"documentId": "<newId>", "sheetId": "<sheetId>"}
preview_sheet {"documentId": "<newId>", "sheetId": "<sheetId>"}
create_document {"copyOf": "<newId>", "asTemplate": true, "title": "Грамота турнира"}

REST: GET /api/documents?templates=true, POST /api/documents {"templateId": …}, POST /api/documents/{id}/duplicate, PATCH /api/documents/{id}, PATCH /api/documents/{id}/sheets/{sheetId}/elements, POST /api/documents/{id}/template.

  • baseVersion — version листа из get_document. Если лист успел поменяться, придёт 409 — перечитайте и повторите.
  • props.text заменяет текст блока целиком; поля пишутся как %name.
  • Последний вызов сохраняет копию макета шаблоном на следующий год — спросите человека, нужно ли.

Исправить ошибку в выданном документе

Задача: в грамоте опечатка в фамилии — выдать исправленную, а старую сделать недействительной.

text
search_registry {"search": "Иванова"}
get_recipients {"documentId": "<documentId>", "limit": 500}
edit_recipients {"documentId": "<documentId>", "update": [{"rowId": "<rowId>", "data": {"name": "Иванова Марина Сергеевна"}}]}
preview_sheet {"documentId": "<documentId>", "sheetId": "<sheetId>", "rowId": "<rowId>"}
— показать человеку: перевыпуск тратит документ из остатка —
reissue_documents {"fileIds": ["<fileId>"]}
get_job {"jobId": "<jobId>", "waitSeconds": 60}
search_registry {"search": "Иванова Марина"}
send_mail {"fileIds": ["<новый fileId>"]}
  • В реестре у строки есть fileId и documentId; строку таблицы находят по имени или почте в get_recipients — перевыпуск печатает по ней.
  • jobId для get_job — из ответа перевыпуска, jobs[0].jobId.
  • Перевыпуск не отзывает старый документ, а заменяет: его страница проверки показывает, где взять новый (state: "replaced", replacedBy).
  • send_mail с fileIds отправляет письмо с уже выпущенным файлом на почту из строки таблицы и квоту не тратит.

Если документ выдан по ошибке вовсе — отзыв, без замены:

text
revoke_documents {"fileIds": ["<fileId>"]}
— показать человеку: число документов и имена —
revoke_documents {"fileIds": ["<fileId>"], "expectedCount": 1, "reasonPublic": "Выдан по ошибке"}

REST — те же пути и тела: GET /api/registry?search=…, PATCH /api/documents/{id}/recipients/rows/{rowId}, POST /api/registry/reissue, POST /api/registry/resend, POST /api/registry/revoke/preview, POST /api/registry/revoke. Перевыпуск и отзыв требуют прав «Полный доступ».

Проект на сайт: несколько документов за одной формой

Задача: у школы есть сайт; на странице олимпиады — кнопки «Получить грамоту» и «Получить благодарность» для учителей. Всё выданное — в отдельном проекте, со своим реестром и цифрами.

text
create_project {"name": "Сайт школы — олимпиада"}
create_document {"title": "Диплом участника", "projectId": "<projectId>", "format": "a4", "preset": {"kind": "participant", "style": "academic", "titlePrinted": false}}
create_document {"title": "Благодарность учителю", "projectId": "<projectId>", "format": "a4", "preset": {"kind": "thanks", "style": "academic", "titlePrinted": false}}
update_document {"documentId": "<id диплома>", "eventName": "Школьная олимпиада по физике"}
update_document {"documentId": "<id благодарности>", "eventName": "Школьная олимпиада по физике"}
check_sheet {…} и preview_sheet {…} — для каждого листа
— показать человеку настройки формы —
setup_form {"kind": "site", "name": "Сайт школы", "documentIds": ["<id диплома>", "<id благодарности>"], "allowedDomains": ["school5.example.ru"]}

В ответе connect.embedHtml — HTML для каждого документа отдельно: две кнопки на одной странице или на разных. Куда вставлять на Тильде, WordPress и Битриксе — в Способах выдачи.

Дальше всё смотрят по проекту:

text
search_registry {"projectId": "<projectId>"}
api_request {"method": "GET", "path": "/api/registry/analytics", "query": {"projectId": "<projectId>"}}
api_request {"method": "GET", "path": "/api/integrations/tilda/requests", "query": {"integrationId": "<integrationId>"}}

REST: POST /api/projects, POST /api/documents (с projectId), POST /api/integrations/tilda, GET /api/registry?projectId=…, GET /api/registry/analytics?projectId=…, GET /api/integrations/tilda/requests. Для формы нужны права «Полный доступ» и возможность tilda в плане; каждая заявка тратит документ из остатка.

Вручай