Перейти к содержимому
СервисАпп API
На этой странице

Фото и документы оборудования

Все операции. Авторизация. Запросы и повторы.

Все пути в этом файле дополняют /api/v1 на домене организации.

Описание и ограничения

Поля

Публичное поле Формат и доступ
id целое, только чтение
equipment_id целое, только чтение; задаётся путём
kind photo / document, входное поле/только чтение
filename безопасное имя без пути, только чтение
mime_type проверенный MIME, только чтение
size целое число байтов, только чтение
created_at ISO 8601 с поясом, только чтение
version непрозрачная строка, только чтение
download_url относительный путь на том же домене организации, только чтение
file ровно один файл, входное поле
client_filename необязательное имя файла, входное поле
kind Поведение
photo не более одного основного фото; новый файл заменяет прежний
document независимые документы, включая изображения схем и паспортов

Клиент передаёт только поля, перечисленные в схеме загрузки. Вид файла определяется через kind, владелец — адресом запроса.

Доступ и выборка

Нужны equipment_media:read для чтения и equipment_media:write для записи, доступ к оборудованию и права «ТОиР — Оборудование → Просмотр» или «ТОиР — Оборудование → Изменение» соответственно. Список, карточка, скачивание, журнал и повторные запросы проверяют текущий доступ к оборудованию, клиенту и объекту, а также подписку и модуль.

Каждый файл проверяется на принадлежность доступному оборудованию. Файл другого оборудования или организации возвращает 404. При скачивании заново проверяется токен; ранее полученный download_url не сохраняет доступ после отзыва токена.

Список поддерживает filter[kind]=photo или document, sort=id/-id и общую пагинацию по курсору. Без фильтра возвращаются оба вида. updated_since и другие фильтры не обещаны. Версия каждого элемента доступна в списке и отдельной JSON-карточке; GET карточки дополнительно возвращает ETag своего представления без скачивания файла.

Загрузка и замена фото

Для загрузки используйте multipart/form-data с полями file и kind. Необязательный client_filename задаёт имя файла до 255 символов без управляющих символов и разделителей пути. Если он не передан, берётся имя загружаемого файла без пути. Явно заданное имя с путём отклоняется. Ссылка, строка base64, путь на сервере или идентификатор другого файла не заменяют загрузку содержимого.

Один запрос принимает один файл размером от 1 до 10 МиБ включительно (10 485 760 байт). Неизвестные поля, повторяющиеся части и несколько файлов возвращают 422; превышение размера — 413 PAYLOAD_TOO_LARGE; неподдерживаемый Content-Type запроса — 415 UNSUPPORTED_MEDIA_TYPE.

kind Разрешённые типы MIME
photo image/jpeg, image/png, image/webp, image/gif
document application/pdf, application/msword, application/vnd.openxmlformats-officedocument.wordprocessingml.document, application/vnd.ms-excel, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, image/jpeg, image/png, image/webp, image/gif

Тип определяется по содержимому и формату, включая Office-контейнер. Заголовок клиента и расширение не являются единственной проверкой; неподдерживаемый или несогласованный файл — 422 FILE_TYPE_NOT_ALLOWED. SVG, HTML, архивы и PowerPoint не входят в эти коллекции, даже если допустимы у вложений акта.

Для kind=photo секундный updated_at оборудования проверяется под блокировкой до замены. Замена через API или панель обновляет этот маркер. Если секунда изменилась, следующий запрос со старой версией получает 412. При совпадении секунды два запроса с разными ключами могут последовательно заменить фото друг друга; это принятое ограничение секундной версии. Одинаковый ключ возвращает исходную запись об операции до повторной проверки прежнего If-Resource-Version. Клиент явно выбирает photo и тем самым запрашивает замену основного фото, а не добавление второго основного снимка.

Существующее фото остаётся доступным до готовности нового. При неуспешной загрузке или ошибочной версии прежнее фото сохраняется. После успешной замены оно становится недоступно через API; дальнейшее удаление старого файла не затрагивает новое фото.

