openapi: 3.0.3

info:
  title: Files.fm Cloud Storage API
  version: "2.0"
  description: >
    Machine-readable specification for the Files.fm Cloud Storage API. It covers the
    modern REST API V2 (recommended for new integrations, reached through the
    `/api_v2/{action}` clean-URL rewrite) and the legacy V1 endpoints that remain
    supported for compatibility (scripts under `/api/` and a few root-level scripts
    such as `save_file.php`, `down.php`, `thumb*.php`).


    Authentication is handled centrally by `api/authenticate_user.php`. Any one of the
    following is sufficient and HTTPS is always required:

      * HTTP Basic authentication (`Authorization: Basic base64(user:pass)`).
      * An existing session cookie (`PHPSESSID`), or `PHPSESSID`/`SessionID` sent as a parameter.
      * Credentials in the request body or query: `user` + `pass`, or `user` + `user_access_token`.
      * Google OAuth (`google_token` + `google_email`) or Apple SSO (`apple_code`).

    Because credentials can be supplied as body/query fields, most operations include the
    reusable `AuthFields` properties in their request body in addition to the declared
    `security` schemes. Provide exactly one authentication method.


    Source of truth: `api/docs/filesfm_api_v2.md`. Concrete tokens and hashes in examples
    are placeholders.
  contact:
    name: Files.fm support
    email: support@files.fm
    url: https://files.fm
  license:
    name: Proprietary

externalDocs:
  description: Full human-readable API documentation
  url: https://files.fm/api/docs/

servers:
  - url: https://api.files.fm
    description: Production API host

tags:
  - name: Folders
    description: Folder creation, listing, metadata, rename, move, delete and restore.
  - name: Files
    description: File upload, download, metadata, rename, move, delete and restore.
  - name: E-Signature
    description: Listing and sharing files for electronic signing.
  - name: Encryption (E2E)
    description: End-to-end encryption key storage and retrieval. Mostly used by the web client.
  - name: Auth & Session
    description: Login, session validation and account info.
  - name: Sharing & Previews
    description: Sharing, thumbnails, ZIP download, embedding and path-access URLs.
  - name: Search
    description: Full-text search across files and folders (session-authenticated web/ajax endpoint).
  - name: MCP
    description: Hosted Model Context Protocol JSON-RPC endpoint for AI clients.

security:
  - basicAuth: []
  - cookieSession: []
  - {}

