{
    "openapi": "3.1.0",
    "info": {
        "title": "API prdomain.ru",
        "version": "1.0.0",
        "description": "Публичное API для модулей CMS: проверки на соответствие 152-ФЗ, скрипт согласий, журнал согласий и документы по сайту."
    },
    "servers": [
        {
            "url": "https://prdomain.ru"
        }
    ],
    "security": [
        {
            "bearerAuth": []
        }
    ],
    "components": {
        "securitySchemes": {
            "bearerAuth": {
                "type": "http",
                "scheme": "bearer",
                "description": "Токен сайта из кабинета."
            }
        },
        "schemas": {
            "Error": {
                "type": "object",
                "properties": {
                    "error": {
                        "type": "object",
                        "properties": {
                            "code": {
                                "type": "string",
                                "enum": [
                                    "unauthorized",
                                    "token_expired",
                                    "site_disabled",
                                    "api_not_in_plan",
                                    "validation_failed",
                                    "not_found",
                                    "too_many_requests",
                                    "server_error"
                                ]
                            },
                            "message": {
                                "type": "string"
                            },
                            "details": {
                                "type": "object"
                            }
                        }
                    }
                }
            }
        }
    },
    "paths": {
        "/api/v1/register": {
            "post": {
                "summary": "Подключить сайт из модуля",
                "description": "Заводит аккаунт, сайт и токен прямо из админки CMS, без кабинета. Токена у модуля ещё нет, поэтому метод работает без него, а платформа указывается заголовком `X-PrDomain-Platform: readyscript/1.0.0`. Новый e-mail — токен сразу в ответе (`201`). E-mail уже есть в базе — токена в ответе нет: на почту уходит код, и модуль обменивает его на токен методом `POST register/confirm` (`202`).",
                "tags": [
                    "Подключение модуля"
                ],
                "responses": {
                    "200": {
                        "description": "Успех",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "status": {
                                            "type": "string"
                                        },
                                        "token": {
                                            "type": "string"
                                        },
                                        "site": {
                                            "type": "object",
                                            "properties": {
                                                "domain": {
                                                    "type": "string"
                                                },
                                                "site_key": {
                                                    "type": "string"
                                                },
                                                "snippet": {
                                                    "type": "string"
                                                },
                                                "verified": {
                                                    "type": "boolean"
                                                }
                                            }
                                        },
                                        "verification": {
                                            "type": "object",
                                            "properties": {
                                                "file_name": {
                                                    "type": "string"
                                                },
                                                "file_content": {
                                                    "type": "string"
                                                },
                                                "meta_tag": {
                                                    "type": "string"
                                                }
                                            }
                                        }
                                    }
                                },
                                "example": {
                                    "status": "registered",
                                    "token": "7|Kx3vQ9…",
                                    "site": {
                                        "domain": "romashka.ru",
                                        "site_key": "abcdefgh12345678",
                                        "snippet": "<script src=\"https://cdn.prdomain.ru/c/abcdefgh12345678.js\"></script>",
                                        "verified": false
                                    },
                                    "verification": {
                                        "file_name": "prdomain-3f9a1c7e5b2d4a60.txt",
                                        "file_content": "3f9a1c7e5b2d4a60",
                                        "meta_tag": "<meta name=\"prdomain-verification\" content=\"3f9a1c7e5b2d4a60\">"
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Нет доступа",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Запрещено",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Не найдено",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "Ошибка проверки",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Слишком часто",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                },
                "security": [],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "domain": {
                                        "type": "string",
                                        "description": "Адрес сайта: example.ru или ссылка на любую его страницу"
                                    },
                                    "email": {
                                        "type": "string",
                                        "description": "Почта владельца: на неё заводится кабинет и приходит код при повторном подключении"
                                    },
                                    "accept_terms": {
                                        "type": "boolean",
                                        "description": "Согласие с офертой и на обработку персональных данных — чекбокс в модуле, не отмеченный заранее"
                                    }
                                },
                                "required": [
                                    "domain",
                                    "email",
                                    "accept_terms"
                                ]
                            }
                        }
                    }
                }
            }
        },
        "/api/v1/register/confirm": {
            "post": {
                "summary": "Подтвердить подключение кодом",
                "description": "Обменивает код из письма на токен сайта. Нужен, когда e-mail уже был в базе: переустановка модуля или потерянный токен. Прежние токены этого модуля для сайта при этом отзываются — в старой копии настроек они больше не работают.",
                "tags": [
                    "Подключение модуля"
                ],
                "responses": {
                    "200": {
                        "description": "Успех",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "status": {
                                            "type": "string"
                                        },
                                        "token": {
                                            "type": "string"
                                        },
                                        "site": {
                                            "type": "object",
                                            "properties": {
                                                "domain": {
                                                    "type": "string"
                                                },
                                                "site_key": {
                                                    "type": "string"
                                                },
                                                "snippet": {
                                                    "type": "string"
                                                },
                                                "verified": {
                                                    "type": "boolean"
                                                }
                                            }
                                        },
                                        "verification": {
                                            "type": "object",
                                            "properties": {
                                                "file_name": {
                                                    "type": "string"
                                                },
                                                "file_content": {
                                                    "type": "string"
                                                },
                                                "meta_tag": {
                                                    "type": "string"
                                                }
                                            }
                                        }
                                    }
                                },
                                "example": {
                                    "status": "registered",
                                    "token": "8|Lm4wR2…",
                                    "site": {
                                        "domain": "romashka.ru",
                                        "site_key": "abcdefgh12345678",
                                        "snippet": "<script src=\"https://cdn.prdomain.ru/c/abcdefgh12345678.js\"></script>",
                                        "verified": true
                                    },
                                    "verification": {
                                        "file_name": "prdomain-3f9a1c7e5b2d4a60.txt",
                                        "file_content": "3f9a1c7e5b2d4a60",
                                        "meta_tag": "<meta name=\"prdomain-verification\" content=\"3f9a1c7e5b2d4a60\">"
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Нет доступа",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Запрещено",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Не найдено",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "Ошибка проверки",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Слишком часто",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                },
                "security": [],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "registration_id": {
                                        "type": "string",
                                        "description": "Номер из ответа `POST register`"
                                    },
                                    "code": {
                                        "type": "string",
                                        "description": "Шесть цифр из письма"
                                    }
                                },
                                "required": [
                                    "registration_id",
                                    "code"
                                ]
                            }
                        }
                    }
                }
            }
        },
        "/api/v1/site": {
            "get": {
                "summary": "Состояние сайта",
                "description": "Что видно с нашей стороны: подтверждены ли права, стоит ли скрипт, что даёт тариф и каков итог последней проверки. Модуль спрашивает этот метод при сохранении настроек и показывает ответ администратору CMS.",
                "tags": [
                    "Сайт"
                ],
                "responses": {
                    "200": {
                        "description": "Успех",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "domain": {
                                            "type": "string"
                                        },
                                        "verified": {
                                            "type": "boolean"
                                        },
                                        "verified_at": {
                                            "type": "string"
                                        },
                                        "script": {
                                            "type": "object",
                                            "properties": {
                                                "snippet": {
                                                    "type": "string"
                                                },
                                                "config_version": {
                                                    "type": "integer"
                                                },
                                                "install_status": {
                                                    "type": "string"
                                                },
                                                "install_checked_at": {
                                                    "type": "string"
                                                }
                                            }
                                        },
                                        "plan": {
                                            "type": "object",
                                            "properties": {
                                                "name": {
                                                    "type": "string"
                                                },
                                                "api": {
                                                    "type": "boolean"
                                                },
                                                "full_report": {
                                                    "type": "boolean"
                                                },
                                                "checks_limit": {
                                                    "type": "integer"
                                                },
                                                "checks_remaining": {
                                                    "type": "integer"
                                                }
                                            }
                                        },
                                        "last_scan": {
                                            "type": "object",
                                            "properties": {
                                                "id": {
                                                    "type": "integer"
                                                },
                                                "status": {
                                                    "type": "string"
                                                },
                                                "score": {
                                                    "type": "integer"
                                                },
                                                "risk": {
                                                    "type": "string"
                                                },
                                                "finished_at": {
                                                    "type": "string"
                                                },
                                                "risks_count": {
                                                    "type": "integer"
                                                }
                                            }
                                        }
                                    }
                                },
                                "example": {
                                    "domain": "romashka.ru",
                                    "verified": true,
                                    "verified_at": "2026-10-01T12:00:00+05:00",
                                    "script": {
                                        "snippet": "<script src=\"https://cdn.prdomain.ru/c/abcdefgh12345678.js\"></script>",
                                        "config_version": 3,
                                        "install_status": "ok",
                                        "install_checked_at": "2026-10-01T12:05:00+05:00"
                                    },
                                    "plan": {
                                        "name": "Агентский",
                                        "api": true,
                                        "full_report": true,
                                        "checks_limit": 10,
                                        "checks_remaining": 7
                                    },
                                    "last_scan": {
                                        "id": 42,
                                        "status": "done",
                                        "score": 70,
                                        "risk": "medium",
                                        "finished_at": "2026-10-01T11:00:00+05:00",
                                        "risks_count": 4
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Нет доступа",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Запрещено",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Не найдено",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "Ошибка проверки",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Слишком часто",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/api/v1/scans": {
            "post": {
                "summary": "Запустить проверку",
                "description": "Ставит сайт в очередь проверки и возвращает её состояние. Если проверка этого сайта уже идёт, возвращается она же с `created: false` — повторное нажатие кнопки в админке CMS не создаёт вторую. Расходует лимит проверок тарифа.",
                "tags": [
                    "Проверки"
                ],
                "responses": {
                    "200": {
                        "description": "Успех",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "id": {
                                            "type": "integer"
                                        },
                                        "status": {
                                            "type": "string"
                                        },
                                        "score": {
                                            "nullable": true
                                        },
                                        "risk": {
                                            "nullable": true
                                        },
                                        "started_at": {
                                            "nullable": true
                                        },
                                        "finished_at": {
                                            "nullable": true
                                        },
                                        "risks_count": {
                                            "type": "integer"
                                        },
                                        "created": {
                                            "type": "boolean"
                                        }
                                    }
                                },
                                "example": {
                                    "id": 43,
                                    "status": "queued",
                                    "score": null,
                                    "risk": null,
                                    "started_at": null,
                                    "finished_at": null,
                                    "risks_count": 0,
                                    "created": true
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Нет доступа",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Запрещено",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Не найдено",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "Ошибка проверки",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Слишком часто",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            },
            "get": {
                "summary": "Последние проверки",
                "description": "До двадцати последних проверок сайта, новые сверху. Нужен, чтобы показать историю в админке CMS и понять, когда сайт проверяли в последний раз.",
                "tags": [
                    "Проверки"
                ],
                "responses": {
                    "200": {
                        "description": "Успех",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "scans": {
                                            "type": "array",
                                            "items": {
                                                "type": "object",
                                                "properties": {
                                                    "id": {
                                                        "type": "integer"
                                                    },
                                                    "status": {
                                                        "type": "string"
                                                    },
                                                    "score": {
                                                        "type": "integer"
                                                    },
                                                    "risk": {
                                                        "type": "string"
                                                    },
                                                    "started_at": {
                                                        "type": "string"
                                                    },
                                                    "finished_at": {
                                                        "type": "string"
                                                    },
                                                    "risks_count": {
                                                        "type": "integer"
                                                    }
                                                }
                                            }
                                        }
                                    }
                                },
                                "example": {
                                    "scans": [
                                        {
                                            "id": 43,
                                            "status": "done",
                                            "score": 70,
                                            "risk": "medium",
                                            "started_at": "2026-10-01T11:00:00+05:00",
                                            "finished_at": "2026-10-01T11:01:30+05:00",
                                            "risks_count": 4
                                        }
                                    ]
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Нет доступа",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Запрещено",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Не найдено",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "Ошибка проверки",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Слишком часто",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/api/v1/scans/{scan}": {
            "get": {
                "summary": "Состояние проверки",
                "description": "Состояние одной проверки. Этот метод модуль опрашивает, пока статус не станет `done` или `failed`: обычно проверка занимает около минуты. Вебхуков от нас нет.",
                "tags": [
                    "Проверки"
                ],
                "responses": {
                    "200": {
                        "description": "Успех",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "id": {
                                            "type": "integer"
                                        },
                                        "status": {
                                            "type": "string"
                                        },
                                        "score": {
                                            "nullable": true
                                        },
                                        "risk": {
                                            "nullable": true
                                        },
                                        "started_at": {
                                            "type": "string"
                                        },
                                        "finished_at": {
                                            "nullable": true
                                        },
                                        "risks_count": {
                                            "type": "integer"
                                        }
                                    }
                                },
                                "example": {
                                    "id": 43,
                                    "status": "running",
                                    "score": null,
                                    "risk": null,
                                    "started_at": "2026-10-01T11:00:00+05:00",
                                    "finished_at": null,
                                    "risks_count": 0
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Нет доступа",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Запрещено",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Не найдено",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "Ошибка проверки",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Слишком часто",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/api/v1/scans/{scan}/report": {
            "get": {
                "summary": "Результат проверки",
                "description": "Признаки риска с нормами закона и возможным штрафом. Состав зависит от тарифа и подтверждения прав: полный отчёт с доказательствами отдаётся владельцу подтверждённого сайта на тарифе с полным отчётом, иначе — открытая часть и число скрытых признаков.",
                "tags": [
                    "Проверки"
                ],
                "responses": {
                    "200": {
                        "description": "Успех",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "scan": {
                                            "type": "object",
                                            "properties": {
                                                "id": {
                                                    "type": "integer"
                                                },
                                                "status": {
                                                    "type": "string"
                                                },
                                                "score": {
                                                    "type": "integer"
                                                },
                                                "risk": {
                                                    "type": "string"
                                                },
                                                "started_at": {
                                                    "type": "string"
                                                },
                                                "finished_at": {
                                                    "type": "string"
                                                },
                                                "risks_count": {
                                                    "type": "integer"
                                                }
                                            }
                                        },
                                        "full": {
                                            "type": "boolean"
                                        },
                                        "hidden_count": {
                                            "type": "integer"
                                        },
                                        "fine_max": {
                                            "type": "integer"
                                        },
                                        "operator_form": {
                                            "type": "string"
                                        },
                                        "findings": {
                                            "type": "array",
                                            "items": {
                                                "type": "object",
                                                "properties": {
                                                    "code": {
                                                        "type": "string"
                                                    },
                                                    "group": {
                                                        "type": "string"
                                                    },
                                                    "title": {
                                                        "type": "string"
                                                    },
                                                    "summary": {
                                                        "type": "string"
                                                    },
                                                    "koap_part": {
                                                        "type": "string"
                                                    },
                                                    "weight": {
                                                        "type": "integer"
                                                    },
                                                    "evidence": {
                                                        "type": "array",
                                                        "items": {
                                                            "type": "object",
                                                            "properties": {
                                                                "label": {
                                                                    "type": "string"
                                                                },
                                                                "items": {
                                                                    "type": "array",
                                                                    "items": {
                                                                        "type": "string"
                                                                    }
                                                                },
                                                                "mono": {
                                                                    "type": "boolean"
                                                                }
                                                            }
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                },
                                "example": {
                                    "scan": {
                                        "id": 43,
                                        "status": "done",
                                        "score": 70,
                                        "risk": "medium",
                                        "started_at": "2026-10-01T11:00:00+05:00",
                                        "finished_at": "2026-10-01T11:01:30+05:00",
                                        "risks_count": 4
                                    },
                                    "full": true,
                                    "hidden_count": 0,
                                    "fine_max": 700000,
                                    "operator_form": "legal",
                                    "findings": [
                                        {
                                            "code": "trackers.before_consent",
                                            "group": "cookies",
                                            "title": "Счётчики загружаются до выбора посетителя",
                                            "summary": "До выбора посетителя загружаются сервисы аналитики или рекламы: Яндекс Метрика.",
                                            "koap_part": "2",
                                            "weight": 10,
                                            "evidence": [
                                                {
                                                    "label": "Сервисы",
                                                    "items": [
                                                        "Яндекс Метрика"
                                                    ],
                                                    "mono": false
                                                }
                                            ]
                                        }
                                    ]
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Нет доступа",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Запрещено",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Не найдено",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "Ошибка проверки",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Слишком часто",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/api/v1/verification": {
            "get": {
                "summary": "Код подтверждения прав",
                "description": "Файл и мета-тег, которыми подтверждаются права на сайт. Модуль может выложить файл сам, без участия человека, — тогда полный отчёт откроется без похода в кабинет.",
                "tags": [
                    "Права на сайт"
                ],
                "responses": {
                    "200": {
                        "description": "Успех",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "verified": {
                                            "type": "boolean"
                                        },
                                        "verified_at": {
                                            "nullable": true
                                        },
                                        "token": {
                                            "type": "string"
                                        },
                                        "file_name": {
                                            "type": "string"
                                        },
                                        "file_url": {
                                            "type": "string"
                                        },
                                        "file_content": {
                                            "type": "string"
                                        },
                                        "meta_tag": {
                                            "type": "string"
                                        }
                                    }
                                },
                                "example": {
                                    "verified": false,
                                    "verified_at": null,
                                    "token": "f3a1c7d2e9b64a0c8d5e2f1a7b3c9d4e",
                                    "file_name": "prdomain-f3a1c7d2e9b64a0c8d5e2f1a7b3c9d4e.txt",
                                    "file_url": "https://romashka.ru/prdomain-f3a1c7d2e9b64a0c8d5e2f1a7b3c9d4e.txt",
                                    "file_content": "f3a1c7d2e9b64a0c8d5e2f1a7b3c9d4e",
                                    "meta_tag": "<meta name=\"prdomain-verification\" content=\"f3a1c7d2e9b64a0c8d5e2f1a7b3c9d4e\">"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Нет доступа",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Запрещено",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Не найдено",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "Ошибка проверки",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Слишком часто",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            },
            "post": {
                "summary": "Подтвердить права",
                "description": "Проверяет, что код выложен, и подтверждает права на сайт. Нужен, чтобы открыть полный отчёт: оплата тарифа сама по себе не доказывает, что сайт ваш.",
                "tags": [
                    "Права на сайт"
                ],
                "responses": {
                    "200": {
                        "description": "Успех",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "verified": {
                                            "type": "boolean"
                                        },
                                        "verified_at": {
                                            "type": "string"
                                        }
                                    }
                                },
                                "example": {
                                    "verified": true,
                                    "verified_at": "2026-10-01T12:00:00+05:00"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Нет доступа",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Запрещено",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Не найдено",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "Ошибка проверки",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Слишком часто",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/api/v1/consent/config": {
            "get": {
                "summary": "Строка установки и настройки баннера",
                "description": "Строка скрипта согласий для вставки в шаблон сайта и текущие опубликованные настройки баннера: макет, оформление, подключённые счётчики.",
                "tags": [
                    "Скрипт согласий"
                ],
                "responses": {
                    "200": {
                        "description": "Успех",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "snippet": {
                                            "type": "string"
                                        },
                                        "version": {
                                            "type": "integer"
                                        },
                                        "published_at": {
                                            "type": "string"
                                        },
                                        "branding": {
                                            "type": "boolean"
                                        },
                                        "ui": {
                                            "type": "object",
                                            "properties": {
                                                "layout": {
                                                    "type": "string"
                                                },
                                                "preset": {
                                                    "type": "string"
                                                }
                                            }
                                        },
                                        "counters": {
                                            "type": "object",
                                            "properties": {
                                                "yandexMetrika": {
                                                    "type": "array",
                                                    "items": {
                                                        "type": "object",
                                                        "properties": {
                                                            "id": {
                                                                "type": "integer"
                                                            }
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                },
                                "example": {
                                    "snippet": "<script src=\"https://cdn.prdomain.ru/c/abcdefgh12345678.js\"></script>",
                                    "version": 3,
                                    "published_at": "2026-10-01T12:00:00+05:00",
                                    "branding": false,
                                    "ui": {
                                        "layout": "bar",
                                        "preset": "minimal"
                                    },
                                    "counters": {
                                        "yandexMetrika": [
                                            {
                                                "id": 12345678
                                            }
                                        ]
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Нет доступа",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Запрещено",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Не найдено",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "Ошибка проверки",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Слишком часто",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            },
            "put": {
                "summary": "Опубликовать баннер",
                "description": "Меняет оформление баннера и номера счётчиков. Новая версия появляется на сайте в течение пяти минут. Номера счётчиков проходят строгую проверку: в конфиг попадает только идентификатор, произвольный код через API подсунуть нельзя.",
                "tags": [
                    "Скрипт согласий"
                ],
                "responses": {
                    "200": {
                        "description": "Успех",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "version": {
                                            "type": "integer"
                                        },
                                        "applied_in": {
                                            "type": "string"
                                        }
                                    }
                                },
                                "example": {
                                    "version": 4,
                                    "applied_in": "до 5 минут"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Нет доступа",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Запрещено",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Не найдено",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "Ошибка проверки",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Слишком часто",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                },
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "ui": {
                                        "type": "object",
                                        "description": "Оформление: layout (bar, corner, modal), preset"
                                    },
                                    "counters": {
                                        "type": "object",
                                        "description": "Номера счётчиков: metrika, tmr, vk, ytm, rambler, liveinternet, gtm, ga, tiktok"
                                    }
                                },
                                "required": []
                            }
                        }
                    }
                }
            }
        },
        "/api/v1/consent/install-check": {
            "post": {
                "summary": "Проверить установку",
                "description": "Загружает главную страницу сайта и смотрит: стоит ли наш скрипт первым, нет ли второго баннера согласий и трекеров прямо в разметке. Найденный скрипт с ключом сайта подтверждает права автоматически.",
                "tags": [
                    "Скрипт согласий"
                ],
                "responses": {
                    "200": {
                        "description": "Успех",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "status": {
                                            "type": "string"
                                        },
                                        "verified": {
                                            "type": "boolean"
                                        },
                                        "checks": {
                                            "type": "array",
                                            "items": {
                                                "type": "object",
                                                "properties": {
                                                    "ok": {
                                                        "type": "boolean"
                                                    },
                                                    "title": {
                                                        "type": "string"
                                                    },
                                                    "text": {
                                                        "type": "string"
                                                    }
                                                }
                                            }
                                        }
                                    }
                                },
                                "example": {
                                    "status": "ok",
                                    "verified": true,
                                    "checks": [
                                        {
                                            "ok": true,
                                            "title": "Стоит первым и без async",
                                            "text": "Скрипт успевает поставить блокировку раньше счётчиков."
                                        }
                                    ]
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Нет доступа",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Запрещено",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Не найдено",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "Ошибка проверки",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Слишком часто",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/api/v1/consents": {
            "get": {
                "summary": "Записи журнала",
                "description": "Журнал согласий сайта, новые сверху, с подсчётом решений за выбранный период. Страницы листаются курсором `before`, а не номером: журнал пополняется во время выгрузки, и нумерованные страницы при этом повторяли бы одни записи и теряли другие.",
                "tags": [
                    "Журнал согласий"
                ],
                "responses": {
                    "200": {
                        "description": "Успех",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "consents": {
                                            "type": "array",
                                            "items": {
                                                "type": "object",
                                                "properties": {
                                                    "id": {
                                                        "type": "integer"
                                                    },
                                                    "consent_id": {
                                                        "type": "string"
                                                    },
                                                    "action": {
                                                        "type": "string"
                                                    },
                                                    "categories": {
                                                        "type": "array",
                                                        "items": {
                                                            "type": "string"
                                                        }
                                                    },
                                                    "text_version": {
                                                        "type": "string"
                                                    },
                                                    "text_hash": {
                                                        "nullable": true
                                                    },
                                                    "source": {
                                                        "type": "string"
                                                    },
                                                    "page": {
                                                        "type": "string"
                                                    },
                                                    "ip": {
                                                        "type": "string"
                                                    },
                                                    "created_at": {
                                                        "type": "string"
                                                    }
                                                }
                                            }
                                        },
                                        "next_before": {
                                            "type": "integer"
                                        },
                                        "summary": {
                                            "type": "object",
                                            "properties": {
                                                "total": {
                                                    "type": "integer"
                                                },
                                                "accept_all": {
                                                    "type": "integer"
                                                },
                                                "custom": {
                                                    "type": "integer"
                                                },
                                                "reject_all": {
                                                    "type": "integer"
                                                }
                                            }
                                        }
                                    }
                                },
                                "example": {
                                    "consents": [
                                        {
                                            "id": 9120,
                                            "consent_id": "k3m9p2q7x1",
                                            "action": "custom",
                                            "categories": [
                                                "analytics"
                                            ],
                                            "text_version": "2026-10",
                                            "text_hash": null,
                                            "source": "banner",
                                            "page": "https://romashka.ru/catalog",
                                            "ip": "95.165.12.0",
                                            "created_at": "2026-10-01T12:00:00+05:00"
                                        }
                                    ],
                                    "next_before": 9120,
                                    "summary": {
                                        "total": 1840,
                                        "accept_all": 1510,
                                        "custom": 210,
                                        "reject_all": 120
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Нет доступа",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Запрещено",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Не найдено",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "Ошибка проверки",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Слишком часто",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "from",
                        "in": "query",
                        "required": false,
                        "description": "С какого времени (ISO 8601)",
                        "schema": {
                            "type": "date"
                        }
                    },
                    {
                        "name": "to",
                        "in": "query",
                        "required": false,
                        "description": "До какого времени",
                        "schema": {
                            "type": "date"
                        }
                    },
                    {
                        "name": "decision",
                        "in": "query",
                        "required": false,
                        "description": "all — согласен со всем, partial — выбрал сам, reject — отказался",
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "name": "source",
                        "in": "query",
                        "required": false,
                        "description": "banner — наш баннер, api — форма CMS",
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "name": "limit",
                        "in": "query",
                        "required": false,
                        "description": "Сколько записей, до 500; по умолчанию 100",
                        "schema": {
                            "type": "int"
                        }
                    },
                    {
                        "name": "before",
                        "in": "query",
                        "required": false,
                        "description": "Записи старше этого номера — из `next_before` прошлого ответа",
                        "schema": {
                            "type": "int"
                        }
                    }
                ]
            },
            "post": {
                "summary": "Записать согласие",
                "description": "Записывает согласие, которое взяла форма CMS, а не наш баннер: форма заказа, обратный звонок, корзина. Иначе такие согласия остаются в базе магазина, где нет ни версии текста, ни его хеша, и доказывать их нечем. В журнале они помечены источником `api`.",
                "tags": [
                    "Журнал согласий"
                ],
                "responses": {
                    "200": {
                        "description": "Успех",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "id": {
                                            "type": "integer"
                                        },
                                        "created_at": {
                                            "type": "string"
                                        }
                                    }
                                },
                                "example": {
                                    "id": 9121,
                                    "created_at": "2026-10-01T12:00:00+05:00"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Нет доступа",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Запрещено",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Не найдено",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "Ошибка проверки",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Слишком часто",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                },
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "consent_id": {
                                        "type": "string",
                                        "description": "Номер согласия: 8–32 знака, строчные буквы и цифры. Его же модуль хранит у заказа"
                                    },
                                    "action": {
                                        "type": "string",
                                        "description": "accept_all, reject_all или custom"
                                    },
                                    "categories": {
                                        "type": "array",
                                        "description": "analytics, advertising, functional; при accept_all и reject_all можно не передавать"
                                    },
                                    "text_version": {
                                        "type": "string",
                                        "description": "Версия текста согласия, который видел посетитель"
                                    },
                                    "text_hash": {
                                        "type": "string",
                                        "description": "SHA-256 текста согласия — им доказывается, что текст не менялся"
                                    },
                                    "page": {
                                        "type": "string",
                                        "description": "Адрес страницы; параметры запроса отбрасываются"
                                    },
                                    "ip": {
                                        "type": "string",
                                        "description": "IP посетителя — мы усекаем его сами"
                                    },
                                    "user_agent": {
                                        "type": "string",
                                        "description": "Браузер посетителя"
                                    },
                                    "created_at": {
                                        "type": "date",
                                        "description": "Время события, если согласие записывают не сразу"
                                    }
                                },
                                "required": []
                            }
                        }
                    }
                }
            }
        },
        "/api/v1/consents.csv": {
            "get": {
                "summary": "Выгрузка журнала",
                "description": "Тот же журнал файлом CSV — для ответа на запрос Роскомнадзора. Отбор задаётся теми же параметрами, что у списка. Разделитель — точка с запятой, в начале BOM: файл открывается в Excel без настройки кодировки.",
                "tags": [
                    "Журнал согласий"
                ],
                "responses": {
                    "200": {
                        "description": "Файл",
                        "content": {
                            "text/csv": {
                                "schema": {
                                    "type": "string",
                                    "format": "binary"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Нет доступа",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Запрещено",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Не найдено",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "Ошибка проверки",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Слишком часто",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "from",
                        "in": "query",
                        "required": false,
                        "description": "С какого времени",
                        "schema": {
                            "type": "date"
                        }
                    },
                    {
                        "name": "to",
                        "in": "query",
                        "required": false,
                        "description": "До какого времени",
                        "schema": {
                            "type": "date"
                        }
                    },
                    {
                        "name": "decision",
                        "in": "query",
                        "required": false,
                        "description": "all, partial или reject",
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "name": "source",
                        "in": "query",
                        "required": false,
                        "description": "banner или api",
                        "schema": {
                            "type": "string"
                        }
                    }
                ]
            }
        },
        "/api/v1/documents": {
            "get": {
                "summary": "Документы сайта",
                "description": "Состояние анкеты и четыре документа: политика, cookie-политика, согласие для форм и уведомление в Роскомнадзор. По `published_url` видно, что уже лежит на самом сайте — его находит проверка, и создавать страницу заново модулю не нужно.",
                "tags": [
                    "Документы"
                ],
                "responses": {
                    "200": {
                        "description": "Успех",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "questionnaire": {
                                            "type": "object",
                                            "properties": {
                                                "filled": {
                                                    "type": "boolean"
                                                },
                                                "complete": {
                                                    "type": "boolean"
                                                },
                                                "missing": {
                                                    "type": "array",
                                                    "items": {
                                                        "type": "object"
                                                    }
                                                },
                                                "revision": {
                                                    "type": "integer"
                                                },
                                                "updated_at": {
                                                    "type": "string"
                                                }
                                            }
                                        },
                                        "documents": {
                                            "type": "array",
                                            "items": {
                                                "type": "object",
                                                "properties": {
                                                    "slug": {
                                                        "type": "string"
                                                    },
                                                    "title": {
                                                        "type": "string"
                                                    },
                                                    "ready": {
                                                        "type": "boolean"
                                                    },
                                                    "published_url": {
                                                        "type": "string"
                                                    },
                                                    "checkable": {
                                                        "type": "boolean"
                                                    }
                                                }
                                            }
                                        }
                                    }
                                },
                                "example": {
                                    "questionnaire": {
                                        "filled": true,
                                        "complete": true,
                                        "missing": [],
                                        "revision": 2,
                                        "updated_at": "2026-10-01T12:00:00+05:00"
                                    },
                                    "documents": [
                                        {
                                            "slug": "policy-generator",
                                            "title": "Политика обработки персональных данных",
                                            "ready": true,
                                            "published_url": "https://romashka.ru/policy",
                                            "checkable": true
                                        }
                                    ]
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Нет доступа",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Запрещено",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Не найдено",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "Ошибка проверки",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Слишком часто",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/api/v1/documents/questionnaire": {
            "get": {
                "summary": "Анкета и ответы",
                "description": "Вопросы анкеты с типами и вариантами — по ним модуль рисует форму в админке CMS, и при изменении перечня вопросов переписывать модуль не придётся. Вместе с ними текущие ответы: если анкету ещё не заполняли, отдаётся заготовка по данным последней проверки.",
                "tags": [
                    "Документы"
                ],
                "responses": {
                    "200": {
                        "description": "Успех",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "steps": {
                                            "type": "array",
                                            "items": {
                                                "type": "object",
                                                "properties": {
                                                    "title": {
                                                        "type": "string"
                                                    },
                                                    "hint": {
                                                        "type": "string"
                                                    },
                                                    "fields": {
                                                        "type": "array",
                                                        "items": {
                                                            "type": "object",
                                                            "properties": {
                                                                "name": {
                                                                    "type": "string"
                                                                },
                                                                "label": {
                                                                    "type": "string"
                                                                },
                                                                "type": {
                                                                    "type": "string"
                                                                },
                                                                "required": {
                                                                    "type": "boolean"
                                                                }
                                                            }
                                                        }
                                                    }
                                                }
                                            }
                                        },
                                        "answers": {
                                            "type": "object",
                                            "properties": {
                                                "operator_name": {
                                                    "type": "string"
                                                },
                                                "site_url": {
                                                    "type": "string"
                                                }
                                            }
                                        },
                                        "required": {
                                            "type": "array",
                                            "items": {
                                                "type": "string"
                                            }
                                        },
                                        "missing": {
                                            "type": "array",
                                            "items": {
                                                "type": "string"
                                            }
                                        }
                                    }
                                },
                                "example": {
                                    "steps": [
                                        {
                                            "title": "Оператор",
                                            "hint": "Эти сведения войдут почти во все документы.",
                                            "fields": [
                                                {
                                                    "name": "operator_name",
                                                    "label": "Наименование или ФИО",
                                                    "type": "text",
                                                    "required": true
                                                }
                                            ]
                                        }
                                    ],
                                    "answers": {
                                        "operator_name": "ООО «Ромашка»",
                                        "site_url": "https://romashka.ru"
                                    },
                                    "required": [
                                        "operator_name",
                                        "operator_inn",
                                        "operator_address",
                                        "site_url",
                                        "contact_email"
                                    ],
                                    "missing": [
                                        "operator_inn"
                                    ]
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Нет доступа",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Запрещено",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Не найдено",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "Ошибка проверки",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Слишком часто",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            },
            "put": {
                "summary": "Сохранить анкету",
                "description": "Сохраняет ответы анкеты. Ответы заменяются целиком: присылать часть анкеты и догадываться, что стало с остальным, — верный способ получить документ с прошлогодними реквизитами. Незнакомые поля отбрасываются.",
                "tags": [
                    "Документы"
                ],
                "responses": {
                    "200": {
                        "description": "Успех",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "saved": {
                                            "type": "boolean"
                                        },
                                        "revision": {
                                            "type": "integer"
                                        },
                                        "missing": {
                                            "type": "array",
                                            "items": {
                                                "type": "object"
                                            }
                                        }
                                    }
                                },
                                "example": {
                                    "saved": true,
                                    "revision": 3,
                                    "missing": []
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Нет доступа",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Запрещено",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Не найдено",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "Ошибка проверки",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Слишком часто",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                },
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "answers": {
                                        "type": "object",
                                        "description": "Ответы по именам полей из GET documents/questionnaire"
                                    }
                                },
                                "required": []
                            }
                        }
                    }
                }
            }
        },
        "/api/v1/documents/{slug}": {
            "get": {
                "summary": "Текст документа",
                "description": "Готовый документ разметкой — её модуль кладёт в страницу CMS. Без стилей: страницу клиент оформит своим шаблоном. Тот же текст, что в кабинете и в скачанном файле.",
                "tags": [
                    "Документы"
                ],
                "responses": {
                    "200": {
                        "description": "Успех",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "slug": {
                                            "type": "string"
                                        },
                                        "title": {
                                            "type": "string"
                                        },
                                        "filename": {
                                            "type": "string"
                                        },
                                        "revision": {
                                            "type": "integer"
                                        },
                                        "revised_at": {
                                            "type": "string"
                                        },
                                        "html": {
                                            "type": "string"
                                        }
                                    }
                                },
                                "example": {
                                    "slug": "policy-generator",
                                    "title": "Политика обработки персональных данных",
                                    "filename": "politika-obrabotki-pdn",
                                    "revision": 2,
                                    "revised_at": "2026-10-01T12:00:00+05:00",
                                    "html": "<h1>Политика обработки персональных данных</h1><p>…</p>"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Нет доступа",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Запрещено",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Не найдено",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "Ошибка проверки",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Слишком часто",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/api/v1/documents/{slug}/file": {
            "get": {
                "summary": "Документ файлом",
                "description": "Тот же документ файлом DOCX или PDF — когда он нужен на подпись. Файлы не хранятся: документ собирается из ответов анкеты при каждом обращении.",
                "tags": [
                    "Документы"
                ],
                "responses": {
                    "200": {
                        "description": "Файл",
                        "content": {
                            "application/octet-stream": {
                                "schema": {
                                    "type": "string",
                                    "format": "binary"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Нет доступа",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Запрещено",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Не найдено",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "Ошибка проверки",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Слишком часто",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "format",
                        "in": "query",
                        "required": false,
                        "description": "docx (по умолчанию) или pdf",
                        "schema": {
                            "type": "string"
                        }
                    }
                ]
            }
        }
    }
}