Успех — 201 с карточкой нового файла, data.version, meta.operation_id, meta.affected_resources и meta.warnings=[], без ETag. Замена фото также создаёт новый идентификатор файла. В meta.affected_resources входят новый файл, прежний файл с deleted=true и version=null при замене, а также оборудование с актуальной версией. Добавление документа тоже обновляет версию оборудования. Если запрос принят, но файл ещё не готов, возвращается 202. До завершения проверяйте журнал операции.

{
  "data": {
    "id": 92,
    "equipment_id": 42,
    "kind": "photo",
    "filename": "Шильдик.jpg",
    "mime_type": "image/jpeg",
    "size": 131072,
    "created_at": "2026-10-30T09:00:00.000000Z",
    "version": "1793782800",
    "download_url": "/api/v1/equipment/42/media/92/download"
  },
  "meta": {
    "operation_id": "op-example-photo-92",
    "affected_resources": [
      {"type": "equipment-media", "id": 92, "version": "1793782800", "deleted": false},
      {"type": "equipment-media", "id": 91, "version": null, "deleted": true},
      {"type": "equipment", "id": 42, "version": "1793782800", "deleted": false}
    ],
    "warnings": []
  }
}

Location у POST указывает на журнал операции. GET метаданных возвращает актуальные data и отдельный ETag представления, без operation_id принятого POST. Каждый ответ, включая ошибку, получает свой X-Request-Id только в заголовке.

Скачивание и удаление

Скачивание возвращает содержимое файла с кодом 200 с проверенным Content-Type, безопасным Content-Disposition: attachment, Cache-Control: private, no-store, X-Content-Type-Options: nosniff и X-Request-Id. JSON-обёртки у бинарного успеха нет. Версию для удаления берут из data.version JSON-метаданных, не из содержимого и не из ETag. Ошибки до начала передачи используют общий JSON-формат. Прямой путь к диску и постоянный публичный URL не выдаются.

Чтение списка, карточки и содержимого файла не создаёт публичную ссылку и не публикует паспорт оборудования. Загрузка не меняет настройки публичного доступа; фото в уже опубликованном паспорте отображается по действующим настройкам.

Удаление принимает {} с If-Resource-Version метаданных файла по секундному updated_at файла; текущее состояние владельца проверяется отдельно под блокировкой. Успех — 200, data={"id":92,"deleted":true}, operation_id и affected_resources: удалённая equipment-media с version=null и оборудование с актуальной version после обновления updated_at. Удаление основного фото оставляет коллекцию пустой; сервер не выбирает документ новым фото автоматически. Старый media_id после замены не может удалить новое фото. Повтор принятого удаления воспроизводит запись об операции; новая команда к отсутствующему ID возвращает 404.

Идемпотентность и восстановление

При повторе загрузки сохраняйте метод, путь, kind, итоговый client_filename, содержимое файла и условия проверки версии. Разделитель частей запроса не влияет на сравнение. Изменение файла, вида, имени, родителя или версии при том же ключе принятой операции возвращает 409 IDEMPOTENCY_CONFLICT.

После сбоя сервер продолжает исходную загрузку или замену, не создавая вторую запись. Принятое удаление сразу закрывает доступ к файлу через API, даже если физическое удаление ещё продолжается.

Повтор загрузки возвращает исходные ID, версию и affected_resources. Если фото позднее заменили ещё раз, повтор запроса прежней загрузки не возвращает его на место и не удаляет новое фото. Если документ позже удалили, повтор запроса не создаёт его заново. Отзыв прав запрещает сохранённый ответ; очистка тела записи об операции не разрешает повторное исполнение ключа.

Если версия оборудования изменилась после принятия фото, но до замены, операция получает состояние failed и ошибку 412 VERSION_MISMATCH. Старое фото сохраняется. Повтор с тем же ключом возвращает сохранённую ошибку; запрос с новой версией требует нового ключа. После смены клиента или объекта оборудования прежний сохранённый ответ недоступен и возвращается 404, даже если у подключения есть доступ к новому владельцу.

Операции

