openapi: 3.1.0
info:
  title: Stirling Image API
  version: 0.9.0
  description: |
    REST API for Stirling Image, a self-hosted image processing platform with 30+ tools.

    ## Authentication

    Most endpoints require authentication via one of two methods:

    1. **Session cookie** — Call `POST /api/auth/login` with username and password. The response includes a `token` field. Pass it as `Authorization: Bearer <token>` on subsequent requests.

    2. **API key** — Generate a key via the Settings UI or `POST /api/v1/api-keys`. Keys are prefixed with `si_`. Pass as `Authorization: Bearer si_...`.

    Endpoints marked with a lock icon require authentication. Admin-only endpoints are noted in their description.
  license:
    name: MIT
    url: https://github.com/stirling-image/stirling-image/blob/main/LICENSE

servers:
  - url: /
    description: Current instance

tags:
  - name: Tools
    description: Image processing tools. Each accepts a multipart file upload and returns a download URL.
  - name: Batch
    description: Process multiple images through a tool in one request.
  - name: Pipelines
    description: Chain multiple tools into reusable workflows.
  - name: Files
    description: Upload, download, and manage processed images.
  - name: Auth
    description: Login, logout, session management, and user administration.
  - name: API Keys
    description: Create and manage API keys for programmatic access.
  - name: Settings
    description: System-wide configuration (admin only for writes).
  - name: Teams
    description: Organize users into teams.
  - name: Branding
    description: Custom logo management.
  - name: System
    description: Health checks, configuration, and job progress.

components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: Session token from login or API key (prefixed with si_)

  schemas:
    Error:
      type: object
      properties:
        statusCode:
          type: integer
          example: 400
        error:
          type: string
          example: Bad Request
        message:
          type: string
          example: Invalid image format

    ToolResponse:
      type: object
      properties:
        jobId:
          type: string
          description: Unique job identifier
        downloadUrl:
          type: string
          description: URL to download the processed image
          example: /api/v1/download/abc123/output.png
        originalSize:
          type: integer
          description: Original file size in bytes
        processedSize:
          type: integer
          description: Processed file size in bytes

    HealthResponse:
      type: object
      properties:
        status:
          type: string
          enum: [healthy, degraded]
        version:
          type: string
        uptime:
          type: string
        database:
          type: string
          enum: [ok, error]

security:
  - bearerAuth: []

