openapi: 3.0.0
info:
  title: Attachment
  version: 2.0.0
  description: |
    An **Attachment** represents a file or folder in the file storage system.
    Files are organized in a folder structure by project and office.

    Use this API to:
    - Upload a file (e.g. a certificate PDF, a training document)
    - Create a folder
    - Search files by name, path, or metadata
    - Download or view a file

    Uploading a file does **not** automatically associate it with any entity.
    To link an uploaded file to a certificate, action, or other entity,
    create an EntityAttachment after uploading the file.

    Folder structure convention:
    - `/projects/{project_id}/` — project root folder
    - `/projects/{project_id}/offices/{office_name}/` — office folder
servers:
  - url: http://service.4hse.com/
security:
  - OAuth2: []
  - AccessToken: []
paths:
  /v2/attachment/index:
    post:
      summary: index
      operationId: indexAttachment
      tags:
        - Attachments
      description: >
        Searches files and folders in the file storage.


        The index uses a metadata-based search system, different from other
        index endpoints.

        Search parameters:

        - `key` + `value`: search by specific metadata (e.g. `key:
        "project_name"`, `value: "Acme Construction Ltd"`)

        - `searchText`: full-text search in the file name

        - `root_path`: limit search to a root folder

        - `parent_path`: limit search to a parent folder
      requestBody:
        description: Parameters for searching attachments
        required: false
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/AttachmentIndexParams"
            example:
              key: project_name
              value: Acme Construction Ltd
              per-page: 10
              page: 1
              sort: attachment_path
      responses:
        "200":
          description: List of attachments
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/Attachment"
              example:
                - attachment_id: 452b7448dce72057574d59f93d456ed0
                  attachment_path: /projects/b5a7d602-3fc9-4e2a-9d1a-6e5b4c8f7a01/fire-safety-certificate.pdf
                  data:
                    mimetype: application/pdf
                    size: "137863"
                    project_id: b5a7d602-3fc9-4e2a-9d1a-6e5b4c8f7a01
                    project_name: Acme Construction Ltd
          headers:
            X-Pagination-Current-Page:
              schema:
                type: integer
              description: Current page
            X-Pagination-Page-Count:
              schema:
                type: integer
              description: Total number of pages
            X-Pagination-Per-Page:
              schema:
                type: integer
              description: Number of items per page
            X-Pagination-Total-Count:
              schema:
                type: integer
              description: Total number of items
  /v2/attachment/view/{id}:
    get:
      summary: view
      operationId: viewAttachment
      tags:
        - Attachments
      description: >
        Retrieves a file or its metadata.


        The `id` parameter is the file path or the `attachment_id` (MD5 hash of
        the path).

        By default returns the file stream (download).

        Set `Accept: application/json` header to get file metadata instead.

        The `mode` parameter controls delivery: `download` (default) or
        `inline`.
      parameters:
        - name: id
          in: path
          description: File path or attachment_id (MD5 hash of the path).
          required: true
          schema:
            type: string
        - name: mode
          in: query
          description: Delivery mode for the file.
          schema:
            type: string
            enum:
              - download
              - inline
            default: download
      responses:
        "200":
          description: Attachment found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Attachment"
              example:
                attachment_id: 452b7448dce72057574d59f93d456ed0
                attachment_path: /projects/b5a7d602-3fc9-4e2a-9d1a-6e5b4c8f7a01/fire-safety-certificate.pdf
                data:
                  mimetype: application/pdf
                  size: "137863"
                  project_id: b5a7d602-3fc9-4e2a-9d1a-6e5b4c8f7a01
                  project_name: Acme Construction Ltd
        "404":
          description: Attachment not found
  /v2/attachment/{id}:
    post:
      summary: create
      operationId: createAttachment
      tags:
        - Attachments
      description: >
        Uploads a new file or creates a folder.


        The `id` path parameter is the destination folder (e.g.
        `/projects/{project_id}/offices/{office_name}/`).

        The `path` body field is the file name.

        The `file` field is the binary file to upload. If omitted, a folder is
        created instead.

        The `replace` field (1 or 0) controls whether to overwrite an existing
        file with the same name.


        After upload, the file exists but is not linked to any entity.

        To link it to a certificate or other entity, create an EntityAttachment.
      parameters:
        - name: id
          in: path
          description: Destination folder path (e.g.
            /projects/{project_id}/offices/{office_name}/).
          required: true
          schema:
            type: string
      requestBody:
        content:
          multipart/form-data:
            schema:
              type: object
              properties:
                path:
                  type: string
                  description: The file name (e.g. "fire-safety-certificate.pdf").
                  example: fire-safety-certificate.pdf
                replace:
                  type: integer
                  description: If 1, replaces an existing file with the same name. Ignored when
                    creating a folder.
                  example: 0
                file:
                  type: string
                  format: binary
                  description: The file to upload. If omitted, a folder is created.
              required:
                - path
      responses:
        "201":
          description: Attachment created successfully
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Attachment"
              example:
                attachment_id: 452b7448dce72057574d59f93d456ed0
                attachment_path: /projects/b5a7d602-3fc9-4e2a-9d1a-6e5b4c8f7a01/fire-safety-certificate.pdf
                data:
                  mimetype: application/pdf
                  size: "137863"
                  project_id: b5a7d602-3fc9-4e2a-9d1a-6e5b4c8f7a01
                  project_name: Acme Construction Ltd
        "400":
          description: Bad request
        "409":
          description: Destination path already exists
        "413":
          description: File size is too large
        "500":
          description: Failed to create for unknown reason
  /v2/attachment/update/{id}:
    put:
      summary: update
      operationId: updateAttachment
      tags:
        - Attachments
      description: >
        Renames or moves a file or folder.


        The `id` parameter is the current path. The `path` body field is the new
        path.
      parameters:
        - name: id
          in: path
          description: Current file or folder path.
          required: true
          schema:
            type: string
      requestBody:
        description: New path for the file or folder
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                path:
                  type: string
                  description: The new file or folder path.
            example:
              path: /projects/b5a7d602-3fc9-4e2a-9d1a-6e5b4c8f7a01/renamed-certificate.pdf
      responses:
        "200":
          description: Attachment updated successfully
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Attachment"
              example:
                attachment_id: 7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d
                attachment_path: /projects/b5a7d602-3fc9-4e2a-9d1a-6e5b4c8f7a01/renamed-certificate.pdf
        "400":
          description: Bad request
        "404":
          description: Attachment not found
        "409":
          description: Destination path already exists
        "500":
          description: Unable to rename file or folder
  /v2/attachment/delete/{id}:
    delete:
      summary: delete
      operationId: deleteAttachment
      tags:
        - Attachments
      description: >
        Deletes a file or folder.


        If `force=false` (default), the response lists linked entities
        (EntityAttachment links).

        If `force=true`, the file and all links are deleted.
      parameters:
        - name: id
          in: path
          description: File or folder path to delete.
          required: true
          schema:
            type: string
        - name: force
          in: query
          description: If true, deletes the file and all entity links. If false, returns a
            preview of affected entities.
          schema:
            type: boolean
            default: false
      responses:
        "204":
          description: Attachment deleted successfully
        "400":
          description: Preview of linked entities (returned when force=false)
