# Документация API НОРМОСКАНА

Загрузите комплект проектной документации через API. Получите замечания в JSON. Скачайте готовые отчёты в PDF.

Обновлено: 8 октября 2026. Версия API: v1.

Базовый адрес: `https://xn--80atfddcbol.xn--p1ai/api/v1`

- [Руководство на сайте](https://нормоскан.рф/api)
- [OpenAPI JSON](https://нормоскан.рф/api/v1/openapi.json)
- [Программа на Python](https://нормоскан.рф/api/examples/check.py)
- [Программа на PowerShell](https://нормоскан.рф/api/examples/check.ps1)

## Быстрый старт

Используйте ключ доступа организации. Этот ключ также открывает кабинет. Вы получаете его при подключении. Передавайте ключ в заголовке Authorization: Bearer <ключ>. API не требует отдельного входа или cookies.

> Базовый адрес содержит /api/v1. Например, GET /me означает GET https://нормоскан.рф/api/v1/me. Латинское имя домена в примерах обозначает тот же сайт.

1. Проверьте ключ запросом GET /me. Этот запрос не запускает проверку документов.
2. Получите лимиты организации через GET /limits.
3. Отправьте файлы одного комплекта через POST /runs.
4. Сохраните run_id из ответа. Это идентификатор принятой проверки.
5. Запрашивайте GET /runs/{run_id} раз в 5 секунд до статуса done или failed.
6. Если status равен failed, прочитайте error и refunded.
7. Если status равен done, получите замечания через GET /runs/{run_id}/findings.
8. Скачайте отчёт через GET /runs/{run_id}/report.pdf.

**Bash / macOS / Linux — подготовка и проверка ключа**

```bash
export NK_BASE="https://xn--80atfddcbol.xn--p1ai/api/v1"
export NK_KEY="ВАШ_КЛЮЧ_ДОСТУПА"

curl --fail-with-body "$NK_BASE/me" \
  -H "Authorization: Bearer $NK_KEY"
curl --fail-with-body "$NK_BASE/limits" \
  -H "Authorization: Bearer $NK_KEY"
```

Замените ВАШ_КЛЮЧ_ДОСТУПА своим ключом. Примеры содержат вымышленные числа, организации и идентификаторы. В рабочих запросах используйте значения из ответов своего API.

**Windows PowerShell — проверка ключа**

```powershell
$env:NK_KEY = "ВАШ_КЛЮЧ_ДОСТУПА"
$base = "https://xn--80atfddcbol.xn--p1ai/api/v1"
$headers = @{ Authorization = "Bearer $env:NK_KEY" }
Invoke-RestMethod -Uri "$base/me" -Headers $headers
Invoke-RestMethod -Uri "$base/limits" -Headers $headers
```

На странице «Обзор API» доступны программы check.py и check.ps1. Они загружают комплект и сохраняют run_id. После проверки программы скачивают JSON, Markdown, общий PDF и доступные PDF по разделам. Скачайте программу в рабочую папку. Задайте свой ключ в NK_KEY.

**Запуск скачанного примера Python**

```bash
python -m pip install httpx
python check.py AR.pdf EOM.pdf --idempotency-key object-001-v1

# Продолжить получение уже принятой проверки без новой загрузки:
python check.py --run-id 9f1c3b7a-2d4e-4c8b-9a1f-6e5d4c3b2a10
```

**Запуск скачанного примера PowerShell**

```powershell
.\check.ps1 -Files .\AR.pdf,.\EOM.pdf -IdempotencyKey object-001-v1

# Продолжить получение результата:
.\check.ps1 -RunId 9f1c3b7a-2d4e-4c8b-9a1f-6e5d4c3b2a10
```

## Авторизация

Передавайте ключ организации в заголовке Authorization: Bearer <ключ>. Используйте ключ, который вы получили при подключении. API не требует отдельного входа, cookies или вызова POST /session/entry.

**Проверить доступ**

```bash
curl --fail-with-body "$NK_BASE/me" \
  -H "Authorization: Bearer $NK_KEY"
```

> Храните ключ в переменной окружения на своём сервере. Не добавляйте ключ в браузерный JavaScript, URL или репозиторий. Документация не вставляет полный ключ в примеры.

| Ответ | Действие |
| --- | --- |
| 200 | Ключ принят. Используйте сведения об организации и её доступе. |
| 401 | Проверьте ключ и схему Authorization: Bearer. |
| 403 | Доступ организации приостановлен. Обратитесь в поддержку. |

## Загрузка комплекта и задание клиента

POST /runs принимает multipart/form-data. Передавайте каждый файл отдельным полем files. Можно загрузить архив ZIP, RAR или 7z. curl или HTTP-библиотека задаёт Content-Type и границу multipart. Не задавайте этот заголовок вручную.

| Поле | Обязательно | Что передать |
| --- | --- | --- |
| files | Да | Документы или архивы. Для каждого файла повторите поле files. |
| object_address | Нет | Адрес объекта. Допустимая длина указана в справочнике POST /runs. |
| user_request | Нет | Задание проверяющему. Сервер сохраняет его с комплектом. Допустимая длина указана в справочнике POST /runs. |
| kinds | Нет | JSON-строка с именами файлов и кодами разделов. Пример: {"AR.pdf":"АР"}. Получите справочник через GET /docset. Получите профиль организации через GET /me. |
| bundle_id | Нет | bundle_id из ответа POST /bundles. Он связывает проверки одного проекта в истории. |
| roles | Нет | JSON-строка с точными путями документов и ролями context или review. При разделении ролей укажите каждый документ. Назначьте хотя бы один review. |

**Отправка двух файлов**

```bash
curl --fail-with-body "$NK_BASE/runs" \
  -H "Authorization: Bearer $NK_KEY" \
  -H "Idempotency-Key: object-001-v1" \
  -F "files=@AR.pdf" \
  -F "files=@EOM.pdf" \
  --form-string "object_address=г. Москва, ул. Примерная, д. 1" \
  --form-string "user_request=Сопоставьте нагрузки ЭОМ с архитектурными решениями." \
  --form-string 'kinds={"AR.pdf":"АР"}' \
  --output accepted.json
```

**Пример ответа 201 — новый комплект принят**

```json
{
  "run_id": "9f1c3b7a-2d4e-4c8b-9a1f-6e5d4c3b2a10",
  "status": "queued",
  "checks_left": 11,
  "double_volume": false,
  "volume_units": 1,
  "user_request": "Сопоставьте нагрузки ЭОМ с архитектурными решениями.",
  "bundle": null,
  "object": {"address": "г. Москва, ул. Примерная, д. 1", "works": null},
  "documents": [
    {"index": 1, "name": "AR.pdf", "kind": "АР", "kind_chosen": true,
     "kind_guess": null, "pages": null, "converted": false, "page_from": null, "page_to": null},
    {"index": 2, "name": "EOM.pdf", "kind": null, "kind_chosen": false,
     "kind_guess": null, "pages": null, "converted": false, "page_from": null, "page_to": null}
  ]
}
```

Ответ 201 означает, что сервер принял комплект. Проверка ещё не завершена. Повтор того же запроса с прежним Idempotency-Key может вернуть 200 и прежний run_id. После приёма status обычно равен queued или running. Сервер запускает обработку автоматически.

Для длинного задания используйте файл UTF-8: -F "user_request=<task.txt". kinds задаёт раздел документа. user_request содержит задание проверяющему. roles задаёт роль: context для справочного документа или review для проверяемого документа. Указывайте точные пути файлов в roles.

Сервер также распознаёт папки Context и Reviews в архиве. Роль из roles имеет приоритет над именем папки.

Основные форматы: PDF, Word, Excel, JPEG, PNG и XML. Сервер распаковывает архивы ZIP, RAR и 7z при приёме. После загрузки проверьте documents.

После проверки прочитайте ограничения в отчёте.

## Очередь и готовность результата

Пример использует Bash и утилиту jq для чтения JSON. accepted.json — файл ответа, сохранённый при загрузке комплекта.

**Узнать состояние**

```bash
RUN_ID="$(jq -er '.run_id' accepted.json)"

curl --fail-with-body "$NK_BASE/runs/$RUN_ID" \
  -H "Authorization: Bearer $NK_KEY"
```

**Фрагмент ответа во время проверки**

```json
{
  "status": "running",
  "stage": "analyzing",
  "stage_text": "Проверяем",
  "progress": {
    "percent": 34, "pages_total": 120, "pages_done": 120,
    "elapsed_s": 95, "eta_s": null
  }
}
```

| status | Что происходит | Ваше действие |
| --- | --- | --- |
| queued | Комплект принят и ожидает запуска. | Продолжайте опрос сохранённого run_id. |
| running | Идёт обработка или подготовка публикации. | Показывайте stage_text. Продолжайте опрос. |
| done | Проверка завершена, результат опубликован. | Получите JSON и PDF. |
| failed | Проверка не завершилась успешно. | Покажите error и refunded. Перед новой загрузкой устраните причину ошибки. |

stage содержит код этапа. stage_text содержит его название для человека. progress содержит percent, pages_total, pages_done и оценку eta_s. null означает, что данных ещё нет. Определяйте готовность по status.

Процент и оценка времени могут измениться.

Время зависит от объёма, форматов документов и очереди. Число одновременных проверок зависит от подключения. Если возникла сетевая ошибка, продолжайте опрос того же run_id с паузами 5–30 секунд. Закрытие браузера не отменяет проверку. Остановка вашей программы также не отменяет проверку.

GET /runs?limit=50 возвращает последние проверки. Новые проверки идут первыми. Допустимые значения limit: от 1 до 200. Для полной истории сохраняйте run_id в своей программе.

Если проверка ещё в очереди, отмените её через POST /runs/{run_id}/cancel. Если обработка уже началась или завершилась, сервер вернёт 409 review_already_started. Отмена сохраняет историю проверки.

**Снять проверку с очереди**

```bash
curl --fail-with-body -X POST "$NK_BASE/runs/$RUN_ID/cancel" \
  -H "Authorization: Bearer $NK_KEY"
```

## Архивы и выбор разделов

Получите список файлов архива через POST /imports/preview. Этот запрос не создаёт проверку и не списывает проверку со счёта. Выберите раздел для каждого документа из списка.

**Посмотреть содержимое архива**

```bash
curl --fail-with-body "$NK_BASE/imports/preview" \
  -H "Authorization: Bearer $NK_KEY" \
  -F "file=@project.zip"
```

| Поле | Значение |
| --- | --- |
| documents[].name | Точный путь документа в архиве. Используйте его как ключ в kinds и roles. |
| documents[].size | Размер документа в байтах. |
| documents[].kind_guess | Предполагаемый раздел. null означает, что сервер не определил раздел. |
| skipped | Файлы, пропущенные при распаковке. |

После выбора разделов отправьте исходный архив через POST /runs. Передайте выбранные коды в kinds. Предварительный просмотр не загружает комплект на проверку. Получите допустимые коды через GET /docset. Получите лимиты через GET /limits.

## JSON, PDF и уточнения

**Получить результат после done**

```bash
curl --fail-with-body "$NK_BASE/runs/$RUN_ID/findings" \
  -H "Authorization: Bearer $NK_KEY" --output findings.json
curl --fail-with-body "$NK_BASE/runs/$RUN_ID/report.pdf" \
  -H "Authorization: Bearer $NK_KEY" --output report.pdf
curl --fail-with-body "$NK_BASE/runs/$RUN_ID/report" \
  -H "Authorization: Bearer $NK_KEY" --output report.md
```

| Поле ответа /findings | Что означает |
| --- | --- |
| findings | Массив замечаний. Для записи решения используйте finding_id. n обозначает номер замечания в отчёте. |
| questions | Массив вопросов. Они не входят в counts.total. Старый ответ может не содержать это поле. |
| counts.total | Число замечаний. Другие ключи counts показывают число замечаний каждого уровня важности. |
| available_sections | Доступные отчёты по разделам. code содержит код раздела. findings содержит число замечаний. Старый отчёт или другое подключение может не содержать это поле. |
| parsed | true означает, что сервер разобрал отчёт на поля. Если parsed равен false, покажите raw_markdown или скачайте /report. |
| warnings / reference_problems | Ограничения проверки и ошибки ссылок. Покажите их специалисту вместе с замечаниями. |
| feedback_snapshot / feedback_actor / feedback | Версия результата, автор интеграции и сохранённые решения по замечаниям. |
| usage | Расход обработки. Ноль не означает бесплатную проверку. Стоимость для клиента определяется тарифом. |

**Пример одного элемента findings**

```json
{
  "n": 7,
  "finding_id": "16f2c114-4904-4cb3-8a92-b61f5e6a583a",
  "status": "Замечание",
  "importance": "Существенная",
  "section": "ЭОМ",
  "essence": "Расчётная мощность щита различается в двух документах.",
  "evidence_text": "EOM.pdf, стр. 12: 12 кВт; AR.pdf, стр. 4: 15 кВт.",
  "action": "Согласовать нагрузку и исправить связанный документ.",
  "pages": [12, 24],
  "page_refs": [
    {"page": 12, "in_docset": true, "kind": "ЭОМ", "doc_index": 1, "doc_page": 12, "label": "ЭОМ, стр. 12"},
    {"page": 24, "in_docset": true, "kind": "АР", "doc_index": 2, "doc_page": 4, "label": "АР, стр. 4"}
  ],
  "sources": []
}
```

essence содержит суть замечания. evidence_text содержит доказательство и ссылки. action содержит предлагаемое действие. section обозначает раздел документации. album и stage могут указывать том и стадию.

topic обозначает тематическую группу, а не раздел документации.

Для подписи страницы используйте page_refs[].label. doc_page обозначает страницу внутри документа. page обозначает сквозную страницу комплекта. Если page_refs пуст, используйте evidence_text. status внутри замечания отличается от status проверки.

Набор текстовых значений status и importance может расширяться.

## Отчёты по разделам

Получите код раздела из available_sections[].code в ответе /findings. Например: АР, КР, СС или ЭОМ. Используйте только коды этого отчёта. Не определяйте раздел по тексту замечания.

**Пример списка доступных отчётов**

```json
{"available_sections": [{"code": "АР", "findings": 15}, {"code": "ЭОМ", "findings": 8}]}
```

**Скачать PDF ЭОМ с кодированием русского параметра**

```bash
# Сначала найдите нужный code в findings.json → available_sections.
curl --fail-with-body --get "$NK_BASE/runs/$RUN_ID/report.pdf" \
  -H "Authorization: Bearer $NK_KEY" \
  --data-urlencode "section=ЭОМ" \
  --output report-EOM.pdf
```

Отдельный отчёт сохраняет номера замечаний из общего отчёта. Вопросы этого раздела идут отдельно. Замечание по нескольким разделам может попасть в каждый соответствующий отчёт. Поэтому сумма счётчиков отдельных отчётов может превышать counts.total.

> Если available_sections отсутствует или пуст, используйте общий PDF. API не подтвердил отдельные отчёты. Ошибка 409 section_unavailable означает, что результат не содержит привязки к нужному разделу. Повтор запроса не добавляет эту привязку. Новая платная проверка также не гарантирует её появление.

| Параметр | Назначение |
| --- | --- |
| section=ЭОМ | PDF одного раздела. Возьмите код из available_sections. |
| theme=engineering | Тематическая группа. Другие значения: norms, construction, unassigned. Доступность зависит от разметки отчёта. |
| format=pdf | /report возвращает PDF вместо текста. /report.pdf всегда возвращает PDF. |

Передавайте только один фильтр: section или theme. Фильтр section доступен для PDF. Используйте /report.pdf?section=… или /report?format=pdf&section=….

Скачайте все PDF через GET /runs/{run_id}/report.zip. ZIP содержит общий отчёт и доступные отчёты по разделам. ZIP не содержит JSON или исходные документы.

**Скачать архив отчётов**

```bash
curl --fail-with-body "$NK_BASE/runs/$RUN_ID/report.zip" \
  -H "Authorization: Bearer $NK_KEY" \
  --output reports.zip
```

## Проекты, версии и решения специалистов

bundle объединяет проверки одного проекта. Создайте bundle один раз. Передавайте его bundle_id при следующих загрузках проекта. Каждая проверка получает свой run_id. revision_id обозначает состав файлов, а не решение по замечанию.

**Создать именованный комплект**

```bash
curl --fail-with-body "$NK_BASE/bundles" \
  -H "Authorization: Bearer $NK_KEY" \
  -H "Content-Type: application/json" \
  --data '{"label":"Жилой дом — рабочая документация"}'
```

1. Получите замечания через GET /runs/{run_id}/findings.
2. Выберите finding_id нужного замечания.
3. Передайте feedback_snapshot в поле snapshot.
4. Задайте decision: accepted для одобрения, rejected для отклонения или withdrawn для отзыва решения.
5. Для первого решения задайте version: 0.
6. При изменении используйте последнюю version своего решения. Найдите его в feedback по finding_id, snapshot и feedback_actor.
7. Задайте уникальную метку request_key. Допустимая длина указана в справочнике метода.
8. При повторе того же запроса сохраните прежний request_key. Повтор не создаёт второе решение.

**Тело POST /runs/{run_id}/feedback — учебный пример**

```json
{
  "snapshot": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
  "finding_id": "16f2c114-4904-4cb3-8a92-b61f5e6a583a",
  "decision": "rejected",
  "reason": "Исправлено в следующем листе комплекта",
  "version": 0,
  "request_key": "review-001-item-7-v1"
}
```

Используйте snapshot и finding_id из ответа API. Примеры содержат вымышленные идентификаторы. При 409 report_changed или version_conflict получите актуальный результат. Затем получите историю решений. Используйте версию из ответа.

Сервер хранит решения отдельно от опубликованного отчёта.

Для решений по замечаниям используйте feedback. Получите дополнительные возможности через GET /features. Общий справочник не гарантирует наличие настроек сотрудников в вашем подключении.

GET /memory возвращает заметки и automatic_application. Сейчас automatic_application равен false. Заметки не изменяют следующую проверку автоматически. POST /memory создаёт предложение заметки по feedback_id проверки, связанной с bundle. Предложение требует отдельного рассмотрения.

Отозвать заметку можно через /memory/{note_id}/revoke.

## Лимиты, списания и повтор отправки

Перед загрузкой получите GET /limits с ключом организации. Лимиты могут различаться. Проверяйте файлы по лимитам своего подключения. Не берите ограничения из учебных примеров.

| Поле /limits | Единица и смысл |
| --- | --- |
| max_files | Число документов после распаковки архивов. |
| max_file_bytes | Размер одного файла в байтах. |
| max_total_bytes | Суммарный размер комплекта в байтах. |
| max_pages | Число страниц комплекта. Сервер определяет его после подготовки документов. |
| oversize_factor | 1 запрещает превышение основного лимита. 2 разрешает двойной объём. Сервер учитывает этот объём при списании проверок. |

Ноль в отдельном лимите означает, что профиль не ограничивает этот показатель. Входной сервер и распаковка имеют свои технические ограничения. Входной сервер может вернуть 413 с HTML. Перед разбором ответа проверьте Content-Type.

volume_units показывает учтённый объём: 0, 1 или 2. Значение зависит от результата и условий подключения. checks_left показывает число оставшихся проверок. Это не сумма в рублях. Если status равен failed, проверьте refunded.

Условия доступа определяются тарифом организации.

| Ситуация с Idempotency-Key | Результат и действие |
| --- | --- |
| Ответ на загрузку потерялся | Повторите те же файлы и параметры с тем же ключом. Новый ключ может создать новую проверку. |
| Тот же ключ и тот же запрос | Сервер возвращает 200 с прежним run_id. Вторую проверку он не создаёт. |
| Тот же ключ, но изменены файлы, kinds, адрес, задание или bundle_id | Сервер возвращает 409 idempotency_conflict. Для новой версии комплекта используйте новую метку. |
| Первая отправка ещё не завершена | 409 idempotency_pending. Подождите и повторите с прежней меткой. |

Сервер сравнивает содержимое файлов, а не только имена и размеры. Метка действует в пределах организации. Для исправленного комплекта используйте новый Idempotency-Key. Запросы GET не требуют этой метки.

Срок хранения исходных документов зависит от подключения. DELETE /runs/{run_id}/documents удаляет исходные документы и производные файлы. Опубликованный отчёт и история остаются. Удаляйте документы только после done или failed. Для текущей проверки серверу нужны исходные файлы.

## Уведомления на ваш сервер

Уведомления подключаются отдельно. Передайте HTTPS-адрес обработчика при подключении. Согласуйте секрет подписи. Проверьте webhook_configured в ответе GET /me. Если значение равно false, используйте опрос.

Настроенный адрес доступен в webhook_url ответа GET /key, если подключение возвращает эти сведения.

События: run.finished и run.failed. НОРМОСКАН отправляет POST с JSON на ваш адрес. Заголовок X-NK-Event содержит событие. При заданном секрете заголовок X-NK-Signature содержит подпись. После уведомления получите состояние и результат через API.

Уведомление не содержит текст замечаний.

**Пример тела уведомления**

```json
{
  "event": "run.finished",
  "run_id": "9f1c3b7a-2d4e-4c8b-9a1f-6e5d4c3b2a10",
  "tenant_id": "example-company",
  "api_version": "v1",
  "at": "2026-09-29T12:00:00+00:00",
  "status": "done", "findings": 8, "refunded": false,
  "pages_total": 120, "checks_left": 11
}
```

**Проверка HMAC-SHA256 на Python**

```python
import hashlib
import hmac

def valid_signature(raw_body: bytes, signature: str, secret: str) -> bool:
    expected = "sha256=" + hmac.new(
        secret.encode("utf-8"), raw_body, hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, signature)

# raw_body — исходные байты HTTP-запроса до разбора JSON.
# signature — значение X-NK-Signature; при отсутствии передайте "".
# secret — согласованный секрет уведомлений, не ключ API.
```

Сначала верните ответ 2xx. Затем обработайте результат. По умолчанию сервер делает до 4 попыток и ждёт ответ до 15 секунд. Паузы между попытками: 2, 4 и 6 секунд. Настройки подключения могут отличаться.

Ответ 4xx прекращает повторы, кроме ответа 429.

Определяйте дубликаты по паре run_id и event. Получите журнал через GET /webhooks/deliveries?run_id=…. pending означает ожидание доставки. delivered означает получение 2xx. rejected означает отказ вашего сервера.

failed означает, что попытки закончились. denied означает запрет адреса настройками. Если уведомление не пришло, получите статус проверки через опрос.

## Ошибки: что делать дальше

Сначала проверьте HTTP-статус и Content-Type. Обычно detail.error содержит код ошибки. detail.message содержит пояснение. detail.file содержит имя проблемного файла. Ответ 422 может содержать список ошибок или объект с кодом.

Входной сервер может вернуть HTML.

**Типичный ответ**

```json
{"detail":{"error":"idempotency_conflict","message":"Этот ключ уже использован для другого комплекта"}}
```

| HTTP / код | Что означает | Что делать |
| --- | --- | --- |
| 400 no_files / bad_format | Нет файлов или пригодных для обработки документов. | Проверьте поля files, файлы и содержимое архива. |
| 400 too_many_files / 413 too_big | Превышен предел подключения. | Проверьте /limits. Уменьшите объём или согласуйте увеличение лимита. |
| 401 unauthorized | Ключ отсутствует, неверен или отозван. | Проверьте Authorization: Bearer и ключ. |
| 402 no_checks_left | Недостаточно доступных проверок. | Проверьте /me и условия доступа. |
| 403 forbidden | Доступ организации приостановлен. | Обратитесь к тому, кто подключал организацию. |
| 404 not_found / no_report | Объект не найден либо результат ещё не опубликован. | Проверьте run_id и его статус. Чужие проверки не раскрываются. |
| 409 idempotency_pending | Сервер ещё обрабатывает первую отправку. | Подождите. Повторите тот же запрос с прежним Idempotency-Key. |
| 409 idempotency_conflict | Метка уже связана с другим запросом. | Для нового комплекта задайте новую метку. При потере ответа сохраните прежнюю метку. |
| 409 section_unavailable / thematic_unavailable | Отчёт не содержит привязки к нужному разделу или группе. | Получите общий PDF. Проверьте available_sections. |
| 400 bad_section / bad_theme / conflicting_filters / section_pdf_only | Неверный фильтр отчёта. | Выберите один допустимый фильтр. section используется с PDF. |
| 409 report_changed / version_conflict | Изменилась версия отчёта или решения. | Получите актуальный результат. Получите историю решений. Затем подтвердите своё изменение. |
| 409 idempotency_conflict при feedback | request_key решения повторён с другим содержимым. | Для нового решения используйте новую метку request_key. |
| 409 discipline_already_assigned | Раздел уже назначен сотруднику. | Проверьте назначение сотрудника через кабинет. Возможность доступна только в отдельных подключениях. |
| 422 | Неверный тип, длина или значение параметра. | Исправьте данные по detail и справочнику метода. |
| 429 / сетевой сбой / 5xx | Временная недоступность. | Повторите чтение с паузой. При повторе загрузки сохраните прежний Idempotency-Key. |
| 503 pdf_unavailable | PDF сейчас не собран. | Сохраните /findings и /report. Повторите запрос PDF позже. |
| 503 backend_unavailable / profile_broken | Подключение временно недоступно. | Повторите запрос позже. Если ошибка остаётся, обратитесь в поддержку. |
| 507 storage_full_retry | Сейчас недостаточно места для приёма. | Повторите позже с тем же Idempotency-Key. |

В обращении в поддержку укажите время запроса, HTTP-статус и код ошибки. Если получили run_id, укажите его. Не добавляйте ключ доступа в обращение или журналы.

## Версия API и термины

Версия API: v1. JSON использует UTF-8. Временные метки используют ISO 8601 с часовым поясом, обычно UTC. Пропускайте неизвестные поля. Порядок ключей JSON не имеет значения.

Получите поддерживаемые версии через GET /version. Несовместимая версия получает новый префикс API.

| Термин | Простое объяснение |
| --- | --- |
| run_id | Идентификатор одной принятой проверки. |
| bundle_id | Идентификатор проекта, который объединяет проверки. |
| finding_id | Идентификатор замечания для записи решения. Не заменяйте его номером n. |
| feedback_snapshot | Версия опубликованного результата, к которой относится решение. |
| Idempotency-Key | Метка отправки комплекта, защищающая от повторного запуска при потере ответа. |
| request_key | Метка отправки решения по замечанию. Она защищает от повторной записи при потере ответа. |
| section | Раздел документации, например ЭОМ или СС. theme обозначает более широкую тематическую группу. |
| вебхук | HTTP-уведомление от НОРМОСКАНА на ваш сервер. |
| комплект | Документы, отправленные вместе на одну проверку. |
| подключение | Доступ организации к API с её лимитами и возможностями. |
| context | Справочный документ. Он служит контекстом для проверки. |
| review | Документ, который нужно проверить. |

Откройте «Справочник методов» для параметров и ответов. Все пути отсчитываются от /api/v1. Для Postman, Insomnia или генератора клиента скачайте OpenAPI JSON. Описания в OpenAPI также даны на русском языке.

## Изменения документации

8 октября 2026. Инструкции используют короткие предложения и отдельные шаги. Названия разделов и отчётов приведены к общим терминам. Параметры запросов и формат ответов сохранены.

7 октября 2026. Инструкции и методы получили отдельные страницы. Добавлены поиск и оглавление. Документация описывает отмену очереди, просмотр архивов, роли context/review и ZIP с PDF. Это обновление не изменило обработку комплектов.

Скачайте контракт из OpenAPI JSON. Перед обновлением интеграции получите возможности подключения через GET /features. Дополнительные методы могут быть доступны только в отдельных кабинетах.

## Справочник методов

### GET /health

Проверить доступность сервиса

Проверяет доступность сервиса. Для проверки ключа организации используйте GET /me.

### GET /version

Узнать версию API

Несовместимая версия получает новый префикс API. supported.<версия>.sunset_at содержит дату прекращения поддержки. Прежняя версия поддерживается не менее 90 дней после объявления.

### GET /docset

Получить справочник разделов

Используйте коды справочника в kinds. Получите разделы организации через GET /me. При загрузке сервер игнорирует коды, которых нет в профиле организации.

### GET /features

Проверить дополнительные возможности

Передайте ключ организации. Набор возможностей зависит от подключения. До отправки задания клиента проверьте user_request.

### GET /me

Проверить ключ и получить организацию

Проверяет ключ и возвращает сведения об организации. Не запускает проверку документов и не списывает проверки. Отдельный вход не требуется.

### GET /limits

Получить лимиты своей организации

Проверяйте комплект по лимитам своего подключения. oversize_factor=1 запрещает превышение. Размеры указаны в байтах. max_files учитывает документы после распаковки. Число страниц определяется после подготовки документов.

### GET /key

Посмотреть сведения о ключе и уведомлениях

Возвращает сведения о ключе без полного секрета. concurrency содержит параметр параллельности подключения. Фактическое число одновременных проверок также зависит от очереди.

### GET /subscription

Посмотреть состояние доступа

Сервер определяет даты и остаток проверок. null означает, что значение не определено.

### GET /usage

Получить расход проверок за период

Показывает расход проверок. Не содержит банковских платежей. Значение days за пределами 1–365 заменяется ближайшей границей диапазона.

### POST /runs

Загрузить комплект на проверку

Передавайте каждый файл комплекта отдельным полем files. Ответ 201 означает приём комплекта. Проверьте documents и ограничения отчёта. Если ответ потерян, повторите запрос с прежним Idempotency-Key. Изменённый запрос с прежней меткой вызывает конфликт. При 409 idempotency_pending подождите. Не создавайте новую метку для повтора.

### GET /runs

Получить последние проверки

Возвращает последние проверки, новые первыми. Сервер ограничивает limit диапазоном 1–200. Для полной истории сохраняйте run_id в своей программе.

### GET /runs/{run_id}

Узнать состояние проверки

Запрашивайте статус раз в 5 секунд. Не запрашивайте чаще раза в 2 секунды. Если status равен done, получите результат. Если status равен failed, прочитайте error и refunded. Процент и eta_s не заменяют status.

### GET /runs/{run_id}/findings

Получить замечания и уточнения в JSON

questions не входят в counts.total. Если parsed равен false, используйте raw_markdown. available_sections содержит доступные отчёты по разделам. Если поля нет, используйте общий PDF.

### GET /runs/{run_id}/report

Скачать текст отчёта или PDF

По умолчанию возвращает Markdown. Для PDF передайте format=pdf. Фильтр section требует format=pdf. Отчёт по разделу сохраняет номера замечаний общего отчёта.

### GET /runs/{run_id}/report.pdf

Скачать общий PDF или PDF раздела

Без фильтра возвращает общий PDF. Возьмите section из available_sections ответа /findings. Если поля нет, API не подтвердил поддержку фильтра. Ошибка 409 section_unavailable означает отсутствие привязки к разделу.

### DELETE /runs/{run_id}/documents

Удалить исходные документы завершённой проверки

Удаляет исходные документы и производные файлы. Сохраняет опубликованный отчёт и историю. Вызывайте только после done или failed. Текущей проверке нужны исходные файлы. Повторное удаление безопасно. Удаление не отменяет проверку.

### GET /bundles

Получить именованные комплекты

Возвращает до 200 проектов. Каждый проект объединяет несколько проверок или версий комплекта.

### POST /bundles

Создать именованный комплект

Создаёт проект для объединения проверок. Сохраните bundle_id. Передавайте его при загрузках через POST /runs. Создание проекта не запускает проверку.

### POST /bundles/{bundle_id}/label

Переименовать комплект

Передайте текущую version из GET /bundles. Если возник конфликт, получите запись заново. Переименование не изменяет прежние проверки.

### GET /runs/{run_id}/feedback

Получить историю решений по замечаниям

Возвращает историю решений отдельно от отчёта. Найдите последнюю версию своего решения по finding_id, snapshot и actor.

### POST /runs/{run_id}/feedback

Сохранить или изменить решение

Возьмите snapshot и finding_id из /findings. Для первого решения задайте version=0. Для изменения используйте текущую версию своего решения. request_key защищает от повторной записи. Новое решение требует новой метки. reason_code и employee_id доступны в подключениях с сотрудниками. Решение не изменяет замечание и не запускает проверку.

### GET /memory

Получить заметки по комплектам

Если automatic_application равен false, заметки не применяются автоматически в следующих проверках.

### POST /memory

Предложить заметку на основе решения

Используйте feedback_id проверки, связанной с bundle. Сервер создаёт заметку со статусом candidate. Она требует рассмотрения и не влияет на проверку автоматически. Повтор того же предложения возвращает прежнюю заметку. Другое содержание с тем же feedback_id вызывает 409 memory_already_proposed.

### POST /memory/{note_id}/revoke

Отозвать заметку

Передайте status=revoked и текущую version. Метод позволяет отозвать заметку. Он не позволяет подтвердить её.

### GET /webhooks/deliveries

Посмотреть доставку уведомлений

Возвращает журнал своей проверки. Пустой список означает отсутствие настройки уведомлений или отправок. Адрес и секрет согласуются при подключении.

### POST /session/entry

Отметить вход через форму кабинета

Кабинет использует этот метод для истории входов. Для проверки API-ключа используйте GET /me. Не вызывайте POST /session/entry при каждом опросе статуса.

### POST /runs/{run_id}/cancel

Снять проверку с очереди

Отменяет проверку своей организации, пока она в очереди. Возвращает обновлённое состояние. Списанная проверка возвращается на счёт. После начала обработки или завершения отмена недоступна.

### GET /runs/{run_id}/report.zip

Скачать все PDF одним архивом

Возвращает ZIP с общим PDF и доступными PDF по разделам. ZIP не содержит JSON или исходные документы. Доступен после публикации результата.

### POST /imports/preview

Посмотреть документы в архиве

Возвращает список документов архива для выбора разделов. Не запускает проверку и не списывает проверки. После выбора разделов отправьте исходный архив через POST /runs.