Метод Путь Назначение
GET /equipment/{equipment_id}/media Список метаданных основного фото и документов
POST /equipment/{equipment_id}/media Добавление документа или установка основного фото, multipart/form-data
GET /equipment/{equipment_id}/media/{id} JSON-карточка файла с version и сильным ETag
GET /equipment/{equipment_id}/media/{id}/download Защищённое скачивание содержимого
POST /equipment/{equipment_id}/media/{id}/delete Удаление файла, JSON {}

GET /equipment/{equipment_id}/media

Список метаданных основного фото и документов.

Доступ. Разрешения подключения: equipment_media:read. Права сотрудника: «ТОиР — Оборудование → Просмотр». Дополнительно действуют доступ к записям, мастерским, модулям и условия полей из описания ресурса выше.

Параметры

Имя Где Тип Обязательно null Ограничения
Authorization заголовок строка да нет Bearer <SERVICEAPP_TOKEN>
equipment_id путь строка да нет Шаблон: ^(?:[0-9]+)$
filter[kind] параметр запроса строка нет нет Допустимо: "photo", "document"
sort параметр запроса строка нет нет Допустимо: "id", "-id"
page[size] параметр запроса целое число нет нет ≥ 1; ≤ 100
page[cursor] параметр запроса строка нет нет не более 8192 символов

Ответы

HTTP Назначение и схема
200 Успех. Полная схема и пример
401 Требуется действующий Bearer. Коды и формат ошибки
403 Недостаточно допуска. Коды и формат ошибки
404 Не найдено или недоступно. Коды и формат ошибки
422 Ошибка входных данных. Коды и формат ошибки
429 Лимит запросов, Retry-After. Коды и формат ошибки
503 Временно недоступно. Коды и формат ошибки

Каждый ответ содержит новый X-Request-Id в заголовке. Общие транспортные отказы и правила повтора действуют также при отказе до контроллера; см. ошибки.

Пример запроса

Запускается из каталога комплекта после настройки переменных в быстром старте. Замените демонстрационные идентификаторы 101 и UUID идентификаторами доступных записей. $RESOURCE_VERSION берётся из GET владельца версии, описанного выше. Для нового действия сохраните новый ключ; для повтора используйте прежний.

curl --silent --show-error --globoff \
  "$API/equipment/101/media" \
  --header "Authorization: Bearer $SERVICEAPP_TOKEN" \
  --header "Accept: application/json"

POST /equipment/{equipment_id}/media

Добавление документа или установка основного фото, multipart/form-data.

Доступ. Разрешения подключения: equipment_media:write. Права сотрудника: «ТОиР — Оборудование → Изменение». Дополнительно действуют доступ к записям, мастерским, модулям и условия полей из описания ресурса выше.

Параметры

Имя Где Тип Обязательно null Ограничения
Authorization заголовок строка да нет Bearer <SERVICEAPP_TOKEN>
equipment_id путь строка да нет Шаблон: ^(?:[0-9]+)$
Idempotency-Key заголовок строка да нет не менее 1 символов; не более 128 символов; Шаблон: ^[A-Za-z0-9._:-]+$
If-Resource-Version заголовок строка нет нет Обязателен для kind=photo: версия оборудования.; не менее 1 символов; не более 128 символов; Шаблон: ^[A-Za-z0-9._~-]+$

Тело запроса

Content-Type: multipart/form-data. Тело обязательно.

Поле Тип Обязательно null Ограничения и смысл
kind строка да нет Допустимо: "photo", "document"
file строка (двоичные данные) да нет Файл от 1 до 10 МиБ включительно; MIME проверяется по содержимому.
client_filename строка нет нет не более 255 символов

Ответы

HTTP Назначение и схема
201 Создано. Полная схема и пример
202 Принято, итог в журнале. Полная схема и пример
401 Требуется действующий Bearer. Коды и формат ошибки
403 Недостаточно допуска. Коды и формат ошибки
404 Не найдено или недоступно. Коды и формат ошибки
409 Конфликт состояния или повтора. Коды и формат ошибки
412 Версия устарела. Коды и формат ошибки
413 Превышен размер. Коды и формат ошибки
415 Неподдерживаемый Content-Type. Коды и формат ошибки
422 Ошибка входных данных. Коды и формат ошибки
428 Нужно предусловие версии. Коды и формат ошибки
429 Лимит запросов, Retry-After. Коды и формат ошибки
503 Временно недоступно. Коды и формат ошибки
HTTP Заголовок ответа Тип Описание
201 Location строка
202 Location строка