paths:
  ###########################################################################
  # Hosted MCP Connector
  ###########################################################################
  /api/mcp:
    post:
      tags: [MCP]
      summary: Files.fm hosted MCP endpoint
      description: >
        Streamable HTTP JSON-RPC 2.0 endpoint for the Files.fm hosted MCP connector.
        Supports `initialize`, `tools/list`, `tools/call`, `resources/list`,
        `resources/read`, and `resources/templates/list`. File and folder tools are
        read-only and require `Authorization: Bearer <user_id>:<api_token>` using a
        Files.fm `API` or `ALL` user access token. Documentation search and static
        documentation resources are public.
      operationId: filesfmMcp
      security:
        - mcpBearerAuth: []
        - {}
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/JsonRpcRequest'
            examples:
              initialize:
                summary: Initialize MCP session
                value:
                  jsonrpc: "2.0"
                  id: 1
                  method: initialize
                  params:
                    protocolVersion: "2025-06-18"
                    capabilities: {}
                    clientInfo:
                      name: example-client
                      version: "1.0.0"
              docsSearch:
                summary: Search Files.fm docs
                value:
                  jsonrpc: "2.0"
                  id: 2
                  method: tools/call
                  params:
                    name: filesfm_search_docs
                    arguments:
                      query: API authentication
      responses:
        '200':
          description: JSON-RPC response.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/JsonRpcResponse'
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '405': { $ref: '#/components/responses/MethodNotAllowed' }
        '500': { $ref: '#/components/responses/ServerError' }

  ###########################################################################
  # API V2 - Folders
  ###########################################################################
  /api_v2/create_folder:
    post:
      tags: [Folders]
      summary: Create a folder
      description: >
        Creates a folder in the user's account and sets its access rights. If
        `parent_folder_hash` is omitted, the folder is created in the account ROOT folder.
      operationId: createFolder
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              allOf:
                - $ref: '#/components/schemas/AuthFields'
                - type: object
                  properties:
                    parent_folder_hash:
                      type: string
                      description: Parent folder hash. Defaults to ROOT when omitted.
                    folder_name:
                      type: string
                      description: Folder name. A random name is generated when omitted.
                    access_type:
                      $ref: '#/components/schemas/FolderAccessType'
      responses:
        '200':
          description: Folder created.
          content:
            application/json:
              schema:
                type: object
                properties:
                  folder_hash: { type: string }
                  folder_edit_key: { type: string }
                  folder_upload_key: { type: string }
                  folder_name: { type: string }
              example:
                folder_hash: "xxxxxxxxx"
                folder_edit_key: "xxxxxxxx"
                folder_upload_key: "xxxxx"
                folder_name: "My folder"
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '429': { $ref: '#/components/responses/RateLimited' }
        '500': { $ref: '#/components/responses/ServerError' }

  /api_v2/get_folder_content:
    post:
      tags: [Folders]
      summary: List folder contents
      description: Returns the direct files and subfolders of a folder (not recursive).
      operationId: getFolderContent
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              allOf:
                - $ref: '#/components/schemas/AuthFields'
                - type: object
                  required: [folder_hash]
                  properties:
                    folder_hash:
                      type: string
                    get_access_rights:
                      type: boolean
                      description: When truthy, each item also includes an `access_rights` object.
      responses:
        '200':
          description: Array of file/folder entries.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/FolderContentItem'
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '500': { $ref: '#/components/responses/ServerError' }

  /api_v2/get_folder_info:
    post:
      tags: [Folders]
      summary: Read folder metadata
      operationId: getFolderInfo
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              allOf:
                - $ref: '#/components/schemas/AuthFields'
                - type: object
                  required: [folder_hash]
                  properties:
                    folder_hash: { type: string }
                    get_access_rights:
                      type: boolean
                      description: When truthy, the response includes an `access_rights` object.
      responses:
        '200':
          description: Folder metadata.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FolderInfo'
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '500': { $ref: '#/components/responses/ServerError' }

  /api_v2/rename_folder:
    post:
      tags: [Folders]
      summary: Rename a folder
      description: Renames a folder the caller may edit. The ROOT folder cannot be renamed.
      operationId: renameFolder
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              allOf:
                - $ref: '#/components/schemas/AuthFields'
                - type: object
                  required: [folder_hash, folder_name]
                  properties:
                    folder_hash: { type: string }
                    folder_name: { type: string }
                    name:
                      type: string
                      description: Alias for `folder_name`. `folder_name` wins if both are sent.
      responses:
        '200':
          description: Folder renamed. `folder_name` is the final normalized, unique name.
          content:
            application/json:
              schema:
                type: object
                properties:
                  folder_hash: { type: string }
                  folder_name: { type: string }
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '405': { $ref: '#/components/responses/MethodNotAllowed' }
        '500': { $ref: '#/components/responses/ServerError' }

  /api_v2/delete_folder:
    post:
      tags: [Folders]
      summary: Delete a folder
      description: >
        Deletes a folder and its contents. To restore, use the legacy
        `/api/undelete_folder.php` script (PRO/BUSINESS/ENTERPRISE only).
      operationId: deleteFolder
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              allOf:
                - $ref: '#/components/schemas/AuthFields'
                - type: object
                  required: [folder_hash]
                  properties:
                    folder_hash: { type: string }
      responses:
        '200':
          description: Status object.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/StatusResult'
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '500': { $ref: '#/components/responses/ServerError' }

  ###########################################################################
  # API V2 - Files
  ###########################################################################
  /api_v2/upload_file:
    post:
      tags: [Files]
      summary: Upload a file to a folder
      description: >
        Uploads one file to an existing folder. Authorization is optional when a valid
        `folder_upload_key` is supplied; otherwise authenticate the user. Do not upload
        directly to ROOT.
      operationId: uploadFile
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              allOf:
                - $ref: '#/components/schemas/AuthFields'
                - type: object
                  required: [folder_hash, file]
                  properties:
                    folder_hash:
                      type: string
                    folder_upload_key:
                      type: string
                      description: Folder key granting upload permission without full access.
                    file:
                      type: string
                      format: binary
                    watermark_text:
                      type: string
                      description: Text watermark for JPG/PNG uploads.
                    watermark_position:
                      type: string
                      enum: [TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT]
                    watermark_color:
                      type: string
                      default: F0FF3C
      responses:
        '200':
          description: File stored.
          content:
            application/json:
              schema:
                type: object
                properties:
                  file_hash: { type: string }
                  file_delete_key: { type: string }
                  file_name: { type: string }
                  timestamp_uploaded: { type: integer }
                  timestamp_last_modified: { type: integer }
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '490':
          description: Virus detected. The file was not stored.
        '491':
          description: File name contains a double extension (e.g. `invoice.pdf.exe`).
        '492':
          description: Folder size limit exceeded.
        '493':
          description: File size limit exceeded.
        '494':
          description: Partial upload; the full-size file was not received.
        '500': { $ref: '#/components/responses/ServerError' }

  /api_v2/download_file:
    post:
      tags: [Files]
      summary: Download raw file contents
      description: >
        Returns raw file bytes by file hash. Authorization is required for PRIVATE files
        and not required for LINK files.
      operationId: downloadFile
      security:
        - basicAuth: []
        - cookieSession: []
        - {}
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              allOf:
                - $ref: '#/components/schemas/AuthFields'
                - type: object
                  required: [file_hash]
                  properties:
                    file_hash: { type: string }
      responses:
        '200':
          description: Raw file contents. `Content-Type` is set from the file type.
          content:
            application/octet-stream:
              schema:
                type: string
                format: binary
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '500': { $ref: '#/components/responses/ServerError' }

  /api_v2/get_file_info:
    post:
      tags: [Files]
      summary: Read file metadata
      operationId: getFileInfo
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              allOf:
                - $ref: '#/components/schemas/AuthFields'
                - type: object
                  required: [file_hash]
                  properties:
                    file_hash: { type: string }
                    get_access_rights:
                      type: boolean
                      description: When truthy, the response includes an `access_rights` object.
      responses:
        '200':
          description: File metadata.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FileInfo'
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '500': { $ref: '#/components/responses/ServerError' }

  /api_v2/rename_file:
    post:
      tags: [Files]
      summary: Rename a file
      operationId: renameFile
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              allOf:
                - $ref: '#/components/schemas/AuthFields'
                - type: object
                  required: [file_hash, file_name]
                  properties:
                    file_hash: { type: string }
                    file_name: { type: string }
      responses:
        '200':
          description: File renamed. `file_name` is the final normalized, unique name.
          content:
            application/json:
              schema:
                type: object
                properties:
                  file_hash: { type: string }
                  file_name: { type: string }
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '405': { $ref: '#/components/responses/MethodNotAllowed' }
        '500': { $ref: '#/components/responses/ServerError' }

  /api_v2/move_file:
    post:
      tags: [Files]
      summary: Move a file to another folder
      operationId: moveFile
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              allOf:
                - $ref: '#/components/schemas/AuthFields'
                - type: object
                  required: [file_hash, folder_hash]
                  properties:
                    file_hash: { type: string }
                    folder_hash:
                      type: string
                      description: Target folder hash.
      responses:
        '200':
          description: File moved. No additional data documented.
        '401': { $ref: '#/components/responses/Unauthorized' }
        '405': { $ref: '#/components/responses/MethodNotAllowed' }
        '500': { $ref: '#/components/responses/ServerError' }

  /api_v2/delete_file:
    post:
      tags: [Files]
      summary: Delete a file
      description: >
        Deletes a file. To restore, use the legacy `/api/undelete_file.php` script
        (PRO/BUSINESS/ENTERPRISE only).
      operationId: deleteFile
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              allOf:
                - $ref: '#/components/schemas/AuthFields'
                - type: object
                  required: [file_hash]
                  properties:
                    file_hash: { type: string }
      responses:
        '200':
          description: File deleted. No additional data documented.
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '500': { $ref: '#/components/responses/ServerError' }

  ###########################################################################
  # API V2 - E-Signature
  ###########################################################################
  /api_v2/get_esignatures:
    post:
      tags: [E-Signature]
      summary: List e-signatures for a file
      operationId: getEsignatures
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              allOf:
                - $ref: '#/components/schemas/AuthFields'
                - type: object
                  required: [file_hash]
                  properties:
                    file_hash: { type: string }
                    from_date:
                      type: string
                      description: Inclusive start date filter.
                    to_date:
                      type: string
                      description: Inclusive end date filter.
      responses:
        '200':
          description: Array of signature records.
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  properties:
                    file_hash: { type: string }
                    signing_timestamp: { type: string }
                    signer_name: { type: string }
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '500': { $ref: '#/components/responses/ServerError' }

  /api_v2/esignature_share:
    post:
      tags: [E-Signature]
      summary: Share a file for signing
      operationId: esignatureShare
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              allOf:
                - $ref: '#/components/schemas/AuthFields'
                - type: object
                  required: [file_hash, emails]
                  properties:
                    file_hash: { type: string }
                    text:
                      type: string
                      description: Message included in the sharing email.
                    emails:
                      type: array
                      items: { type: string, format: email }
                      description: Recipients of the signing request email.
                    next_share_emails:
                      type: array
                      items: { type: string, format: email }
                      description: Emails prefilled for recipients that may re-share.
      responses:
        '200':
          description: Share request processed.
          content:
            application/json:
              schema:
                type: object
                properties:
                  status: { type: string }
                  sent_emails: { type: string }
                  next_share_emails: { type: string }
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '500': { $ref: '#/components/responses/ServerError' }

  ###########################################################################
  # API V2 - End-to-end encryption keys
  ###########################################################################
  /api_v2/save_user_encryption_keys:
    post:
      tags: [Encryption (E2E)]
      summary: Store a user's public encryption keys
      operationId: saveUserEncryptionKeys
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              allOf:
                - $ref: '#/components/schemas/AuthFields'
                - type: object
                  required: [userID, kem, dsa, x25519, ed25519]
                  properties:
                    userID:
                      type: integer
                      description: Must match the authenticated user.
                    kem: { type: string, description: KEM public key. }
                    dsa: { type: string, description: DSA public key. }
                    x25519: { type: string, description: X25519 public key. }
                    ed25519: { type: string, description: Ed25519 public key. }
      responses:
        '200':
          description: Keys stored.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/SuccessResult' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '500': { $ref: '#/components/responses/ServerError' }

  /api_v2/save_file_encryption_key:
    post:
      tags: [Encryption (E2E)]
      summary: Store the wrapped encryption key for a file
      operationId: saveFileEncryptionKey
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              allOf:
                - $ref: '#/components/schemas/AuthFields'
                - type: object
                  required: [userID, fileHash, encryptionKey, keySignature]
                  properties:
                    userID: { type: integer }
                    fileHash: { type: string }
                    encryptionKey: { type: string, description: Wrapped content key. }
                    keySignature: { type: string }
                    fileSignature: { type: string }
      responses:
        '200':
          description: Key stored.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/SuccessResult' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '500': { $ref: '#/components/responses/ServerError' }

  /api_v2/get_file_encryption_key:
    post:
      tags: [Encryption (E2E)]
      summary: Retrieve the wrapped encryption key for a file
      operationId: getFileEncryptionKey
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              allOf:
                - $ref: '#/components/schemas/AuthFields'
                - type: object
                  required: [userID, fileHash]
                  properties:
                    userID: { type: integer }
                    fileHash: { type: string }
                    isEncryptedFolder:
                      type: boolean
                      description: If true, fetch the key created by the encrypting user.
      responses:
        '200':
          description: Wrapped key and sender public keys.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EncryptionKeyResponse'
        '400': { $ref: '#/components/responses/BadRequest' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '500': { $ref: '#/components/responses/ServerError' }

  /api_v2/save_upload_encryption_key:
    post:
      tags: [Encryption (E2E)]
      summary: Store the wrapped encryption key for a folder
      operationId: saveUploadEncryptionKey
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              allOf:
                - $ref: '#/components/schemas/AuthFields'
                - type: object
                  required: [userID, uploadHash, encryptionKey, keySignature]
                  properties:
                    userID: { type: integer }
                    uploadHash: { type: string }
                    encryptionKey: { type: string }
                    keySignature: { type: string }
      responses:
        '200':
          description: Key stored.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/SuccessResult' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '500': { $ref: '#/components/responses/ServerError' }

  /api_v2/get_upload_encryption_key:
    post:
      tags: [Encryption (E2E)]
      summary: Retrieve the wrapped encryption key for a folder
      operationId: getUploadEncryptionKey
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              allOf:
                - $ref: '#/components/schemas/AuthFields'
                - type: object
                  required: [userID, uploadHash]
                  properties:
                    userID: { type: integer }
                    uploadHash: { type: string }
                    isEncryptedFolder:
                      type: boolean
      responses:
        '200':
          description: Wrapped key and sender public keys.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EncryptionKeyResponse'
        '400': { $ref: '#/components/responses/BadRequest' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '500': { $ref: '#/components/responses/ServerError' }

  /api_v2/get_share_encryption_key:
    post:
      tags: [Encryption (E2E)]
      summary: Get a re-wrappable key for sharing with another user
      description: Provide exactly one of `fileHash` or `uploadHash`.
      operationId: getShareEncryptionKey
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              allOf:
                - $ref: '#/components/schemas/AuthFields'
                - type: object
                  required: [userID, receiverEmail]
                  properties:
                    userID: { type: integer }
                    receiverEmail:
                      type: string
                      format: email
                      description: Recipient must have published encryption keys.
                    fileHash:
                      type: string
                      description: Use this or `uploadHash`.
                    uploadHash:
                      type: string
                      description: Use this or `fileHash`.
      responses:
        '200':
          description: Wrapped key and recipient public keys.
          content:
            application/json:
              schema:
                type: object
                properties:
                  encryptionKey: { type: string }
                  keySignature: { type: string }
                  receiverKEMKey: { type: string }
                  receiverDSAKey: { type: string }
                  receiverX25519Key: { type: string }
                  receiverED25519Key: { type: string }
                  receiverUserID: { type: integer }
        '400': { $ref: '#/components/responses/BadRequest' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '500': { $ref: '#/components/responses/ServerError' }

  ###########################################################################
  # Legacy V1 - Auth & session
  ###########################################################################
  /api/authenticate_user.php:
    get:
      tags: [Auth & Session]
      summary: Authenticate and return UserData
      description: >
        Standalone authentication endpoint. Returns the `UserData` object including ROOT
        folder hash/keys. Add `get_user_access_token=1` to also mint an API token.
      operationId: authenticateUser
      parameters:
        - $ref: '#/components/parameters/UserQuery'
        - $ref: '#/components/parameters/PassQuery'
        - $ref: '#/components/parameters/TokenQuery'
        - name: get_user_access_token
          in: query
          schema: { type: boolean }
          description: When set, include `user_access_token` in the response.
      responses:
        '200':
          description: Authentication result.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UserData'
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403':
          description: "Two-factor authentication required (`TWO_FACTOR_REQURIED: 1` header)."

  /api/login.php:
    get:
      tags: [Auth & Session]
      summary: Lightweight login (plain-text cookie string)
      description: >
        Logs in and returns a plain-text cookie string containing the session and ROOT
        folder info. Returns `error: incorrect login` on failure. Prefer
        `authenticate_user.php` for structured JSON.
      operationId: login
      parameters:
        - $ref: '#/components/parameters/UserQuery'
        - $ref: '#/components/parameters/PassQuery'
      responses:
        '200':
          description: Plain-text cookie string or an error line.
          content:
            text/plain:
              schema: { type: string }
              example: "cookie:PHPSESSID=...; user=demo; hash=...; ID=11315; email=demo@example.com; root_upload_hash=...; root_upload_key=..."

  /api/sesslogin.php:
    get:
      tags: [Auth & Session]
      summary: Cross-domain browser SSO login helper
      description: >
        Validates a session and `redirect_url`, logs the user in on the current and partner
        domains, then issues an HTTP redirect. Intended for browser SSO flows, not REST
        clients. `redirect_url` must be a `failiem.lv`/`files.fm`/`files.me`/`file.fm` host.
      operationId: sessLogin
      parameters:
        - name: PHPSESSID
          in: query
          required: true
          schema: { type: string }
          description: Session id to validate.
        - name: username
          in: query
          schema: { type: string }
        - name: redirect_url
          in: query
          schema: { type: string, format: uri }
          description: Allowed Files.fm/partner host to redirect to after login.
      responses:
        '302':
          description: Redirect to `redirect_url` after a successful login.
          headers:
            Location:
              schema: { type: string }
              description: Target URL.

  /api/validate_session.php:
    get:
      tags: [Auth & Session]
      summary: Check whether the current session is logged in
      operationId: validateSession
      security:
        - cookieSession: []
      responses:
        '200':
          description: Session status.
          content:
            application/json:
              schema:
                type: object
                properties:
                  Success: { type: boolean }
                  Message: { type: string, nullable: true }
                  SessionID: { type: string }

  /api/get_storage_data.php:
    get:
      tags: [Auth & Session]
      summary: Storage usage and quota
      operationId: getStorageData
      responses:
        '200':
          description: Storage data in MB.
          content:
            application/json:
              schema:
                type: object
                properties:
                  success: { type: boolean }
                  message: { type: string, nullable: true }
                  data:
                    type: object
                    properties:
                      used_mb: { type: number }
                      quota_mb: { type: string }
                      available_mb: { type: string }
                      free_mb: { type: number }
        '401': { $ref: '#/components/responses/Unauthorized' }

  ###########################################################################
  # Legacy V1 - Folders: create & keys
  ###########################################################################
  /api/get_upload_id.php:
    get:
      tags: [Folders]
      summary: Create a folder (V1 backend of create_folder)
      operationId: getUploadId
      parameters:
        - $ref: '#/components/parameters/UserQuery'
        - $ref: '#/components/parameters/PassQuery'
        - name: folder_name
          in: query
          schema: { type: string }
        - name: parent_hash
          in: query
          schema: { type: string }
          description: Defaults to ROOT when authenticated.
        - name: access_type
          in: query
          schema:
            $ref: '#/components/schemas/FolderAccessType'
      responses:
        '200':
          description: New folder hash and keys.
          content:
            application/json:
              schema:
                type: object
                properties:
                  hash: { type: string }
                  delete_key: { type: string }
                  add_key: { type: string }
                  UserID:
                    oneOf:
                      - { type: integer }
                      - { type: boolean }
                  FolderName: { type: string }

  /api/get_upload_keys.php:
    get:
      tags: [Folders]
      summary: Get a folder's add and delete keys
      operationId: getUploadKeys
      parameters:
        - name: hash
          in: query
          required: true
          schema: { type: string }
        - $ref: '#/components/parameters/UserQuery'
        - $ref: '#/components/parameters/PassQuery'
      responses:
        '200':
          description: Folder keys.
          content:
            application/json:
              schema:
                type: object
                properties:
                  AddFileKey: { type: string }
                  DeleteKey: { type: string }

  /api/get_add_key.php:
    get:
      tags: [Folders]
      summary: Get only the folder add key
      description: Requires an active user session.
      operationId: getAddKey
      security:
        - cookieSession: []
      parameters:
        - name: hash
          in: query
          required: true
          schema: { type: string }
      responses:
        '200':
          description: Folder add key.
          content:
            application/json:
              schema:
                type: object
                properties:
                  add_key: { type: string }

  /api/get_file_keys.php:
    get:
      tags: [Files]
      summary: Get file edit keys
      description: >
        Returns file edit keys (a folder edit key also works where supported). Only
        partially documented; contact Files.fm for the full parameter set and enabled
        access.
      operationId: getFileKeys
      parameters:
        - name: hash
          in: query
          required: true
          schema: { type: string }
          description: File hash.
      responses:
        '200':
          description: File key data.
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true

  /api/get_ids_and_keys_from_paths.php:
    post:
      tags: [Folders]
      summary: Resolve directory paths to ids/keys, creating missing folders
      operationId: getIdsAndKeysFromPaths
      parameters:
        - name: upload_hash
          in: query
          required: true
          schema: { type: string }
          description: Parent folder hash.
        - name: add_key
          in: query
          required: true
          schema: { type: string }
          description: Parent folder add key.
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              allOf:
                - $ref: '#/components/schemas/AuthFields'
                - type: object
                  properties:
                    Directories:
                      type: array
                      items: { type: string }
                      description: One directory path per entry (e.g. `/test/sub1`).
                    access_type:
                      $ref: '#/components/schemas/AccessType'
                    addto: { type: string }
                    addhash: { type: string }
      responses:
        '200':
          description: Map of path to id/add_key.
          content:
            application/json:
              schema:
                type: object
                properties:
                  Success: { type: boolean }
                  Directories:
                    type: object
                    additionalProperties:
                      type: object
                      properties:
                        id: { type: integer }
                        add_key: { type: string }
                  Error: { type: string, nullable: true }

  ###########################################################################
  # Legacy V1 - Files: upload & replace
  ###########################################################################
  /save_file.php:
    post:
      tags: [Files]
      summary: Upload or replace a file (V1 backend of upload_file)
      description: >
        Uploads one file (multipart field `file`). With `get_file_hash`, returns the new
        file hash as plain text. To replace, pass `do=update` and
        `replace_file_hash={file_hash}`.
      operationId: saveFile
      parameters:
        - name: up_id
          in: query
          required: true
          schema: { type: string }
          description: Target folder hash.
        - name: key
          in: query
          required: true
          schema: { type: string }
          description: Folder add key.
        - name: get_file_hash
          in: query
          schema: { type: boolean }
          description: When present, the response body is the uploaded file hash.
        - name: do
          in: query
          schema: { type: string, enum: [update] }
          description: Set to `update` to replace an existing file.
        - name: replace_file_hash
          in: query
          schema: { type: string }
          description: File hash to replace (with `do=update`).
        - name: watermark_text
          in: query
          schema: { type: string }
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required: [file]
              properties:
                file:
                  type: string
                  format: binary
      responses:
        '200':
          description: Upload result. With `get_file_hash`, the body is the file hash.
          content:
            text/plain:
              schema: { type: string }

  ###########################################################################
  # Legacy V1 - Listing & metadata
  ###########################################################################
  /api/get_file_list_for_upload.php:
    get:
      tags: [Folders]
      summary: List direct children of a folder (V1 backend of get_folder_content)
      description: >
        Lists direct child files/folders of one folder. Authenticated callers may list any folder
        they can view. Additionally, this endpoint accepts anonymous (no-credential) requests for
        folders whose access type is `LINK` or `PUBLIC`: pass only the `hash`. Anonymous listing is
        strictly view-only (no `delete_key`/`add_key`; `PRIVATE` and password-protected folders are
        rejected; `PRIVATE` files inside a public folder are hidden), is rate-limited per IP, and is
        delayed by ~2 seconds per request to deter hash enumeration. The V2 `get_folder_content`
        action does not allow anonymous access.
      operationId: getFileListForUpload
      security:
        - {}
        - cookieSession: []
        - basicAuth: []
      parameters:
        - name: hash
          in: query
          required: true
          schema: { type: string }
        - name: include_folders
          in: query
          schema: { type: integer, enum: [0, 1] }
      responses:
        '200':
          description: Array of child entries (empty array for empty folders).
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  properties:
                    hash: { type: string }
                    name: { type: string }
                    Size: { type: string }
        '429':
          description: Anonymous per-IP rate limit exceeded.
          content:
            application/json:
              schema:
                type: object
                properties:
                  error: { type: string, example: rate_limited }

  /api/get_upload_tree.php:
    get:
      tags: [Folders]
      summary: Recursive folder tree (also used for search)
      operationId: getUploadTree
      parameters:
        - name: Hash
          in: query
          required: true
          schema: { type: string }
        - name: DeleteKey
          in: query
          schema: { type: string }
      responses:
        '200':
          description: Nested tree of folders and files.
          content:
            application/json:
              schema:
                type: object
                properties:
                  error: { type: string, nullable: true }
                  data: { type: object }

  /api/upload_list.php:
    get:
      tags: [Folders]
      summary: List direct children of the ROOT folder
      operationId: uploadList
      parameters:
        - $ref: '#/components/parameters/UserQuery'
        - $ref: '#/components/parameters/PassQuery'
      responses:
        '200':
          description: ROOT children metadata (no keys).
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  properties:
                    hash: { type: string }
                    date: { type: string }
                    description: { type: string }
                    files: { type: string }
                    size: { type: string }
                    views: { type: string }
                    valid_to_date: { type: string }

  /api/get_file_info.php:
    get:
      tags: [Files]
      summary: Rich file metadata by hash + delete key
      operationId: getFileInfoV1
      parameters:
        - name: hash
          in: query
          required: true
          schema: { type: string }
        - name: del_key
          in: query
          required: true
          schema: { type: string }
      responses:
        '200':
          description: File metadata.
          content:
            application/json:
              schema:
                type: object
                properties:
                  success: { type: boolean }
                  file_info: { type: object }
    post:
      tags: [Files]
      summary: Resolve a file name to its hash
      description: Mode `action=get_file_hash_by_name`. Requires a logged-in user with view rights.
      operationId: getFileHashByName
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              allOf:
                - $ref: '#/components/schemas/AuthFields'
                - type: object
                  required: [action, upload_hash, file_name]
                  properties:
                    action: { type: string, enum: [get_file_hash_by_name] }
                    upload_hash: { type: string }
                    file_name: { type: string }
      responses:
        '200':
          description: Resolved file hash.
          content:
            application/json:
              schema:
                type: object
                properties:
                  success: { type: boolean }
                  file_info:
                    type: object
                    properties:
                      hash: { type: string }

  /api/get_uploads_exist.php:
    post:
      tags: [Folders]
      summary: Check existence of multiple folders
      operationId: getUploadsExist
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [hashes]
              properties:
                hashes:
                  type: array
                  items: { type: string }
      responses:
        '200':
          description: Map of folder hash to existence.
          content:
            application/json:
              schema:
                type: object
                additionalProperties: { type: boolean }
        '401': { $ref: '#/components/responses/Unauthorized' }

  ###########################################################################
  # Legacy V1 - Rename & access type
  ###########################################################################
  /api/change_file.php:
    post:
      tags: [Files]
      summary: Rename a file (update display name)
      operationId: changeFile
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              allOf:
                - $ref: '#/components/schemas/AuthFields'
                - type: object
                  required: [file_hash, file_display_name]
                  properties:
                    file_hash: { type: string }
                    folder_hash: { type: string }
                    file_display_name: { type: string }
      responses:
        '200':
          description: Update result.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/SuccessMessageResult' }

  /api/change_upload.php:
    post:
      tags: [Folders]
      summary: Rename a folder (DisplayName). ROOT cannot be renamed.
      operationId: changeUpload
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              allOf:
                - $ref: '#/components/schemas/AuthFields'
                - type: object
                  required: [upload_display_name]
                  properties:
                    Hash:
                      type: string
                      description: Folder hash. Alternatively pass `UploadID`.
                    UploadID: { type: integer }
                    upload_display_name: { type: string }
      responses:
        '200':
          description: Update result.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/SuccessMessageResult' }

  /api/upload_access_type.php:
    get:
      tags: [Folders]
      summary: Change a folder's access type
      operationId: uploadAccessType
      parameters:
        - name: h
          in: query
          required: true
          schema: { type: string }
          description: Folder hash.
        - name: type
          in: query
          required: true
          schema:
            $ref: '#/components/schemas/AccessType'
      responses:
        '200':
          description: Result.
          content:
            application/json:
              schema:
                type: object
                properties:
                  success: { type: boolean }
                  error: { type: string }

  ###########################################################################
  # Legacy V1 - Delete & restore
  ###########################################################################
  /api/delete_file.php:
    get:
      tags: [Files]
      summary: Delete a file by hash + delete key
      operationId: deleteFileV1
      parameters:
        - name: hash
          in: query
          required: true
          schema: { type: string }
        - name: del_key
          in: query
          required: true
          schema: { type: string }
      responses:
        '200':
          description: '`{"success":1}` on success, `{"success":0}` on failure.'
          content:
            application/json:
              schema:
                type: object
                properties:
                  success: { type: integer, enum: [0, 1] }

  /api/delete_upload.php:
    get:
      tags: [Folders]
      summary: Delete a folder by hash + delete key
      operationId: deleteUploadV1
      parameters:
        - name: u
          in: query
          required: true
          schema: { type: string }
          description: Folder hash.
        - name: del_key
          in: query
          required: true
          schema: { type: string }
        - name: delete_only_subfolders
          in: query
          schema: { type: integer, enum: [0, 1] }
      responses:
        '200':
          description: Delete result.
          content:
            application/json:
              schema:
                type: object
                properties:
                  success: { type: integer, enum: [0, 1] }
                  error: { type: integer }
                  deleted_id:
                    type: integer
                    nullable: true

  /api/list.php:
    get:
      tags: [Files]
      summary: Legacy combined delete endpoint (file or folder)
      description: >
        Legacy alias kept for compatibility. Delete a file by passing `f={file_hash}`, or
        delete the whole folder by passing `t=ok`. Prefer `delete_file.php` /
        `delete_upload.php` (or the V2 actions) for new integrations.
      operationId: listCombined
      parameters:
        - name: i
          in: query
          required: true
          schema: { type: string }
          description: Folder hash.
        - name: k
          in: query
          required: true
          schema: { type: string }
          description: Folder delete key.
        - name: f
          in: query
          schema: { type: string }
          description: File hash to delete (file-delete mode).
        - name: t
          in: query
          schema: { type: string, enum: [ok] }
          description: Set to `ok` to delete the whole folder (folder-delete mode).
      responses:
        '200':
          description: Delete result.
          content:
            application/json:
              schema:
                type: object
                properties:
                  success: { type: integer, enum: [0, 1] }

  /api/multi_delete.php:
    post:
      tags: [Folders]
      summary: Delete many files/folders in one transaction (max 200)
      operationId: multiDelete
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ItemsBody'
      responses:
        '200':
          description: Bulk delete result.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/BulkResult' }
        '401': { $ref: '#/components/responses/Unauthorized' }

  /api/undelete_file.php:
    get:
      tags: [Files]
      summary: Restore a deleted file (paying users)
      operationId: undeleteFile
      parameters:
        - name: hash
          in: query
          required: true
          schema: { type: string }
        - name: return_new_path
          in: query
          schema: { type: boolean }
        - name: return_new_folder_id
          in: query
          schema: { type: boolean }
      responses:
        '200':
          description: Restore status.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/StatusResult' }

  /api/undelete_folder.php:
    get:
      tags: [Folders]
      summary: Restore a deleted folder (PRO/BUSINESS/ENTERPRISE)
      operationId: undeleteFolder
      parameters:
        - name: hash
          in: query
          required: true
          schema: { type: string }
        - name: return_new_path
          in: query
          schema: { type: boolean }
      responses:
        '200':
          description: Restore status.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/StatusResult' }

  /api/multi_restore.php:
    post:
      tags: [Folders]
      summary: Restore many deleted files/folders (max 200)
      operationId: multiRestore
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ItemsBody'
      responses:
        '200':
          description: Bulk restore result.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/BulkResult' }
        '401': { $ref: '#/components/responses/Unauthorized' }

  /api/delete_items_from_recyclebin.php:
    post:
      tags: [Folders]
      summary: Permanently delete items from the recycle bin (destructive)
      description: Requires the account password for confirmation. Not recoverable.
      operationId: deleteItemsFromRecyclebin
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [password, items]
              properties:
                password:
                  type: string
                  format: password
                items:
                  $ref: '#/components/schemas/ItemSet'
      responses:
        '200':
          description: '`{"status":"ok"}` or `{"status":"error","error_code":1}`.'
          content:
            application/json:
              schema: { $ref: '#/components/schemas/StatusResult' }

  /api/restore_version_file.php:
    get:
      tags: [Files]
      summary: Restore a previous file version (BUSINESS/ENTERPRISE)
      operationId: restoreVersionFile
      parameters:
        - name: hash
          in: query
          required: true
          schema: { type: string }
        - name: del_key
          in: query
          required: true
          schema: { type: string }
        - name: version_id
          in: query
          required: true
          schema: { type: string }
      responses:
        '200':
          description: Restore status.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/StatusResult' }

  ###########################################################################
  # Legacy V1 - Download, thumbnails, previews, sharing
  ###########################################################################
  /down.php:
    get:
      tags: [Files]
      summary: Download a file by hash (V1 backend of download_file)
      operationId: down
      security:
        - basicAuth: []
        - cookieSession: []
        - {}
      parameters:
        - name: i
          in: query
          required: true
          schema: { type: string }
          description: File hash.
      responses:
        '200':
          description: Raw file contents.
          content:
            application/octet-stream:
              schema: { type: string, format: binary }

  /thumb.php:
    get:
      tags: [Sharing & Previews]
      summary: Small image thumbnail (~400x271)
      operationId: thumb
      security:
        - {}
      parameters:
        - name: i
          in: query
          required: true
          schema: { type: string }
          description: File hash.
      responses:
        '200':
          description: Thumbnail image.
          content:
            image/jpeg:
              schema: { type: string, format: binary }

  /thumb_show.php:
    get:
      tags: [Sharing & Previews]
      summary: Large thumbnail (~1500px) for a file or folder
      operationId: thumbShow
      security:
        - {}
      parameters:
        - name: i
          in: query
          required: true
          schema: { type: string }
      responses:
        '200':
          description: Large image.
          content:
            image/jpeg:
              schema: { type: string, format: binary }

  /thumb_video_picture.php:
    get:
      tags: [Sharing & Previews]
      summary: Video poster frame
      operationId: thumbVideoPicture
      security:
        - {}
      parameters:
        - name: i
          in: query
          required: true
          schema: { type: string }
      responses:
        '200':
          description: Video poster image.
          content:
            image/jpeg:
              schema: { type: string, format: binary }

  /thumb_folder.php:
    get:
      tags: [Sharing & Previews]
      summary: Folder cover thumbnail
      operationId: thumbFolder
      security:
        - {}
      parameters:
        - name: i
          in: query
          required: true
          schema: { type: string }
          description: Folder hash.
      responses:
        '200':
          description: Folder cover image.
          content:
            image/jpeg:
              schema: { type: string, format: binary }

  /server_scripts/zip/zip_streamer/upload_zip_streamer.php:
    get:
      tags: [Sharing & Previews]
      summary: Download a folder as a streamed ZIP
      operationId: zipStreamer
      security:
        - basicAuth: []
        - cookieSession: []
        - {}
      parameters:
        - name: uhash
          in: query
          required: true
          schema: { type: string }
          description: Folder hash.
        - name: password
          in: query
          schema: { type: string }
          description: Required for password-protected folders.
      responses:
        '200':
          description: Streamed ZIP archive.
          content:
            application/zip:
              schema: { type: string, format: binary }

  /api/embed_urls.php:
    get:
      tags: [Sharing & Previews]
      summary: Get the human-readable path-access URL for a file
      operationId: embedUrls
      parameters:
        - name: i
          in: query
          required: true
          schema: { type: string }
          description: File hash.
      responses:
        '200':
          description: Path-access URL.
          content:
            application/json:
              schema:
                type: object
                properties:
                  path_access_url: { type: string }

  /api/sharing.php:
    post:
      tags: [Sharing & Previews]
      summary: Share files/folders with fine-grained rights
      description: >
        Full sharing endpoint (view, download, write, delete, add, share). Contact
        Files.fm for the complete parameter set.
      operationId: sharing
      requestBody:
        content:
          application/x-www-form-urlencoded:
            schema:
              $ref: '#/components/schemas/AuthFields'
      responses:
        '200':
          description: Sharing result.
          content:
            application/json:
              schema: { type: object }
    get:
      tags: [Sharing & Previews]
      summary: List and manage shares for an item
      description: >
        Session-authenticated sharing dialog backend. Lists existing shares for an item and
        toggles individual access rights. The session user must hold the `allow_sharing` right
        on the item. Select the item with one of `UploadID`, `FileID`, `UploadHash`, `FileHash`
        or `DvsDocumentId`. `GetModalWindow` and `GetSharingTable` return rendered HTML;
        `UpdateUploadSharing` returns the updated rights as JSON. Setting all rights to 0
        effectively removes an assignee (there is no separate delete-share endpoint).
      operationId: manageSharing
      security:
        - cookieSession: []
      parameters:
        - name: Action
          in: query
          required: true
          schema: { type: string, enum: [GetModalWindow, GetSharingTable, UpdateUploadSharing] }
          description: Operation to perform.
        - name: UploadID
          in: query
          schema: { type: integer }
          description: Folder (upload) id. One item selector is required.
        - name: FileID
          in: query
          schema: { type: integer }
          description: File id.
        - name: UploadHash
          in: query
          schema: { type: string }
          description: Folder hash.
        - name: FileHash
          in: query
          schema: { type: string }
          description: File hash.
        - name: DvsDocumentId
          in: query
          schema: { type: integer }
          description: DVS document id (document management).
        - name: AssigneeType
          in: query
          schema: { type: string, enum: [USER, ANY_USER, EMAIL, ORG_USER_GROUP, ORG_EXTERN_USER_GROUP] }
          description: "UpdateUploadSharing: assignee type."
        - name: AssigneeUserID
          in: query
          schema: { type: integer }
          description: "UpdateUploadSharing: assignee id (user, email or group)."
        - name: AccessRightName
          in: query
          schema: { type: string, enum: [CanRead, CanWrite, CanDelete, CanView, CanShare, CanAdd] }
          description: "UpdateUploadSharing: access right to change."
        - name: AccessRightStatus
          in: query
          schema: { type: integer, enum: [0, 1] }
          description: "UpdateUploadSharing: 1 grants, 0 revokes."
      responses:
        '200':
          description: >
            For `GetModalWindow`/`GetSharingTable`, `{ Success, HTML, ... }` with rendered HTML.
            For `UpdateUploadSharing`, `{ Success, AccessRights }`.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/SharingManageResult' }

  /api/save_sharing_confirmation.php:
    get:
      tags: [Sharing & Previews]
      summary: Confirm or decline a pending share
      operationId: saveSharingConfirmation
      parameters:
        - name: item_type
          in: query
          required: true
          schema: { type: string, enum: [FILE, UPLOAD] }
        - name: item_id
          in: query
          required: true
          schema: { type: string }
        - name: confirm
          in: query
          schema: { type: integer, enum: [0, 1] }
          description: Set to 1 to accept.
        - name: redirect_after_save
          in: query
          schema: { type: integer, enum: [0, 1] }
      responses:
        '200':
          description: Confirmation result.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/SuccessMessageResult' }

  /ajax/search.php:
    get:
      tags: [Search]
      servers:
        - url: https://files.fm
          description: Web host (session-authenticated ajax endpoints).
      summary: Full-text search across files and folders
      description: >
        Searches the authenticated user's files and folders (own, shared, in a specific
        folder, and optionally deleted items). Session-authenticated (pass `PHPSESSID` as a
        cookie or query parameter). Storage subscribers use the Sphinx full-text index;
        other accounts fall back to a SQL `LIKE` search.


        Two request styles: simplified API mode (`result_format=JSON` + `query`, optional
        `folder_hash`) returns a clean JSON array; full mode passes a URL-encoded
        `search_json` object plus table paging/sorting parameters and, without
        `result_format`, returns pre-rendered HTML rows (`WEB_TABLESORTER`).
      operationId: search
      security:
        - cookieSession: []
      parameters:
        - name: result_format
          in: query
          schema: { type: string, enum: [JSON, OWNCLOUD, WEB_TABLESORTER] }
          description: "`JSON` (clean array, recommended), `OWNCLOUD`, or omitted for `WEB_TABLESORTER` (HTML rows)."
        - name: query
          in: query
          schema: { type: string, minLength: 2 }
          description: Simplified-mode search phrase (alternative to `search_json`).
        - name: folder_hash
          in: query
          schema: { type: string }
          description: Simplified-mode folder restriction (subfolders included).
        - name: search_json
          in: query
          schema: { type: string }
          description: >
            URL-encoded JSON search definition (full mode). Fields: `Mode`
            (`simple`/`advanced`), `Query`, `Types`, `Sections`
            (`MY`/`SHARED_WITH_ME`/`SHARED_WITH_ME_ONLY_ORGANIZATION`/`DELETED`/`THIS_FOLDER`),
            `ThisFolder`, and for advanced `Operator`/`Criteria`.
        - name: table_size
          in: query
          schema: { type: integer, minimum: 1, maximum: 1000, default: 1000 }
          description: Results per page.
        - name: table_page
          in: query
          schema: { type: integer, minimum: 0, default: 0 }
          description: Zero-based page number.
        - name: table_column
          in: query
          style: deepObject
          explode: true
          schema:
            type: object
            additionalProperties: { type: integer, enum: [0, 1] }
          description: "Sort column index to direction (0 = ascending, 1 = descending). Index 2 = name, 4 = size, 5 = owner, 6 = views; others = modification date."
        - name: NoSubfolders
          in: query
          schema: { type: boolean }
          description: Do not search subfolders.
        - name: get_access_rights
          in: query
          schema: { type: boolean }
          description: With `result_format=JSON`, add an `access_rights` object to each item.
        - name: PHPSESSID
          in: query
          schema: { type: string }
          description: Session id (if not sent as a cookie).
      responses:
        '200':
          description: >
            With `result_format=JSON`, a JSON array of result items. Otherwise a
            `WEB_TABLESORTER`/`OWNCLOUD` object. Errors return `{"status":"error"}`.
          content:
            application/json:
              schema:
                oneOf:
                  - type: array
                    items: { $ref: '#/components/schemas/SearchResultItem' }
                  - type: object
                    description: WEB_TABLESORTER / OWNCLOUD envelope or error object.

  /ajax/share.php:
    post:
      tags: [Sharing & Previews]
      servers:
        - url: https://files.fm
          description: Web host (session-authenticated ajax endpoints).
      summary: Grant sharing access to users or emails
      description: >
        Grants access to one or more recipients on a single file (`file_view_hash`/`file_hash`)
        or folder (`upload_hash`), with fine-grained rights, and optionally sends share emails.
        Session-authenticated (pass `PHPSESSID`). Same pipeline used by the V2 `esignature_share`
        action. Set `submit=1` to perform the share. Share URLs can embed file/folder delete and
        add keys depending on the granted rights, so treat the response and emails as sensitive.
      operationId: shareItem
      security:
        - cookieSession: []
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              required: [items, submit]
              properties:
                items:
                  type: array
                  description: Recipients. Each entry is `{ value, item_type }`.
                  items:
                    type: object
                    properties:
                      value:
                        type: string
                        description: Email address or organization user id.
                      item_type:
                        type: string
                        enum: [email, org_user]
                        description: Recipient type.
                file_view_hash:
                  type: string
                  description: File hash to share (use this or upload_hash).
                file_hash:
                  type: string
                  description: Alternative file hash parameter.
                upload_hash:
                  type: string
                  description: Folder hash to share.
                rights:
                  type: object
                  description: Access rights to grant.
                  properties:
                    view: { type: boolean, description: Preview/view (CanView). }
                    download: { type: boolean, description: Download (CanRead). }
                    write: { type: boolean, description: Edit/upload (CanWrite). }
                    add: { type: boolean, description: Add files (CanAdd). }
                    delete: { type: boolean, description: Delete (CanDelete). }
                    share: { type: boolean, description: Re-share (CanShare). }
                description:
                  type: string
                  description: Message included in the share email.
                submit:
                  type: string
                  enum: ['1']
                  description: Must be `1` to perform the share; other values validate recipients only.
                send_share_email:
                  type: string
                  enum: ['0', '1']
                  description: "`0` suppresses the notification email (default sends)."
                send_notification_once_open:
                  type: string
                  enum: ['0', '1']
                  description: "`0` disables the link-opened notification (default on)."
                save_share_message:
                  type: string
                  enum: ['0', '1']
                  description: "`1` saves the message as the user's default."
                date_active_from:
                  type: string
                  description: Start of time-limited access window (must be <= date_active_to).
                date_active_to:
                  type: string
                  description: End of time-limited access window.
                next_share_emails:
                  type: array
                  items: { type: string }
                  description: Additional emails to chain-share to the same item.
                external_user_group:
                  type: integer
                  description: Enterprise only - external user group for newly added external users.
                encryption_key:
                  type: string
                  description: "E2E-encrypted items: recipient-wrapped key."
                key_signature:
                  type: string
                  description: "E2E-encrypted items: signature of the wrapped key."
      responses:
        '200':
          description: Share result. `status` is `ok` or `error`; `items` is keyed by recipient value.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ShareResult' }

components:
  securitySchemes:
    basicAuth:
      type: http
      scheme: basic
      description: HTTP Basic with Files.fm username/email and password.
    cookieSession:
      type: apiKey
      in: cookie
      name: PHPSESSID
      description: Session cookie from a prior login.
    mcpBearerAuth:
      type: http
      scheme: bearer
      bearerFormat: "<user_id>:<api_token>"
      description: Files.fm MCP bearer token using a user ID and an API or ALL user access token.

  parameters:
    UserQuery:
      name: user
      in: query
      schema: { type: string }
      description: Username or email (may also be sent in the body).
    PassQuery:
      name: pass
      in: query
      schema: { type: string, format: password }
      description: Account password. Avoid sending secrets in URLs where possible.
    TokenQuery:
      name: user_access_token
      in: query
      schema: { type: string }
      description: API access token for a user or service account.

  schemas:
    AuthFields:
      type: object
      description: >
        Optional credential fields. Provide exactly one authentication method (these body
        fields, HTTP Basic, or a session cookie). Required when no other auth is supplied.
      properties:
        user:
          type: string
          description: Username or email.
        pass:
          type: string
          format: password
          description: Account password.
        user_access_token:
          type: string
          description: API access token (preferred for automation).
        PHPSESSID:
          type: string
          description: Session id passed as a parameter instead of a cookie.

    FolderAccessType:
      type: string
      enum: [PRIVATE, LINK]
      description: Access type for folder creation. LINK allows read/list by anyone with the hash.

    AccessType:
      type: string
      enum: [PRIVATE, LINK, PASSWORD, PUBLIC]
      description: Full folder access type set.

    AccessRights:
      type: object
      description: Present only when get_access_rights is requested.
      properties:
        allow_view: { type: boolean }
        allow_delete: { type: boolean }
        allow_add: { type: boolean }
        allow_edit: { type: boolean }
        allow_download: { type: boolean }

    FolderContentItem:
      type: object
      properties:
        type:
          type: string
          enum: [file, folder]
        hash: { type: string }
        name: { type: string }
        size: { type: integer }
        timestamp_uploaded: { type: integer }
        timestamp_last_modified: { type: integer }
        delete_key:
          type: string
          nullable: true
        add_files_key:
          type: string
          nullable: true
        access_rights:
          $ref: '#/components/schemas/AccessRights'

    SearchResultItem:
      type: object
      properties:
        type:
          type: string
          enum: [file, folder]
        name: { type: string }
        hash: { type: string }
        size: { type: integer }
        mod_date: { type: string }
        path: { type: string }
        public_access_type:
          type: string
          enum: [PUBLIC, PRIVATE]
        parent_hash: { type: string }
        content_type:
          type: string
          nullable: true
        file_count:
          type: integer
          nullable: true
        subfolder_count:
          type: integer
          nullable: true
        access_rights:
          allOf:
            - $ref: '#/components/schemas/AccessRights'
          nullable: true

    ShareResult:
      type: object
      properties:
        status:
          type: string
          enum: [ok, error]
        items:
          type: object
          description: Per-recipient result, keyed by the recipient value (email or user id).
          additionalProperties:
            type: object
            properties:
              item_type: { type: string }
              value: { type: string }
              email: { type: string }
              is_valid_email: { type: boolean }
              user_id:
                type: integer
                nullable: true
              org_id:
                type: integer
                nullable: true
              can_email:
                type: boolean
                nullable: true
              have_to_send_enterprise_invite_email: { type: boolean }
              enterprise_invite_email_link: { type: string }
        message:
          type: string
          nullable: true
          description: "null on success, or one of: no-access-rights, already_shared, marked_as_spam, free_emails_exhausted, wrong_dates."
        invalid_emails: { type: string }
        unsubscribed_emails: { type: string }
        sent_emails: { type: string }

    SharingManageResult:
      type: object
      properties:
        Success: { type: boolean }
        HTML:
          type: string
          nullable: true
          description: Rendered HTML (GetModalWindow / GetSharingTable).
        ItemNameHTML:
          type: string
          description: Escaped item name (GetSharingTable).
        ProjectTeamInfo:
          type: object
          nullable: true
          description: Enterprise project team info (GetSharingTable).
        AccessRights:
          type: object
          nullable: true
          description: Updated rights (UpdateUploadSharing).
          properties:
            CanView: { type: integer, enum: [0, 1] }
            CanRead: { type: integer, enum: [0, 1] }
            CanWrite: { type: integer, enum: [0, 1] }
            CanAdd: { type: integer, enum: [0, 1] }
            CanDelete: { type: integer, enum: [0, 1] }
            CanShare: { type: integer, enum: [0, 1] }

    FileInfo:
      type: object
      properties:
        hash: { type: string }
        name: { type: string }
        size: { type: integer }
        timestamp_uploaded: { type: integer }
        timestamp_last_modified: { type: integer }
        delete_key:
          type: string
          nullable: true
        parent_folder_hash:
          type: string
          nullable: true
        access_type:
          type: string
          enum: [PRIVATE, LINK, PASSWORD, PUBLIC]
          description: File access type; if the file has no own type, the parent folder's type is returned.
        password_protected:
          type: boolean
          description: True when the parent folder has a password set (password value is never returned).
        price_set:
          type: boolean
          description: True when the file has a download/sale price configured (price amount is not returned).
        access_rights:
          $ref: '#/components/schemas/AccessRights'

    FolderInfo:
      type: object
      properties:
        hash: { type: string }
        name: { type: string }
        size: { type: integer }
        timestamp_uploaded: { type: integer }
        timestamp_last_modified: { type: integer }
        delete_key:
          type: string
          nullable: true
        parent_folder_hash:
          type: string
          nullable: true
        add_files_key:
          type: string
          nullable: true
        file_count: { type: integer }
        subfolder_count: { type: integer }
        access_type:
          type: string
          enum: [PRIVATE, LINK, PASSWORD, PUBLIC]
        password_protected:
          type: boolean
          description: True when the folder has a password set (password value is never returned).
        price_set:
          type: boolean
          description: True when the folder has a download/sale price configured (price amount is not returned).
        access_rights:
          $ref: '#/components/schemas/AccessRights'

    UserData:
      type: object
      properties:
        ID: { type: integer }
        UserName: { type: string }
        Email: { type: string }
        first_name: { type: string }
        last_name: { type: string }
        avatar_path: { type: string }
        profile_background: { type: string }
        root_upload_hash: { type: string }
        root_upload_key: { type: string }
        root_add_key: { type: string }
        mobile_camera_upload_hash: { type: string }
        current_location_country_code: { type: string }
        file_size_limit: { type: integer }
        queue_size_limit: { type: integer }
        user_access_token:
          type: string
          description: Present only when get_user_access_token was requested.

    EncryptionKeyResponse:
      type: object
      properties:
        encryptionKey: { type: string }
        keySignature: { type: string }
        fileSignature: { type: string }
        senderPublicKEMKey: { type: string }
        senderPublicDSAKey: { type: string }
        senderPublicX25519Key: { type: string }
        senderPublicED25519Key: { type: string }
        senderUserID: { type: integer }

    ItemSet:
      type: object
      properties:
        files:
          type: array
          items: { type: string }
        folders:
          type: array
          items: { type: string }

    ItemsBody:
      type: object
      required: [items]
      properties:
        items:
          $ref: '#/components/schemas/ItemSet'

    SuccessResult:
      type: object
      properties:
        success: { type: boolean }

    StatusResult:
      type: object
      properties:
        status:
          type: string
          enum: [ok, error]
        error_code:
          type: integer
          nullable: true

    SuccessMessageResult:
      type: object
      properties:
        Success: { type: boolean }
        Error:
          type: string
          nullable: true
        Message:
          type: string
          nullable: true

    BulkResult:
      type: object
      properties:
        success: { type: boolean }
        error: { type: string }
        error_code:
          type: integer
          nullable: true

    Error:
      type: object
      properties:
        error: { type: string }
        message: { type: string }

    JsonRpcRequest:
      type: object
      required: [jsonrpc, method]
      properties:
        jsonrpc:
          type: string
          enum: ["2.0"]
        id:
          nullable: true
          oneOf:
            - type: string
            - type: integer
        method:
          type: string
          enum:
            - initialize
            - tools/list
            - tools/call
            - resources/list
            - resources/read
            - resources/templates/list
        params:
          type: object
          additionalProperties: true

    JsonRpcResponse:
      type: object
      required: [jsonrpc]
      properties:
        jsonrpc:
          type: string
          enum: ["2.0"]
        id:
          nullable: true
          oneOf:
            - type: string
            - type: integer
        result:
          type: object
          additionalProperties: true
        error:
          type: object
          properties:
            code: { type: integer }
            message: { type: string }

  responses:
    BadRequest:
      description: Invalid or incomplete parameters.
      content:
        application/json:
          schema: { $ref: '#/components/schemas/Error' }
    Unauthorized:
      description: Authorization failed or is missing.
      content:
        application/json:
          schema: { $ref: '#/components/schemas/Error' }
    Forbidden:
      description: Authenticated user does not have access rights.
      content:
        application/json:
          schema: { $ref: '#/components/schemas/Error' }
    NotFound:
      description: Resource not found.
      content:
        application/json:
          schema: { $ref: '#/components/schemas/Error' }
    MethodNotAllowed:
      description: Operation not permitted for this resource (e.g. missing rights or not found).
      content:
        application/json:
          schema: { $ref: '#/components/schemas/Error' }
    RateLimited:
      description: Rate limit hit. Wait and retry later.
      content:
        application/json:
          schema: { $ref: '#/components/schemas/Error' }
    ServerError:
      description: Backend/API service error.
      content:
        application/json:
          schema: { $ref: '#/components/schemas/Error' }