components:
  schemas:
    Attachment:
      type: object
      properties:
        attachment_id:
          type: string
          maxLength: 50
          description: MD5 hash of the file path. This is not a UUID — it is derived from
            the path.
        attachment_path:
          type: string
          maxLength: 1024
          description: Full path of the file or folder in the storage system (e.g.
            `/projects/{project_id}/offices/{office_name}/document.pdf`).
        data:
          type: object
          description: File metadata including mimetype, size, and contextual information
            (project_id, project_name, etc.).
      required:
        - attachment_id
        - attachment_path
    AttachmentIndexParams:
      type: object
      properties:
        per-page:
          type: integer
          minimum: 1
          default: 100
        page:
          type: integer
          minimum: 1
          default: 1
        sort:
          type: string
          description: Field to sort by. Prefix with minus for descending order.
          enum:
            - attachment_path
        key:
          type: string
          description: Metadata key to search by (e.g. "project_name", "mimetype").
        value:
          type: string
          description: Value to match for the specified key.
        searchText:
          type: string
          description: Full-text search in file names.
        root_path:
          type: string
          description: Limit search to files under this root path.
        parent_path:
          type: string
          description: Limit search to files directly inside this parent folder.
      example:
        key: project_name
        value: Acme Construction Ltd
        per-page: 50
        page: 1
        sort: attachment_path
  securitySchemes:
    OAuth2:
      type: oauth2
      flows:
        password:
          tokenUrl: https://auth.4hse.com/realms/4hse/protocol/openid-connect/token
          scopes: {}
    AccessToken:
      type: apiKey
      in: query
      name: access-token