Каждый ответ содержит новый X-Request-Id в заголовке. Общие транспортные отказы и правила повтора действуют также при отказе до контроллера; см. ошибки.

У принятого POST Location и meta.operation_id указывают на журнал операции; ETag отсутствует. При повторе запроса сохраняются исходные data/meta, добавляется Idempotency-Replayed: true. Версии передавайте по назначению, указанному в описании ресурса.

Пример запроса

Запускается из каталога комплекта после настройки переменных в быстром старте. Замените демонстрационные идентификаторы 101 и UUID идентификаторами доступных записей. $RESOURCE_VERSION берётся из GET владельца версии, описанного выше. Для нового действия сохраните новый ключ; для повтора используйте прежний.

curl --silent --show-error --globoff --request POST \
  "$API/equipment/101/media" \
  --header "Authorization: Bearer $SERVICEAPP_TOKEN" \
  --header "Accept: application/json" \
  --header "Idempotency-Key: example:v1-equipment-media-store:001" \
  --form 'kind=document' \
  --form 'file=@./examples/attachment.pdf;type=application/pdf'

GET /equipment/{equipment_id}/media/{id}

JSON-карточка файла с version и сильным ETag.

Доступ. Разрешения подключения: equipment_media:read. Права сотрудника: «ТОиР — Оборудование → Просмотр». Дополнительно действуют доступ к записям, мастерским, модулям и условия полей из описания ресурса выше.

Параметры

Имя Где Тип Обязательно null Ограничения
Authorization заголовок строка да нет Bearer <SERVICEAPP_TOKEN>
equipment_id путь строка да нет Шаблон: ^(?:[0-9]+)$
id путь строка да нет Шаблон: ^(?:[0-9]+)$
If-None-Match заголовок строка нет нет —

Ответы

HTTP Назначение и схема
200 Успех. Полная схема и пример
304 Представление не изменилось, без тела. Условный GET
401 Требуется действующий Bearer. Коды и формат ошибки
403 Недостаточно допуска. Коды и формат ошибки
404 Не найдено или недоступно. Коды и формат ошибки
422 Ошибка входных данных. Коды и формат ошибки
429 Лимит запросов, Retry-After. Коды и формат ошибки
503 Временно недоступно. Коды и формат ошибки
HTTP Заголовок ответа Тип Описание
200 ETag строка
304 ETag строка

Каждый ответ содержит новый X-Request-Id в заголовке. Общие транспортные отказы и правила повтора действуют также при отказе до контроллера; см. ошибки.

Пример запроса

Запускается из каталога комплекта после настройки переменных в быстром старте. Замените демонстрационные идентификаторы 101 и UUID идентификаторами доступных записей. $RESOURCE_VERSION берётся из GET владельца версии, описанного выше. Для нового действия сохраните новый ключ; для повтора используйте прежний.

curl --silent --show-error --globoff \
  "$API/equipment/101/media/101" \
  --header "Authorization: Bearer $SERVICEAPP_TOKEN" \
  --header "Accept: application/json"

GET /equipment/{equipment_id}/media/{id}/download

Защищённое скачивание содержимого.

Доступ. Разрешения подключения: equipment_media:read. Права сотрудника: «ТОиР — Оборудование → Просмотр». Дополнительно действуют доступ к записям, мастерским, модулям и условия полей из описания ресурса выше.

Параметры

Имя Где Тип Обязательно null Ограничения
Authorization заголовок строка да нет Bearer <SERVICEAPP_TOKEN>
equipment_id путь строка да нет Шаблон: ^(?:[0-9]+)$
id путь строка да нет Шаблон: ^(?:[0-9]+)$

Ответы