paths:
  /api/v1/health:
    get:
      tags: [System]
      summary: Health check
      description: Returns server health status. Used by Docker HEALTHCHECK. Public endpoint.
      security: []
      responses:
        "200":
          description: Server health
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HealthResponse"

  /api/v1/tools/resize:
    post:
      tags: [Tools]
      summary: Resize
      description: Resize an image to specific dimensions or by percentage.
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required: [file, settings]
              properties:
                file:
                  type: string
                  format: binary
                  description: Image file to process
                settings:
                  type: string
                  description: |
                    JSON string with options:
                    - `width` (number, optional) — Target width in pixels
                    - `height` (number, optional) — Target height in pixels
                    - `fit` (string, default "contain") — One of: contain, cover, fill, inside, outside
                    - `withoutEnlargement` (boolean, default false) — Prevent upscaling
                    - `percentage` (number, optional) — Scale by percentage instead of fixed dimensions
      responses:
        "200":
          description: Processed image
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ToolResponse"
        "400":
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Authentication required

  /api/v1/tools/crop:
    post:
      tags: [Tools]
      summary: Crop
      description: Crop an image to a specific region.
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required: [file, settings]
              properties:
                file:
                  type: string
                  format: binary
                  description: Image file to process
                settings:
                  type: string
                  description: |
                    JSON string with options:
                    - `left` (integer, required) — Left offset in pixels
                    - `top` (integer, required) — Top offset in pixels
                    - `width` (integer, required) — Width of crop region in pixels
                    - `height` (integer, required) — Height of crop region in pixels
      responses:
        "200":
          description: Processed image
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ToolResponse"
        "400":
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Authentication required

  /api/v1/tools/rotate:
    post:
      tags: [Tools]
      summary: Rotate and flip
      description: Rotate an image by angle or flip horizontally/vertically.
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required: [file, settings]
              properties:
                file:
                  type: string
                  format: binary
                  description: Image file to process
                settings:
                  type: string
                  description: |
                    JSON string with options:
                    - `angle` (number, default 0) — Rotation angle in degrees
                    - `horizontal` (boolean, default false) — Flip horizontally
                    - `vertical` (boolean, default false) — Flip vertically
      responses:
        "200":
          description: Processed image
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ToolResponse"
        "400":
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Authentication required

  /api/v1/tools/convert:
    post:
      tags: [Tools]
      summary: Convert format
      description: Convert an image to a different format.
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required: [file, settings]
              properties:
                file:
                  type: string
                  format: binary
                  description: Image file to process
                settings:
                  type: string
                  description: |
                    JSON string with options:
                    - `format` (string, required) — One of: jpg, png, webp, avif, tiff, gif, heic
                    - `quality` (number 1-100, optional) — Output quality
      responses:
        "200":
          description: Processed image
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ToolResponse"
        "400":
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Authentication required

  /api/v1/tools/compress:
    post:
      tags: [Tools]
      summary: Compress
      description: Reduce image file size by quality level or target size.
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required: [file, settings]
              properties:
                file:
                  type: string
                  format: binary
                  description: Image file to process
                settings:
                  type: string
                  description: |
                    JSON string with options:
                    - `mode` (string, default "quality") — One of: quality, targetSize
                    - `quality` (number 1-100, optional) — Compression quality level
                    - `targetSizeKb` (number, optional) — Target file size in kilobytes
      responses:
        "200":
          description: Processed image
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ToolResponse"
        "400":
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Authentication required

  /api/v1/tools/strip-metadata:
    post:
      tags: [Tools]
      summary: Strip metadata
      description: Remove EXIF, GPS, ICC, or XMP metadata from an image.
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required: [file, settings]
              properties:
                file:
                  type: string
                  format: binary
                  description: Image file to process
                settings:
                  type: string
                  description: |
                    JSON string with options:
                    - `stripExif` (boolean, default false) — Remove EXIF data
                    - `stripGps` (boolean, default false) — Remove GPS data
                    - `stripIcc` (boolean, default false) — Remove ICC profile
                    - `stripXmp` (boolean, default false) — Remove XMP data
                    - `stripAll` (boolean, default true) — Remove all metadata
      responses:
        "200":
          description: Processed image
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ToolResponse"
        "400":
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Authentication required

  /api/v1/tools/border:
    post:
      tags: [Tools]
      summary: Border and frame
      description: Add a border, rounded corners, padding, or shadow to an image.
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required: [file, settings]
              properties:
                file:
                  type: string
                  format: binary
                  description: Image file to process
                settings:
                  type: string
                  description: |
                    JSON string with options:
                    - `borderWidth` (number 0-200, default 10) — Border thickness in pixels
                    - `borderColor` (hex string, default "#000000") — Border color
                    - `cornerRadius` (number 0-500, default 0) — Corner rounding radius
                    - `padding` (number 0-200, default 0) — Inner padding in pixels
                    - `shadowBlur` (number 0-50, default 0) — Drop shadow blur radius
                    - `shadowColor` (hex string, default "#00000080") — Drop shadow color
      responses:
        "200":
          description: Processed image
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ToolResponse"
        "400":
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Authentication required

  /api/v1/tools/brightness-contrast:
    post:
      tags: [Tools]
      summary: Brightness and contrast
      description: Adjust brightness, contrast, saturation, color channels, and effects.
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required: [file, settings]
              properties:
                file:
                  type: string
                  format: binary
                  description: Image file to process
                settings:
                  type: string
                  description: |
                    JSON string with options:
                    - `brightness` (number -100 to 100, default 0) — Brightness adjustment
                    - `contrast` (number -100 to 100, default 0) — Contrast adjustment
                    - `saturation` (number -100 to 100, default 0) — Saturation adjustment
                    - `red` (number 0-200, default 100) — Red channel multiplier
                    - `green` (number 0-200, default 100) — Green channel multiplier
                    - `blue` (number 0-200, default 100) — Blue channel multiplier
                    - `effect` (string, default "none") — One of: none, grayscale, sepia, invert
      responses:
        "200":
          description: Processed image
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ToolResponse"
        "400":
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Authentication required

  /api/v1/tools/saturation:
    post:
      tags: [Tools]
      summary: Saturation and exposure
      description: Adjust color saturation and exposure settings.
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required: [file, settings]
              properties:
                file:
                  type: string
                  format: binary
                  description: Image file to process
                settings:
                  type: string
                  description: |
                    JSON string with options:
                    - `brightness` (number -100 to 100, default 0) — Brightness adjustment
                    - `contrast` (number -100 to 100, default 0) — Contrast adjustment
                    - `saturation` (number -100 to 100, default 0) — Saturation adjustment
                    - `red` (number 0-200, default 100) — Red channel multiplier
                    - `green` (number 0-200, default 100) — Green channel multiplier
                    - `blue` (number 0-200, default 100) — Blue channel multiplier
                    - `effect` (string, default "none") — One of: none, grayscale, sepia, invert
      responses:
        "200":
          description: Processed image
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ToolResponse"
        "400":
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Authentication required

  /api/v1/tools/color-channels:
    post:
      tags: [Tools]
      summary: Color channels
      description: Adjust individual RGB color channels.
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required: [file, settings]
              properties:
                file:
                  type: string
                  format: binary
                  description: Image file to process
                settings:
                  type: string
                  description: |
                    JSON string with options:
                    - `brightness` (number -100 to 100, default 0) — Brightness adjustment
                    - `contrast` (number -100 to 100, default 0) — Contrast adjustment
                    - `saturation` (number -100 to 100, default 0) — Saturation adjustment
                    - `red` (number 0-200, default 100) — Red channel multiplier
                    - `green` (number 0-200, default 100) — Green channel multiplier
                    - `blue` (number 0-200, default 100) — Blue channel multiplier
                    - `effect` (string, default "none") — One of: none, grayscale, sepia, invert
      responses:
        "200":
          description: Processed image
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ToolResponse"
        "400":
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Authentication required

  /api/v1/tools/color-effects:
    post:
      tags: [Tools]
      summary: Color effects
      description: Apply color effects like grayscale, sepia, or invert.
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required: [file, settings]
              properties:
                file:
                  type: string
                  format: binary
                  description: Image file to process
                settings:
                  type: string
                  description: |
                    JSON string with options:
                    - `brightness` (number -100 to 100, default 0) — Brightness adjustment
                    - `contrast` (number -100 to 100, default 0) — Contrast adjustment
                    - `saturation` (number -100 to 100, default 0) — Saturation adjustment
                    - `red` (number 0-200, default 100) — Red channel multiplier
                    - `green` (number 0-200, default 100) — Green channel multiplier
                    - `blue` (number 0-200, default 100) — Blue channel multiplier
                    - `effect` (string, default "none") — One of: none, grayscale, sepia, invert
      responses:
        "200":
          description: Processed image
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ToolResponse"
        "400":
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Authentication required

  /api/v1/tools/watermark-text:
    post:
      tags: [Tools]
      summary: Text watermark
      description: Add a text watermark to an image.
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required: [file, settings]
              properties:
                file:
                  type: string
                  format: binary
                  description: Image file to process
                settings:
                  type: string
                  description: |
                    JSON string with options:
                    - `text` (string 1-500, required) — Watermark text
                    - `fontSize` (number 8-200, default 48) — Font size in pixels
                    - `color` (hex string, default "#000000") — Text color
                    - `opacity` (number 0-100, default 50) — Opacity percentage
                    - `position` (string, default "center") — One of: center, top-left, top-right, bottom-left, bottom-right, tiled
                    - `rotation` (number -360 to 360, default 0) — Text rotation angle
      responses:
        "200":
          description: Processed image
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ToolResponse"
        "400":
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Authentication required

  /api/v1/tools/text-overlay:
    post:
      tags: [Tools]
      summary: Text overlay
      description: Add styled text overlay with optional background box.
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required: [file, settings]
              properties:
                file:
                  type: string
                  format: binary
                  description: Image file to process
                settings:
                  type: string
                  description: |
                    JSON string with options:
                    - `text` (string 1-500, required) — Overlay text
                    - `fontSize` (number 8-200, default 48) — Font size in pixels
                    - `color` (hex string, default "#FFFFFF") — Text color
                    - `position` (string, default "bottom") — One of: top, center, bottom
                    - `backgroundBox` (boolean, default false) — Show background box behind text
                    - `backgroundColor` (hex string, default "#000000") — Background box color
                    - `shadow` (boolean, default true) — Add text shadow
      responses:
        "200":
          description: Processed image
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ToolResponse"
        "400":
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Authentication required

  /api/v1/tools/replace-color:
    post:
      tags: [Tools]
      summary: Replace color
      description: Replace a specific color in an image with another color or transparency.
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required: [file, settings]
              properties:
                file:
                  type: string
                  format: binary
                  description: Image file to process
                settings:
                  type: string
                  description: |
                    JSON string with options:
                    - `sourceColor` (hex string, default "#FF0000") — Color to replace
                    - `targetColor` (hex string, default "#00FF00") — Replacement color
                    - `makeTransparent` (boolean, default false) — Make matched pixels transparent instead
                    - `tolerance` (number 0-255, default 30) — Color matching tolerance
      responses:
        "200":
          description: Processed image
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ToolResponse"
        "400":
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Authentication required

  /api/v1/tools/gif-tools:
    post:
      tags: [Tools]
      summary: GIF tools
      description: Resize, extract frames from, or optimize animated GIFs.
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required: [file, settings]
              properties:
                file:
                  type: string
                  format: binary
                  description: GIF file to process
                settings:
                  type: string
                  description: |
                    JSON string with options:
                    - `width` (number 1-4096, optional) — Target width in pixels
                    - `height` (number 1-4096, optional) — Target height in pixels
                    - `extractFrame` (number, optional) — Extract specific frame index
                    - `optimize` (boolean, default false) — Optimize GIF file size
      responses:
        "200":
          description: Processed image
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ToolResponse"
        "400":
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Authentication required

  /api/v1/tools/smart-crop:
    post:
      tags: [Tools]
      summary: Smart crop
      description: Automatically crop to the most interesting region at the specified dimensions.
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required: [file, settings]
              properties:
                file:
                  type: string
                  format: binary
                  description: Image file to process
                settings:
                  type: string
                  description: |
                    JSON string with options:
                    - `width` (integer, required) — Target width in pixels
                    - `height` (integer, required) — Target height in pixels
      responses:
        "200":
          description: Processed image
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ToolResponse"
        "400":
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Authentication required

  /api/v1/tools/vectorize:
    post:
      tags: [Tools]
      summary: Image to SVG
      description: Convert a raster image to SVG vector format.
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required: [file, settings]
              properties:
                file:
                  type: string
                  format: binary
                  description: Image file to process
                settings:
                  type: string
                  description: |
                    JSON string with options:
                    - `colorMode` (string, default "bw") — One of: bw, color
                    - `threshold` (number 0-255, default 128) — Binarization threshold
                    - `detail` (string, default "medium") — One of: low, medium, high
      responses:
        "200":
          description: Processed image (downloadUrl points to .svg file)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ToolResponse"
        "400":
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Authentication required

  /api/v1/tools/svg-to-raster:
    post:
      tags: [Tools]
      summary: SVG to raster
      description: Convert an SVG file to a raster image format.
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required: [file, settings]
              properties:
                file:
                  type: string
                  format: binary
                  description: SVG file to convert
                settings:
                  type: string
                  description: |
                    JSON string with options:
                    - `width` (number 1-8192, default 1024) — Output width in pixels
                    - `height` (number 1-8192, optional) — Output height in pixels
                    - `backgroundColor` (hex string, default "#00000000") — Background color
                    - `outputFormat` (string, default "png") — One of: png, jpg, webp
      responses:
        "200":
          description: Processed image
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ToolResponse"
        "400":
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Authentication required

  /api/v1/tools/image-to-pdf:
    post:
      tags: [Tools]
      summary: Image to PDF
      description: Convert one or more images into a PDF document.
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required: [file, settings]
              properties:
                file:
                  type: array
                  items:
                    type: string
                    format: binary
                  description: One or more image files to include in the PDF
                settings:
                  type: string
                  description: |
                    JSON string with options:
                    - `pageSize` (string, default "A4") — One of: A4, Letter, A3, A5
                    - `orientation` (string, default "portrait") — One of: portrait, landscape
                    - `margin` (number 0-100, default 20) — Page margin in points
      responses:
        "200":
          description: Generated PDF (downloadUrl points to .pdf file)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ToolResponse"
        "400":
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Authentication required

  /api/v1/tools/split:
    post:
      tags: [Tools]
      summary: Split image
      description: Split an image into a grid of tiles. Returns a ZIP file.
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required: [file, settings]
              properties:
                file:
                  type: string
                  format: binary
                  description: Image file to split
                settings:
                  type: string
                  description: |
                    JSON string with options:
                    - `columns` (number 1-10, default 2) — Number of columns
                    - `rows` (number 1-10, default 2) — Number of rows
      responses:
        "200":
          description: ZIP archive with results
          content:
            application/zip:
              schema:
                type: string
                format: binary
        "400":
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Authentication required

  /api/v1/tools/bulk-rename:
    post:
      tags: [Tools]
      summary: Bulk rename
      description: Rename multiple images using a pattern template. Supports {{index}}, {{padded}}, and {{original}} tokens. Returns a ZIP file.
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required: [file, settings]
              properties:
                file:
                  type: array
                  items:
                    type: string
                    format: binary
                  description: Multiple image files to rename
                settings:
                  type: string
                  description: |
                    JSON string with options:
                    - `pattern` (string 1-200, default "image-{{index}}") — Naming pattern template
                    - `startIndex` (number, default 1) — Starting index number
      responses:
        "200":
          description: ZIP archive with results
          content:
            application/zip:
              schema:
                type: string
                format: binary
        "400":
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Authentication required

  /api/v1/tools/favicon:
    post:
      tags: [Tools]
      summary: Favicon generator
      description: Generate a complete favicon set (16px to 512px PNGs, ICO, and manifest.json) from an image. Returns a ZIP file.
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required: [file]
              properties:
                file:
                  type: string
                  format: binary
                  description: Image file to use as favicon source
      responses:
        "200":
          description: ZIP archive with results
          content:
            application/zip:
              schema:
                type: string
                format: binary
        "400":
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Authentication required

  /api/v1/tools/watermark-image:
    post:
      tags: [Tools]
      summary: Image watermark
      description: Add an image watermark overlay.
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required: [file, watermark, settings]
              properties:
                file:
                  type: string
                  format: binary
                  description: Main image file
                watermark:
                  type: string
                  format: binary
                  description: Watermark overlay image
                settings:
                  type: string
                  description: |
                    JSON string with options:
                    - `position` (string, default "bottom-right") — One of: center, top-left, top-right, bottom-left, bottom-right
                    - `opacity` (number 0-100, default 50) — Watermark opacity percentage
                    - `scale` (number 1-100, default 25) — Watermark size as percentage of base image
      responses:
        "200":
          description: Processed image
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ToolResponse"
        "400":
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Authentication required

  /api/v1/tools/compose:
    post:
      tags: [Tools]
      summary: Image composition
      description: Composite an overlay image onto a base image with blend modes.
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required: [file, overlay, settings]
              properties:
                file:
                  type: string
                  format: binary
                  description: Base image file
                overlay:
                  type: string
                  format: binary
                  description: Overlay image file
                settings:
                  type: string
                  description: |
                    JSON string with options:
                    - `x` (number, default 0) — Horizontal offset of overlay
                    - `y` (number, default 0) — Vertical offset of overlay
                    - `opacity` (number 0-100, default 100) — Overlay opacity percentage
                    - `blendMode` (string, default "over") — One of: over, multiply, screen, overlay, darken, lighten, hard-light, soft-light, difference, exclusion
      responses:
        "200":
          description: Processed image
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ToolResponse"
        "400":
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Authentication required

  /api/v1/tools/compare:
    post:
      tags: [Tools]
      summary: Image compare
      description: Compare two images and generate a visual difference report.
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required: [file1, file2]
              properties:
                file1:
                  type: string
                  format: binary
                  description: First image to compare
                file2:
                  type: string
                  format: binary
                  description: Second image to compare
      responses:
        "200":
          description: Processed image
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ToolResponse"
        "400":
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Authentication required

  /api/v1/tools/erase-object:
    post:
      tags: [Tools]
      summary: Object eraser
      description: Erase objects from an image using a mask. White areas in the mask indicate regions to erase. Uses LaMa inpainting.
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required: [image, mask]
              properties:
                image:
                  type: string
                  format: binary
                  description: Source image file
                mask:
                  type: string
                  format: binary
                  description: Mask image (white areas will be erased)
      responses:
        "200":
          description: Processed image
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ToolResponse"
        "400":
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Authentication required

  /api/v1/tools/collage:
    post:
      tags: [Tools]
      summary: Collage / grid
      description: Combine multiple images into a grid collage.
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required: [file, settings]
              properties:
                file:
                  type: array
                  items:
                    type: string
                    format: binary
                  description: Multiple image files to combine
                settings:
                  type: string
                  description: |
                    JSON string with options:
                    - `layout` (string, default "2x2") — One of: 2x2, 3x3, 1x3, 2x1, 3x1, 1x2
                    - `gap` (number 0-50, default 4) — Gap between images in pixels
                    - `backgroundColor` (hex string, default "#FFFFFF") — Background fill color
      responses:
        "200":
          description: Processed image
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ToolResponse"
        "400":
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Authentication required

  /api/v1/tools/remove-background:
    post:
      tags: [Tools]
      summary: Remove background
      description: Remove the background from an image using AI (rembg). Runs locally.
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required: [file]
              properties:
                file:
                  type: string
                  format: binary
                  description: Image file to process
                settings:
                  type: string
                  description: |
                    JSON string with options:
                    - `model` (string, optional) — AI model name
                    - `backgroundColor` (string, optional) — Hex color to replace removed background with
      responses:
        "200":
          description: Processed image
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ToolResponse"
        "400":
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Authentication required

  /api/v1/tools/upscale:
    post:
      tags: [Tools]
      summary: Image upscaling
      description: Upscale an image using AI (Real-ESRGAN). Runs locally.
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required: [file, settings]
              properties:
                file:
                  type: string
                  format: binary
                  description: Image file to upscale
                settings:
                  type: string
                  description: |
                    JSON string with options:
                    - `scale` (number, default 2) — Upscale factor
      responses:
        "200":
          description: Processed image
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ToolResponse"
        "400":
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Authentication required

  /api/v1/tools/blur-faces:
    post:
      tags: [Tools]
      summary: Face blur
      description: Detect and blur faces in an image for privacy. Uses OpenCV. Runs locally.
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required: [file, settings]
              properties:
                file:
                  type: string
                  format: binary
                  description: Image file to process
                settings:
                  type: string
                  description: |
                    JSON string with options:
                    - `blurRadius` (number, default 30) — Blur strength radius
                    - `sensitivity` (number 0-1, default 0.5) — Face detection sensitivity
      responses:
        "200":
          description: Processed image
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ToolResponse"
        "400":
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Authentication required

  /api/v1/tools/ocr:
    post:
      tags: [Tools]
      summary: OCR / text extraction
      description: Extract text from an image using OCR. Runs locally.
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required: [file, settings]
              properties:
                file:
                  type: string
                  format: binary
                  description: Image file to extract text from
                settings:
                  type: string
                  description: |
                    JSON string with options:
                    - `engine` (string, default "tesseract") — One of: tesseract, paddleocr
                    - `language` (string, default "en") — One of: en, de, fr, es, zh, ja, ko
      responses:
        "200":
          description: Extracted text
          content:
            application/json:
              schema:
                type: object
                properties:
                  text:
                    type: string
                    description: Extracted text content
                  engine:
                    type: string
                  confidence:
                    type: number
        "400":
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Authentication required

  /api/v1/tools/info:
    post:
      tags: [Tools]
      summary: Image info
      description: Get detailed metadata about an image including dimensions, format, color space, and EXIF data.
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required: [file]
              properties:
                file:
                  type: string
                  format: binary
                  description: Image file to inspect
      responses:
        "200":
          description: Image metadata
          content:
            application/json:
              schema:
                type: object
                properties:
                  width:
                    type: integer
                  height:
                    type: integer
                  format:
                    type: string
                  space:
                    type: string
                  channels:
                    type: integer
                  depth:
                    type: string
                  density:
                    type: integer
                  hasAlpha:
                    type: boolean
                  fileSize:
                    type: integer
                  exif:
                    type: object
        "400":
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Authentication required

  /api/v1/tools/color-palette:
    post:
      tags: [Tools]
      summary: Color palette
      description: Extract the dominant colors from an image.
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required: [file]
              properties:
                file:
                  type: string
                  format: binary
                  description: Image file to analyze
      responses:
        "200":
          description: Dominant colors
          content:
            application/json:
              schema:
                type: object
                properties:
                  colors:
                    type: array
                    items:
                      type: string
                    description: Hex color strings
        "400":
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Authentication required

  /api/v1/tools/barcode-read:
    post:
      tags: [Tools]
      summary: Barcode reader
      description: Read QR codes and barcodes from an image.
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required: [file]
              properties:
                file:
                  type: string
                  format: binary
                  description: Image file containing barcode or QR code
      responses:
        "200":
          description: Decoded barcode data
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: string
                  location:
                    type: object
        "400":
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Authentication required

  /api/v1/tools/find-duplicates:
    post:
      tags: [Tools]
      summary: Find duplicates
      description: Find duplicate images in a set using perceptual hashing.
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required: [file]
              properties:
                file:
                  type: array
                  items:
                    type: string
                    format: binary
                  description: Multiple image files to check for duplicates
      responses:
        "200":
          description: Duplicate groups
          content:
            application/json:
              schema:
                type: object
                properties:
                  groups:
                    type: array
                    items:
                      type: object
                      properties:
                        files:
                          type: array
                          items:
                            type: string
                        similarity:
                          type: number
        "400":
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Authentication required

  /api/v1/tools/qr-generate:
    post:
      tags: [Tools]
      summary: QR code generator
      description: Generate a QR code image from text. This tool accepts a JSON body, not multipart.
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [text]
              properties:
                text:
                  type: string
                  maxLength: 2000
                  description: Text to encode in the QR code
                size:
                  type: integer
                  minimum: 100
                  maximum: 2000
                  default: 400
                  description: Image size in pixels
                errorCorrection:
                  type: string
                  enum: [L, M, Q, H]
                  default: M
                foreground:
                  type: string
                  default: "#000000"
                  description: Foreground color (hex)
                background:
                  type: string
                  default: "#FFFFFF"
                  description: Background color (hex)
      responses:
        "200":
          description: Generated QR code
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ToolResponse"
        "400":
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Authentication required

  /api/v1/tools/strip-metadata/inspect:
    post:
      tags: [Tools]
      summary: Inspect metadata
      description: View all metadata (EXIF, GPS, ICC, XMP) in an image without removing it.
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required: [file]
              properties:
                file:
                  type: string
                  format: binary
                  description: Image file to inspect
      responses:
        "200":
          description: Image metadata
          content:
            application/json:
              schema:
                type: object
                properties:
                  exif:
                    type: object
                    description: EXIF metadata
                  gps:
                    type: object
                    description: GPS metadata
                  icc:
                    type: object
                    description: ICC profile metadata
                  xmp:
                    type: object
                    description: XMP metadata
        "400":
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Authentication required

  # ─── Batch ────────────────────────────────────────────────────────────────

  /api/v1/tools/{toolId}/batch:
    post:
      tags: [Batch]
      summary: Batch process images
      description: >
        Send multiple images through a single tool. Returns a ZIP file.
        The response includes an X-Job-Id header for progress tracking.
      security:
        - bearerAuth: []
      parameters:
        - name: toolId
          in: path
          required: true
          schema:
            type: string
          description: Tool identifier (e.g. resize, crop)
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required: [file]
              properties:
                file:
                  type: array
                  items:
                    type: string
                    format: binary
                  description: Image files to process (one or more)
                settings:
                  type: string
                  description: JSON string of tool settings applied to all images
      responses:
        "200":
          description: ZIP archive of processed images
          headers:
            X-Job-Id:
              schema:
                type: string
              description: Job identifier for progress tracking
          content:
            application/zip:
              schema:
                type: string
                format: binary
        "400":
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Authentication required

  # ─── Pipelines ────────────────────────────────────────────────────────────

  /api/v1/pipeline/execute:
    post:
      tags: [Pipelines]
      summary: Execute a pipeline
      description: Run an image through a chain of tools.
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required: [file, steps]
              properties:
                file:
                  type: string
                  format: binary
                  description: Image file to process
                steps:
                  type: string
                  description: JSON array string of {toolId, settings} objects
      responses:
        "200":
          description: Pipeline result
          content:
            application/json:
              schema:
                type: object
                properties:
                  jobId:
                    type: string
                  downloadUrl:
                    type: string
                  stepsCompleted:
                    type: integer
        "400":
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Authentication required

  /api/v1/pipeline/save:
    post:
      tags: [Pipelines]
      summary: Save a pipeline
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name, steps]
              properties:
                name:
                  type: string
                description:
                  type: string
                steps:
                  type: array
                  items:
                    type: object
                    properties:
                      toolId:
                        type: string
                      settings:
                        type: object
      responses:
        "200":
          description: Saved pipeline
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                  name:
                    type: string
        "400":
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Authentication required

  /api/v1/pipeline/list:
    get:
      tags: [Pipelines]
      summary: List saved pipelines
      security:
        - bearerAuth: []
      responses:
        "200":
          description: List of pipelines
          content:
            application/json:
              schema:
                type: object
                properties:
                  pipelines:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                        name:
                          type: string
                        description:
                          type: string
                        steps:
                          type: array
                          items:
                            type: object
                        createdAt:
                          type: string
                          format: date-time
        "400":
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Authentication required

  /api/v1/pipeline/{id}:
    delete:
      tags: [Pipelines]
      summary: Delete a pipeline
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "204":
          description: Pipeline deleted
        "400":
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Authentication required

  # ─── Files ────────────────────────────────────────────────────────────────

  /api/v1/upload:
    post:
      tags: [Files]
      summary: Upload an image
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required: [file]
              properties:
                file:
                  type: string
                  format: binary
      responses:
        "200":
          description: Upload result
          content:
            application/json:
              schema:
                type: object
                properties:
                  jobId:
                    type: string
                  filename:
                    type: string
        "400":
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Authentication required

  /api/v1/download/{jobId}/{filename}:
    get:
      tags: [Files]
      summary: Download a processed image
      security: []
      parameters:
        - name: jobId
          in: path
          required: true
          schema:
            type: string
        - name: filename
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Image binary
          content:
            image/*:
              schema:
                type: string
                format: binary
        "404":
          description: File not found

  /api/v1/files:
    get:
      tags: [Files]
      summary: List saved files
      security:
        - bearerAuth: []
      responses:
        "200":
          description: List of saved files
          content:
            application/json:
              schema:
                type: object
                properties:
                  files:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                        filename:
                          type: string
                        size:
                          type: integer
                        contentType:
                          type: string
                        createdAt:
                          type: string
                          format: date-time
        "400":
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Authentication required
    delete:
      tags: [Files]
      summary: Delete saved files
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [ids]
              properties:
                ids:
                  type: array
                  items:
                    type: string
      responses:
        "204":
          description: Files deleted
        "400":
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Authentication required

  /api/v1/files/upload:
    post:
      tags: [Files]
      summary: Save file to library
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required: [file]
              properties:
                file:
                  type: string
                  format: binary
      responses:
        "200":
          description: Saved file
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                  filename:
                    type: string
        "400":
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Authentication required

  /api/v1/files/save-result:
    post:
      tags: [Files]
      summary: Save a processing result to library
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [jobId, filename]
              properties:
                jobId:
                  type: string
                filename:
                  type: string
      responses:
        "200":
          description: Saved file
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                  filename:
                    type: string
        "400":
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Authentication required

  /api/v1/files/{id}:
    get:
      tags: [Files]
      summary: Get file metadata
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: File metadata
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                  filename:
                    type: string
                  size:
                    type: integer
                  contentType:
                    type: string
        "400":
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Authentication required

  /api/v1/files/{id}/download:
    get:
      tags: [Files]
      summary: Download a saved file
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Image binary
          content:
            image/*:
              schema:
                type: string
                format: binary
        "400":
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Authentication required

  /api/v1/files/{id}/thumbnail:
    get:
      tags: [Files]
      summary: Get file thumbnail
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Image binary
          content:
            image/*:
              schema:
                type: string
                format: binary
        "400":
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Authentication required

  # ─── Auth ─────────────────────────────────────────────────────────────────

  /api/auth/login:
    post:
      tags: [Auth]
      summary: Log in
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [username, password]
              properties:
                username:
                  type: string
                password:
                  type: string
                  format: password
      responses:
        "200":
          description: Login successful
          content:
            application/json:
              schema:
                type: object
                properties:
                  token:
                    type: string
                  user:
                    type: object
                    properties:
                      id:
                        type: string
                      username:
                        type: string
                      role:
                        type: string
        "400":
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Invalid credentials

  /api/auth/logout:
    post:
      tags: [Auth]
      summary: Log out
      security:
        - bearerAuth: []
      responses:
        "204":
          description: Logged out
        "400":
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Authentication required

  /api/auth/session:
    get:
      tags: [Auth]
      summary: Get current session
      security: []
      responses:
        "200":
          description: Session info
          content:
            application/json:
              schema:
                type: object
                properties:
                  user:
                    type: object
                    properties:
                      id:
                        type: string
                      username:
                        type: string
                      role:
                        type: string
                  authenticated:
                    type: boolean

  /api/auth/change-password:
    post:
      tags: [Auth]
      summary: Change password
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [currentPassword, newPassword]
              properties:
                currentPassword:
                  type: string
                  format: password
                newPassword:
                  type: string
                  format: password
      responses:
        "204":
          description: Password changed
        "400":
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Authentication required

  /api/auth/users:
    get:
      tags: [Auth]
      summary: List users (admin)
      security:
        - bearerAuth: []
      responses:
        "200":
          description: List of users
          content:
            application/json:
              schema:
                type: object
                properties:
                  users:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                        username:
                          type: string
                        role:
                          type: string
                        team:
                          type: string
                        createdAt:
                          type: string
                          format: date-time
                  maxUsers:
                    type: integer
        "400":
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Authentication required
        "403":
          description: Admin access required
    post:
      tags: [Auth]
      summary: Create user (admin)
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [username, password]
              properties:
                username:
                  type: string
                password:
                  type: string
                  format: password
                role:
                  type: string
                  enum: [admin, user]
                  default: user
                team:
                  type: string
                  default: Default
      responses:
        "201":
          description: User created
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                  username:
                    type: string
                  role:
                    type: string
        "400":
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Authentication required
        "403":
          description: Admin access required
        "409":
          description: Username already exists

  /api/auth/users/{id}:
    put:
      tags: [Auth]
      summary: Update user (admin)
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                role:
                  type: string
                  enum: [admin, user]
                team:
                  type: string
      responses:
        "200":
          description: User updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                  username:
                    type: string
                  role:
                    type: string
        "400":
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Authentication required
        "403":
          description: Admin access required
    delete:
      tags: [Auth]
      summary: Delete user (admin)
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "204":
          description: User deleted
        "400":
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Authentication required
        "403":
          description: Admin access required

  /api/auth/users/{id}/reset-password:
    post:
      tags: [Auth]
      summary: Reset user password (admin)
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [newPassword]
              properties:
                newPassword:
                  type: string
                  format: password
      responses:
        "204":
          description: Password reset
        "400":
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Authentication required
        "403":
          description: Admin access required

  # ─── API Keys ─────────────────────────────────────────────────────────────

  /api/v1/api-keys:
    post:
      tags: [API Keys]
      summary: Create an API key
      description: >
        Generate a new API key. The full key (prefixed with si_) is returned
        only once.
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name]
              properties:
                name:
                  type: string
                  maxLength: 100
      responses:
        "201":
          description: API key created
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                  name:
                    type: string
                  key:
                    type: string
                  prefix:
                    type: string
        "400":
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Authentication required
    get:
      tags: [API Keys]
      summary: List API keys
      description: Returns key metadata but not the full key.
      security:
        - bearerAuth: []
      responses:
        "200":
          description: List of API keys
          content:
            application/json:
              schema:
                type: object
                properties:
                  keys:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                        name:
                          type: string
                        prefix:
                          type: string
                        createdAt:
                          type: string
                          format: date-time
        "400":
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Authentication required

  /api/v1/api-keys/{id}:
    delete:
      tags: [API Keys]
      summary: Delete an API key
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "204":
          description: API key deleted
        "400":
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Authentication required

  # ─── Settings ─────────────────────────────────────────────────────────────

  /api/v1/settings:
    get:
      tags: [Settings]
      summary: Get all settings
      security:
        - bearerAuth: []
      responses:
        "200":
          description: All settings as key-value pairs
          content:
            application/json:
              schema:
                type: object
                additionalProperties:
                  type: string
        "400":
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Authentication required
    put:
      tags: [Settings]
      summary: Update settings (admin)
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties:
                type: string
      responses:
        "200":
          description: Settings updated
        "400":
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Authentication required
        "403":
          description: Admin access required

  /api/v1/settings/{key}:
    get:
      tags: [Settings]
      summary: Get a single setting
      security:
        - bearerAuth: []
      parameters:
        - name: key
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Setting value
          content:
            application/json:
              schema:
                type: object
                properties:
                  key:
                    type: string
                  value:
                    type: string
        "400":
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Authentication required

  # ─── Teams ────────────────────────────────────────────────────────────────

  /api/v1/teams:
    get:
      tags: [Teams]
      summary: List teams
      security:
        - bearerAuth: []
      responses:
        "200":
          description: List of teams
          content:
            application/json:
              schema:
                type: object
                properties:
                  teams:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: integer
                        name:
                          type: string
        "400":
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Authentication required
    post:
      tags: [Teams]
      summary: Create a team
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name]
              properties:
                name:
                  type: string
      responses:
        "201":
          description: Team created
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: integer
                  name:
                    type: string
        "400":
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Authentication required
        "409":
          description: Team name already exists

  /api/v1/teams/{id}:
    put:
      tags: [Teams]
      summary: Rename a team
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name]
              properties:
                name:
                  type: string
      responses:
        "200":
          description: Team updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: integer
                  name:
                    type: string
        "400":
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Authentication required
    delete:
      tags: [Teams]
      summary: Delete a team
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        "204":
          description: Team deleted
        "400":
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Authentication required

  # ─── Branding ─────────────────────────────────────────────────────────────

  /api/v1/settings/logo:
    get:
      tags: [Branding]
      summary: Get custom logo
      security: []
      responses:
        "200":
          description: Logo image
          content:
            image/*:
              schema:
                type: string
                format: binary
        "404":
          description: No custom logo set
    post:
      tags: [Branding]
      summary: Upload custom logo (admin)
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required: [logo]
              properties:
                logo:
                  type: string
                  format: binary
                  description: Logo image file
      responses:
        "200":
          description: Logo uploaded
        "400":
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Authentication required
        "403":
          description: Admin access required
    delete:
      tags: [Branding]
      summary: Remove custom logo (admin)
      security:
        - bearerAuth: []
      responses:
        "204":
          description: Logo removed
        "400":
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Authentication required
        "403":
          description: Admin access required

  # ─── System (additional) ──────────────────────────────────────────────────

  /api/v1/config/auth:
    get:
      tags: [System]
      summary: Auth configuration
      security: []
      responses:
        "200":
          description: Auth config
          content:
            application/json:
              schema:
                type: object
                properties:
                  authEnabled:
                    type: boolean

  /api/v1/jobs/{jobId}/progress:
    get:
      tags: [System]
      summary: Job progress (SSE)
      description: >
        Server-Sent Events stream for tracking long-running jobs. Closes
        automatically 5 seconds after completion.
      security: []
      parameters:
        - name: jobId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: SSE stream of job progress events
          content:
            text/event-stream:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    enum: [processing, completed, failed]
                  progress:
                    type: integer
                    minimum: 0
                    maximum: 100
                  completedFiles:
                    type: array
                    items:
                      type: string
                  failedFiles:
                    type: array
                    items:
                      type: string