HTTP Назначение и схема
200 Успех. Бинарный файл без JSON-обёртки; вложения
401 Требуется действующий Bearer. Коды и формат ошибки
403 Недостаточно допуска. Коды и формат ошибки
404 Не найдено или недоступно. Коды и формат ошибки
422 Ошибка входных данных. Коды и формат ошибки
429 Лимит запросов, Retry-After. Коды и формат ошибки
503 Временно недоступно. Коды и формат ошибки
HTTP Заголовок ответа Тип Описание
200 Content-Disposition строка
200 X-Content-Type-Options строка
200 Cache-Control строка
401 Cache-Control строка
403 Cache-Control строка
404 Cache-Control строка
422 Cache-Control строка
429 Cache-Control строка
503 Cache-Control строка

Каждый ответ содержит новый X-Request-Id в заголовке. Общие транспортные отказы и правила повтора действуют также при отказе до контроллера; см. ошибки.

Пример запроса

Запускается из каталога комплекта после настройки переменных в быстром старте. Замените демонстрационные идентификаторы 101 и UUID идентификаторами доступных записей. $RESOURCE_VERSION берётся из GET владельца версии, описанного выше. Для нового действия сохраните новый ключ; для повтора используйте прежний.

curl --silent --show-error --globoff \
  "$API/equipment/101/media/101/download" \
  --header "Authorization: Bearer $SERVICEAPP_TOKEN" \
  --header "Accept: application/json"

POST /equipment/{equipment_id}/media/{id}/delete

Удаление файла, JSON {}.

Доступ. Разрешения подключения: equipment_media:write. Права сотрудника: «ТОиР — Оборудование → Изменение». Дополнительно действуют доступ к записям, мастерским, модулям и условия полей из описания ресурса выше.

Параметры

Имя Где Тип Обязательно null Ограничения
Authorization заголовок строка да нет Bearer <SERVICEAPP_TOKEN>
equipment_id путь строка да нет Шаблон: ^(?:[0-9]+)$
id путь строка да нет Шаблон: ^(?:[0-9]+)$
Idempotency-Key заголовок строка да нет не менее 1 символов; не более 128 символов; Шаблон: ^[A-Za-z0-9._:-]+$
If-Resource-Version заголовок строка да нет не менее 1 символов; не более 128 символов; Шаблон: ^[A-Za-z0-9._~-]+$

Тело запроса

Content-Type: application/json. Тело обязательно.

Тело — пустой JSON-объект {}. Другие ключи не принимаются.

Ответы

HTTP Назначение и схема
200 Успех. Полная схема и пример
401 Требуется действующий Bearer. Коды и формат ошибки
403 Недостаточно допуска. Коды и формат ошибки
404 Не найдено или недоступно. Коды и формат ошибки
409 Конфликт состояния или повтора. Коды и формат ошибки
412 Версия устарела. Коды и формат ошибки
422 Ошибка входных данных. Коды и формат ошибки
428 Нужно предусловие версии. Коды и формат ошибки
429 Лимит запросов, Retry-After. Коды и формат ошибки
503 Временно недоступно. Коды и формат ошибки
HTTP Заголовок ответа Тип Описание
200 Location строка

Каждый ответ содержит новый X-Request-Id в заголовке. Общие транспортные отказы и правила повтора действуют также при отказе до контроллера; см. ошибки.

У принятого POST Location и meta.operation_id указывают на журнал операции; ETag отсутствует. При повторе запроса сохраняются исходные data/meta, добавляется Idempotency-Replayed: true. Версии передавайте по назначению, указанному в описании ресурса.

Пример запроса

Запускается из каталога комплекта после настройки переменных в быстром старте. Замените демонстрационные идентификаторы 101 и UUID идентификаторами доступных записей. $RESOURCE_VERSION берётся из GET владельца версии, описанного выше. Для нового действия сохраните новый ключ; для повтора используйте прежний.

curl --silent --show-error --globoff --request POST \
  "$API/equipment/101/media/101/delete" \
  --header "Authorization: Bearer $SERVICEAPP_TOKEN" \
  --header "Accept: application/json" \
  --header "Idempotency-Key: example:v1-equipment-media-destroy:001" \
  --header "If-Resource-Version: $RESOURCE_VERSION" \
  --header "Content-Type: application/json" \
  --data-binary '@examples/requests/v1.equipment-media.destroy.json'