openapi: "3.0.3"
info:
  title: "Clarity API"
  version: "0.1.0"
  description: |
    REST API served by the Clarity Tauri binary (Rust/Warp, TLS) on `https://localhost:3030`.

    > Last updated: 2026-05-23 — generated from `clarity:backend/src-tauri/src/main.rs` + handler sources.

    **Applies to:** [mode: standalone] [mode: historian]

    ### Authentication
    Most endpoints require a JWT Bearer token in the `Authorization` header.
    Obtain a token via `POST /exactapi/login`. Roles: `admin`, `read-write`, `read-only`.

    Rate limits: auth endpoints 30 req/60 s; ingest endpoints 1 000 req/min.

    ### TLS
    Self-signed certificate. Download the CA cert from `GET /exactapi/ca-cert` and
    install it, or disable cert verification in your client.

    ### Order Portal
    A separate FastAPI service (`http://localhost:8080`). Not part of the Tauri binary.
    Use the **Order Portal** server entry in the dropdown to reach those endpoints.

    ### SQLite CRUD
    Dynamic CRUD routes are generated for every data model via the `register_models!`
    macro (`clarity:backend/src-tauri/src/sqlite_api/schema.rs`). The pattern for each
    `{model}` path is documented under the **SQLite CRUD** tag. All CRUD routes require
    JWT auth unless noted otherwise.

    ### EMS Routes
    Routes defined in `clarity:backend/src-tauri/src/processing_api/ems.rs` under
    `/dataflow/ems/` but not confirmed active in `main.rs` api_routes composition.
    > TODO-VERIFY: confirm whether EMS routes are served directly or proxied.

servers:
  - url: "https://localhost:3030"
    description: "Clarity backend — Warp TLS (self-signed cert)"
  - url: "http://localhost:8080"
    description: "Order Portal — separate FastAPI service"

security:
  - BearerAuth: []

tags:
  - name: Authentication
    description: Registration, login, password management
  - name: Users
    description: Admin user management
  - name: Collections
    description: Time-series collection metadata
  - name: Storage - Write
    description: Write time-series values to binary storage
  - name: Storage - Query
    description: Query time-series values from binary storage
  - name: Tag Mappings
    description: Tag-mapping queries and writes
  - name: Ingest
    description: External data ingest (client-keyed, rate-limited, 10 MB limit)
  - name: Elog
    description: Event log group data and report downloads
  - name: EMS
    description: "Energy Management System analytics (TODO-VERIFY: may be proxied, not confirmed in main.rs)"
  - name: Monitor - Alarms
    description: Real-time alarm snapshots and event queries
  - name: Monitor - Rules
    description: Alarm rule CRUD
  - name: Monitor - Config
    description: Monitor system enable/disable and collection tick-rate management
  - name: Backup
    description: Backup status, configuration, and restore
  - name: PI Connector
    description: OSIsoft PI Web API metadata connector (hierarchy, onboarding, backfill)
  - name: ADK
    description: Google ADK proxy — forwards to pulse_multi_agents on port 8000
  - name: Process Logs
    description: Sub-process log streaming
  - name: Sensor Data
    description: Kairos-compatible sensor last-list
  - name: Attachments
    description: File attachment containers and uploads
  - name: Dashboards
    description: Dashboard records (SQLite CRUD)
  - name: Connections
    description: Connection records (SQLite CRUD)
  - name: Tags
    description: Tag metadata records — `tagmeta` model (SQLite CRUD)
  - name: SQLite CRUD
    description: |
      Dynamic CRUD routes for all data models. Pattern per `{model}`:
      `GET /{model}`, `POST /{model}`, `GET /{model}/count`, `GET /{model}/findOne`,
      `GET /{model}/{id}`, `PUT /{model}/update`, `POST /{model}/bulk`,
      `GET /{model}/{id}/exists`, `GET /{model}/{id}/{relationship}`.

      All routes are prefixed with `/exactapi/`. Auth required on all operations.

      Models: `units` `sites` `orgs` `equipment` `tagmeta` `deviations` `faulttrees`
      `faulttemplates` `incidents` `calculations` `dashboards` `dashboardplots`
      `userprofiles` `useractivities` `clients` `ingestconfigs` `tags` `statuses`
      `activities` `heatrates` `modelpipelines` `boilerassets` `configs`
      `configurations` `profiles_lookups` `labels` `connections` `users`
  - name: Order Portal
    description: Air-gapped software licensing — separate FastAPI service on port 8080

paths:

  # ──────────────────────────────────────────────────────────────
  # Authentication
  # ──────────────────────────────────────────────────────────────

  /exactapi/register:
    post:
      tags: [Authentication]
      summary: Register a new user
      description: >
        Creates a new user account with role `read-write`.
        Rate-limited to 30 requests per 60 s per email.
        No auth token required.
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/RegisterRequest"
      responses:
        "200":
          description: Registration result
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: "#/components/schemas/SuccessMessage"
                  - $ref: "#/components/schemas/ErrorMessage"

  /exactapi/login:
    post:
      tags: [Authentication]
      summary: Login and obtain JWT
      description: >
        Authenticates a user and returns a JWT.
        Rate-limited to 30 requests per 60 s per email.
        No auth token required.
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/LoginRequest"
      responses:
        "200":
          description: JWT token
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/LoginResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "429":
          $ref: "#/components/responses/RateLimited"

  /exactapi/me:
    get:
      tags: [Authentication]
      summary: Get current user identity
      description: Returns `user_id` (email/sub) and `role` from the JWT claims.
      responses:
        "200":
          description: Current user info
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UserInfo"
        "401":
          $ref: "#/components/responses/Unauthorized"

  /exactapi/me/details:
    get:
      tags: [Authentication]
      summary: Get current user details
      description: Returns whether the unit associated with the user has been configured.
      responses:
        "200":
          description: User details
          content:
            application/json:
              schema:
                type: object
                properties:
                  unitConfigured:
                    type: boolean
        "401":
          $ref: "#/components/responses/Unauthorized"

  # ──────────────────────────────────────────────────────────────
  # Users (admin)
  # ──────────────────────────────────────────────────────────────

  /exactapi/admin/user/delete:
    post:
      tags: [Users]
      summary: Delete a user (admin)
      description: Permanently deletes a user account. Requires `admin` role.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [email]
              properties:
                email:
                  type: string
                  format: email
      responses:
        "200":
          description: Deletion result
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SuccessOrError"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          description: User not found

  /exactapi/update_password:
    post:
      tags: [Users]
      summary: Update a user's password (admin)
      description: Updates the password for any user. Requires `admin` role.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [email, new_password]
              properties:
                email:
                  type: string
                  format: email
                new_password:
                  type: string
      responses:
        "200":
          description: Update result
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SuccessOrError"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          description: User not found

  # ──────────────────────────────────────────────────────────────
  # Collections
  # ──────────────────────────────────────────────────────────────

  /exactapi/create_collection:
    post:
      tags: [Collections]
      summary: Create a time-series collection
      description: >
        Creates on-disk collection metadata and mmap file layout for a
        `org/site/unit/grid` scope. Source: `clarity:backend/src-tauri/src/api/storage.rs`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CollectionRequest"
      responses:
        "200":
          description: Collection created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SuccessOrError"
        "401":
          $ref: "#/components/responses/Unauthorized"

  /exactapi/update_collection:
    post:
      tags: [Collections]
      summary: Update collection metadata
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CollectionRequest"
      responses:
        "200":
          description: Collection updated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SuccessOrError"
        "401":
          $ref: "#/components/responses/Unauthorized"

  /exactapi/collection:
    get:
      tags: [Collections]
      summary: List all collections
      description: >
        Scans the data directory tree and returns metadata for every
        `org/site/unit/grid` collection that has a `metadata.json`.
      responses:
        "200":
          description: Array of collections
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/CollectionResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"

  /exactapi/collection/{id}:
    get:
      tags: [Collections]
      summary: Get collection by ID
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: Collection ID (derived from org/site/unit/grid path)
      responses:
        "200":
          description: Collection
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CollectionResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          description: Collection not found

  # ──────────────────────────────────────────────────────────────
  # Storage - Write
  # ──────────────────────────────────────────────────────────────

  /exactapi/write:
    post:
      tags: [Storage - Write]
      summary: Write time-series data (JSON)
      description: >
        Accepts either a `WriteRequest` (tag-keyed, `{tags: {name: [[ts_ms, val]]}}`)
        or a `BulkWriteRequest` (columnar batches). Scope is resolved from explicit
        `organization/site/unit/grid` fields or from the global TAG_SCOPE_MAP.
        Source: `clarity:backend/src-tauri/src/main.rs`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              oneOf:
                - $ref: "#/components/schemas/WriteRequest"
                - $ref: "#/components/schemas/BulkWriteRequest"
      responses:
        "200":
          description: Write accepted
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SuccessOrError"
        "401":
          $ref: "#/components/responses/Unauthorized"

  /exactapi/write_fast:
    post:
      tags: [Storage - Write]
      summary: Write time-series data (fast path)
      description: Same schema as `/write`; uses an optimised mmap write path.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              oneOf:
                - $ref: "#/components/schemas/WriteRequest"
                - $ref: "#/components/schemas/BulkWriteRequest"
      responses:
        "200":
          description: Write accepted
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SuccessOrError"
        "401":
          $ref: "#/components/responses/Unauthorized"

  /exactapi/write_buffered:
    post:
      tags: [Storage - Write]
      summary: Write time-series data (buffered)
      description: Writes are held in an in-memory `WriteBuffer` and flushed periodically.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              oneOf:
                - $ref: "#/components/schemas/WriteRequest"
                - $ref: "#/components/schemas/BulkWriteRequest"
      responses:
        "200":
          description: Write buffered
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SuccessOrError"
        "401":
          $ref: "#/components/responses/Unauthorized"

  /exactapi/write_buffer_stats:
    get:
      tags: [Storage - Write]
      summary: Write buffer statistics
      description: Returns current state of the in-memory write buffer.
      responses:
        "200":
          description: Buffer stats
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        "401":
          $ref: "#/components/responses/Unauthorized"

  # ──────────────────────────────────────────────────────────────
  # Storage - Query
  # ──────────────────────────────────────────────────────────────

  /exactapi/fast_query:
    get:
      tags: [Storage - Query]
      summary: Query time-series data (GET, JSON)
      description: >
        Returns `HashMap<tag, [(timestamp_ms, value)]>`.
        Scope fields are optional; if omitted, scope is resolved from the
        TAG_SCOPE_MAP. Supports an optional aggregation `pipeline`.
        Source: `clarity:backend/src-tauri/src/main.rs` + `clarity:backend/src-tauri/src/api/query.rs`.
      parameters:
        - $ref: "#/components/parameters/OrgQuery"
        - $ref: "#/components/parameters/SiteQuery"
        - $ref: "#/components/parameters/UnitQuery"
        - $ref: "#/components/parameters/GridQuery"
        - name: tags
          in: query
          required: true
          schema:
            type: array
            items:
              type: string
          style: form
          explode: true
        - name: start
          in: query
          required: true
          schema:
            type: integer
            format: int64
          description: Start timestamp in milliseconds (Unix epoch)
        - name: end
          in: query
          required: true
          schema:
            type: integer
            format: int64
          description: End timestamp in milliseconds (Unix epoch)
      responses:
        "200":
          description: Time-series data per tag
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TimeSeriesResult"
        "401":
          $ref: "#/components/responses/Unauthorized"
    post:
      tags: [Storage - Query]
      summary: Query time-series data (POST, JSON)
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/QueryRequestKairosAlike"
      responses:
        "200":
          description: Time-series data per tag
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TimeSeriesResult"
        "401":
          $ref: "#/components/responses/Unauthorized"

  /exactapi/fast_query_binary:
    get:
      tags: [Storage - Query]
      summary: Query time-series data (GET, binary)
      description: >
        Returns binary-encoded data (`application/octet-stream`).
        Format: per-tag `(timestamp_ms u64, value f32)` pairs.
        Supports push-down aggregation for single-step pipelines.
        Source: `clarity:backend/src-tauri/src/main.rs`.
      parameters:
        - $ref: "#/components/parameters/OrgQuery"
        - $ref: "#/components/parameters/SiteQuery"
        - $ref: "#/components/parameters/UnitQuery"
        - $ref: "#/components/parameters/GridQuery"
        - name: tags
          in: query
          required: true
          schema:
            type: array
            items:
              type: string
          style: form
          explode: true
        - name: start
          in: query
          required: true
          schema:
            type: integer
            format: int64
        - name: end
          in: query
          required: true
          schema:
            type: integer
            format: int64
      responses:
        "200":
          description: Binary time-series data
          content:
            application/octet-stream:
              schema:
                type: string
                format: binary
        "401":
          $ref: "#/components/responses/Unauthorized"
    post:
      tags: [Storage - Query]
      summary: Query time-series data (POST, binary)
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/QueryRequestKairosAlike"
      responses:
        "200":
          description: Binary time-series data
          content:
            application/octet-stream:
              schema:
                type: string
                format: binary
        "401":
          $ref: "#/components/responses/Unauthorized"

  /exactapi/fast_query_optimised:
    get:
      tags: [Storage - Query]
      summary: Query time-series data (GET, optimised binary — shared timestamps)
      description: >
        Returns binary format 2: shared timestamp array + per-tag validity bitmaps.
        Uses `QueryRequest` (explicit org/site/unit/grid required).
        Source: `clarity:backend/src-tauri/src/main.rs`.
      parameters:
        - $ref: "#/components/parameters/OrgRequired"
        - $ref: "#/components/parameters/SiteRequired"
        - $ref: "#/components/parameters/UnitRequired"
        - $ref: "#/components/parameters/GridRequired"
        - name: tags
          in: query
          required: true
          schema:
            type: array
            items:
              type: string
          style: form
          explode: true
        - name: start
          in: query
          required: true
          schema:
            type: integer
            format: int64
        - name: end
          in: query
          required: true
          schema:
            type: integer
            format: int64
      responses:
        "200":
          description: Optimised binary time-series data
          content:
            application/octet-stream:
              schema:
                type: string
                format: binary
        "401":
          $ref: "#/components/responses/Unauthorized"
    post:
      tags: [Storage - Query]
      summary: Query time-series data (POST, optimised binary — shared timestamps)
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/QueryRequest"
      responses:
        "200":
          description: Optimised binary time-series data
          content:
            application/octet-stream:
              schema:
                type: string
                format: binary
        "401":
          $ref: "#/components/responses/Unauthorized"

  /exactapi/lastlist:
    get:
      tags: [Storage - Query]
      summary: Latest value per tag (GET)
      description: >
        Returns the most recent `(timestamp_ms, value)` per requested tag.
        Reads only the latest day file per scope — memory-efficient.
        Source: `clarity:backend/src-tauri/src/main.rs`.
      parameters:
        - $ref: "#/components/parameters/OrgQuery"
        - $ref: "#/components/parameters/SiteQuery"
        - $ref: "#/components/parameters/UnitQuery"
        - $ref: "#/components/parameters/GridQuery"
        - name: tags
          in: query
          required: true
          schema:
            type: array
            items:
              type: string
          style: form
          explode: true
      responses:
        "200":
          description: Latest value per tag
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/LastListResult"
        "401":
          $ref: "#/components/responses/Unauthorized"
    post:
      tags: [Storage - Query]
      summary: Latest value per tag (POST)
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/LastListRequest"
      responses:
        "200":
          description: Latest value per tag
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/LastListResult"
        "401":
          $ref: "#/components/responses/Unauthorized"

  # ──────────────────────────────────────────────────────────────
  # Tag Mappings
  # ──────────────────────────────────────────────────────────────

  /exactapi/fast_query_tag_mapping:
    post:
      tags: [Tag Mappings]
      summary: Query time-series via tag-mapping spec
      description: >
        Resolves tag specs from the SQLite tag-mappings table, then queries
        binary storage and returns the result. Auth token required.
        Source: `clarity:backend/src-tauri/src/main.rs`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              description: Tag-mapping query spec (dynamic, resolved at runtime)
              additionalProperties: true
      responses:
        "200":
          description: Time-series binary or JSON data
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TimeSeriesResult"
            application/octet-stream:
              schema:
                type: string
                format: binary
        "401":
          $ref: "#/components/responses/Unauthorized"

  /exactapi/write_tag_mapping:
    post:
      tags: [Tag Mappings]
      summary: Write data via tag-mapping spec
      description: >
        Resolves tag specs from tag-mappings table, then writes data to binary storage.
        Source: `clarity:backend/src-tauri/src/main.rs`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              description: Tag-mapping write spec (dynamic)
              additionalProperties: true
      responses:
        "200":
          description: Write result
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SuccessOrError"
        "401":
          $ref: "#/components/responses/Unauthorized"

  # ──────────────────────────────────────────────────────────────
  # Sensor Data (Kairos-compatible)
  # ──────────────────────────────────────────────────────────────

  /sensordata/{id}/lastlist:
    post:
      tags: [Sensor Data]
      summary: Kairos-compatible last-list
      description: >
        Returns the latest value per tag in Kairos response format.
        `{id}` is ignored; scope is resolved from TAG_SCOPE_MAP.
        Source: `clarity:backend/src-tauri/src/main.rs`.
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: Ignored; present for Kairos API compatibility
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SensorLastListRequest"
      responses:
        "200":
          description: Last values in Kairos format
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SensorLastListResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"

  # ──────────────────────────────────────────────────────────────
  # Misc / Utility
  # ──────────────────────────────────────────────────────────────

  /exactapi/context/options:
    get:
      tags: [Collections]
      summary: Context options (org/site/unit)
      security: []
      description: Returns available orgs, sites, and units. Currently returns empty arrays.
      responses:
        "200":
          description: Context options
          content:
            application/json:
              schema:
                type: object
                properties:
                  orgs:
                    type: array
                    items:
                      type: string
                  sites:
                    type: array
                    items:
                      type: string
                  units:
                    type: array
                    items:
                      type: string

  /exactapi/mdns:
    get:
      tags: [Collections]
      summary: mDNS URL
      security: []
      description: Returns the mDNS URL for this Clarity instance (`https://clarity.local:3030`).
      responses:
        "200":
          description: mDNS URL
          content:
            application/json:
              schema:
                type: object
                properties:
                  url:
                    type: string
                    example: "https://clarity.local:3030"

  /exactapi/ca-cert:
    get:
      tags: [Authentication]
      summary: Download CA certificate
      security: []
      description: >
        Serves the self-signed CA certificate (PEM format) so remote machines
        can install it to trust HTTPS on `clarity.local`.
      responses:
        "200":
          description: PEM certificate file
          content:
            application/x-pem-file:
              schema:
                type: string
                format: binary
        "404":
          description: Certificate not found

  /exactapi/create_qdrant_collection:
    post:
      tags: [Storage - Write]
      summary: Create Qdrant vector collection
      description: Creates a Qdrant vector store collection for AI/embedding features.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Result
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        "401":
          $ref: "#/components/responses/Unauthorized"

  /docs:
    get:
      tags: [Authentication]
      summary: Swagger UI
      security: []
      description: Serves the built-in Swagger UI for this API.
      responses:
        "200":
          description: HTML page
          content:
            text/html:
              schema:
                type: string

  /exactapi/openapi.yaml:
    get:
      tags: [Authentication]
      summary: OpenAPI specification
      security: []
      description: Serves the bundled `openapi.yaml` file.
      responses:
        "200":
          description: YAML document
          content:
            text/yaml:
              schema:
                type: string

  # ──────────────────────────────────────────────────────────────
  # Ingest
  # ──────────────────────────────────────────────────────────────

  /ingest/health:
    get:
      tags: [Ingest]
      summary: Ingest health check
      security: []
      description: >
        Returns `{"status": "ok"}`.
        Source: `clarity:backend/src-tauri/src/processing_api/ingest.rs`.
      responses:
        "200":
          description: Health OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: "ok"

  /ingest/{client_id}/{tag_group}:
    post:
      tags: [Ingest]
      summary: Ingest time-series data
      description: >
        Writes time-series values keyed by `client_id`/`tag_group` scope.
        Auth required via JWT Bearer. Rate-limited: 1 000 req/min per client.
        Payload limit: 10 MB. Token is cached for 3 600 s.
        Source: `clarity:backend/src-tauri/src/processing_api/ingest.rs`.
      parameters:
        - name: client_id
          in: path
          required: true
          schema:
            type: string
          description: Client identifier (maps to ingest scope)
        - name: tag_group
          in: path
          required: true
          schema:
            type: string
          description: Tag group identifier
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/IngestPayload"
      responses:
        "200":
          description: Data ingested
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SuccessOrError"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "413":
          description: Payload too large (> 10 MB)
        "429":
          $ref: "#/components/responses/RateLimited"

  /ingest/backfill/{client_id}/{tag_group}:
    post:
      tags: [Ingest]
      summary: Backfill historical ingest data
      description: >
        Same as `/ingest/{client_id}/{tag_group}` but intended for historical backfill.
        Source: `clarity:backend/src-tauri/src/processing_api/ingest.rs`.
      parameters:
        - name: client_id
          in: path
          required: true
          schema:
            type: string
        - name: tag_group
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/IngestPayload"
      responses:
        "200":
          description: Backfill ingested
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SuccessOrError"
        "401":
          $ref: "#/components/responses/Unauthorized"

  # ──────────────────────────────────────────────────────────────
  # Elog
  # ──────────────────────────────────────────────────────────────

  /elog/group/data/health:
    get:
      tags: [Elog]
      summary: Elog health check
      security: []
      description: >
        Source: `clarity:backend/src-tauri/src/processing_api/elog.rs`.
      responses:
        "200":
          description: Health OK
          content:
            application/json:
              schema:
                type: object
    post:
      tags: [Elog]
      summary: Elog health check (POST)
      security: []
      responses:
        "200":
          description: Health OK

  /elog/group/data:
    post:
      tags: [Elog]
      summary: Elog group data
      description: >
        Returns grouped elog data for a given time range and elog ID.
        Source: `clarity:backend/src-tauri/src/processing_api/elog.rs`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ElogDataRequest"
      responses:
        "200":
          description: Elog group data
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        "401":
          $ref: "#/components/responses/Unauthorized"

  /elog/group/data/deviation-report:
    post:
      tags: [Elog]
      summary: Elog deviation report
      description: Returns a deviation report for the given elog/time range.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ElogDataRequest"
      responses:
        "200":
          description: Deviation report data
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        "401":
          $ref: "#/components/responses/Unauthorized"

  /elog/group/data/downloadcsv:
    post:
      tags: [Elog]
      summary: Download elog CSV
      description: >
        Downloads tag data as a CSV file for the given time range.
        Source: `clarity:backend/src-tauri/src/processing_api/elog.rs`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/DownloadCsvRequest"
      responses:
        "200":
          description: CSV file download
          content:
            text/csv:
              schema:
                type: string
                format: binary
        "401":
          $ref: "#/components/responses/Unauthorized"

  /elog/summary/download:
    post:
      tags: [Elog]
      summary: Download shift summary (Excel)
      description: >
        Returns an Excel `.xlsx` file with shift summary data for the given units and time range.
        Source: `clarity:backend/src-tauri/src/processing_api/elog.rs`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SummaryDownloadRequest"
      responses:
        "200":
          description: Excel file download
          content:
            application/vnd.openxmlformats-officedocument.spreadsheetml.sheet:
              schema:
                type: string
                format: binary
        "401":
          $ref: "#/components/responses/Unauthorized"

  # ──────────────────────────────────────────────────────────────
  # EMS (TODO-VERIFY)
  # ──────────────────────────────────────────────────────────────

  /dataflow/ems/kpiparams:
    get:
      tags: [EMS]
      summary: KPI parameters
      description: >
        > TODO-VERIFY: routes defined in `clarity:backend/src-tauri/src/processing_api/ems.rs`
        > but NOT confirmed registered in `main.rs` api_routes. May be proxied or inactive.
      parameters:
        - name: startTime
          in: query
          schema: { type: integer, format: int64 }
        - name: endTime
          in: query
          schema: { type: integer, format: int64 }
        - name: unitsId
          in: query
          schema: { type: string }
        - name: tagType
          in: query
          schema: { type: string }
        - name: agg
          in: query
          schema: { type: string }
      responses:
        "200":
          description: KPI parameter data
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
    post:
      tags: [EMS]
      summary: KPI parameters (POST)
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/KpiParamsRequest"
      responses:
        "200":
          description: KPI parameter data
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true

  /dataflow/ems/tags/summary:
    get:
      tags: [EMS]
      summary: Tag summary
      description: >
        > TODO-VERIFY: see `/dataflow/ems/kpiparams` note.
      parameters:
        - name: startTime
          in: query
          schema: { type: integer, format: int64 }
        - name: endTime
          in: query
          schema: { type: integer, format: int64 }
        - name: unitsId
          in: query
          schema: { type: string }
      responses:
        "200":
          description: Tag summary data
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
    post:
      tags: [EMS]
      summary: Tag summary (POST)
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Tag summary data
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true

  /dataflow/ems/tags/summary/download:
    post:
      tags: [EMS]
      summary: Tag summary download
      description: >
        > TODO-VERIFY: see `/dataflow/ems/kpiparams` note.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Download file
          content:
            application/octet-stream:
              schema:
                type: string
                format: binary

  /dataflow/ems/meters/group-config:
    get:
      tags: [EMS]
      summary: Meters group configuration
      description: >
        > TODO-VERIFY: see `/dataflow/ems/kpiparams` note.
      responses:
        "200":
          description: Group config
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
    post:
      tags: [EMS]
      summary: Meters group configuration (POST)
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Group config
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true

  # ──────────────────────────────────────────────────────────────
  # Monitor - Alarms
  # ──────────────────────────────────────────────────────────────
  # Note: Monitor routes have no JWT auth middleware applied.
  # Source: clarity:backend/src-tauri/src/monitor/api.rs

  /exactapi/monitor/alarm/snapshot:
    get:
      tags: [Monitor - Alarms]
      summary: Current alarm snapshot
      description: >
        Returns the latest `AlarmSnapshot` from the in-memory `SnapshotStore`.
        No auth required (TODO-VERIFY).
        Source: `clarity:backend/src-tauri/src/monitor/api.rs:147-154`.
      security: []
      responses:
        "200":
          description: Alarm snapshot
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true

  /exactapi/monitor/alarm/summary:
    get:
      tags: [Monitor - Alarms]
      summary: Alarm summary (all or per collection)
      description: >
        Returns `{collection_count, triggered_tags, timestamp_ms, tick_number, evaluation_duration_ms}`.
        Filter by `collection_id` query param for a single collection.
      security: []
      parameters:
        - name: collection_id
          in: query
          schema:
            type: string
          description: Optional — filter to a single collection
      responses:
        "200":
          description: Alarm summary
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AlarmSummary"

  /exactapi/monitor/alarm/triggered-only:
    get:
      tags: [Monitor - Alarms]
      summary: Triggered alarms only
      description: Returns only collections that have at least one triggered tag.
      security: []
      parameters:
        - name: collection_id
          in: query
          schema:
            type: string
      responses:
        "200":
          description: Triggered alarm collections
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true

  /exactapi/monitor/alarm/events:
    get:
      tags: [Monitor - Alarms]
      summary: Query alarm events
      description: >
        Paginated query over historical alarm events stored in SQLite.
        Source: `clarity:backend/src-tauri/src/monitor/api.rs:283-310`.
      security: []
      parameters:
        - name: from_ms
          in: query
          schema: { type: integer, format: int64 }
        - name: to_ms
          in: query
          schema: { type: integer, format: int64 }
        - name: collection_id
          in: query
          schema: { type: string }
        - name: tag_index
          in: query
          schema: { type: integer }
        - name: rule_id
          in: query
          schema: { type: string }
        - name: status
          in: query
          schema: { type: string }
        - name: limit
          in: query
          schema: { type: integer, default: 100, maximum: 1000 }
        - name: offset
          in: query
          schema: { type: integer, default: 0 }
      responses:
        "200":
          description: Paginated alarm events
          content:
            application/json:
              schema:
                type: object
                properties:
                  events:
                    type: array
                    items:
                      type: object
                      additionalProperties: true
                  total_count:
                    type: integer
                  has_more:
                    type: boolean

  /exactapi/monitor/alarm/events/active:
    get:
      tags: [Monitor - Alarms]
      summary: Active alarm events
      security: []
      description: Returns all currently active (unresolved) alarm events.
      responses:
        "200":
          description: Active events
          content:
            application/json:
              schema:
                type: object
                properties:
                  events:
                    type: array
                    items:
                      type: object
                      additionalProperties: true
                  total_count:
                    type: integer

  /exactapi/monitor/alarm/events/stats:
    get:
      tags: [Monitor - Alarms]
      summary: Alarm event statistics
      security: []
      parameters:
        - name: from_ms
          in: query
          required: true
          schema: { type: integer, format: int64 }
        - name: to_ms
          in: query
          required: true
          schema: { type: integer, format: int64 }
      responses:
        "200":
          description: Event statistics
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true

  /exactapi/monitor/alarm/events/{event_id}:
    get:
      tags: [Monitor - Alarms]
      summary: Get alarm event by ID
      security: []
      parameters:
        - name: event_id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Alarm event
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        "404":
          description: Event not found

  /exactapi/monitor/alarm/events/cleanup:
    delete:
      tags: [Monitor - Alarms]
      summary: Delete old alarm events
      security: []
      description: Removes events older than the specified timestamp.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [older_than_ms]
              properties:
                older_than_ms:
                  type: integer
                  format: int64
                  description: Delete events with timestamp before this value (ms)
      responses:
        "200":
          description: Deleted count
          content:
            application/json:
              schema:
                type: object
                properties:
                  deleted_count:
                    type: integer

  # ──────────────────────────────────────────────────────────────
  # Monitor - Rules
  # ──────────────────────────────────────────────────────────────

  /exactapi/monitor/rule:
    post:
      tags: [Monitor - Rules]
      summary: Create alarm rule
      security: []
      description: >
        Creates a new alarm rule definition.
        Source: `clarity:backend/src-tauri/src/monitor/api.rs:329-366`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateRuleRequest"
      responses:
        "201":
          description: Rule created
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
        "400":
          description: Invalid rule definition
    get:
      tags: [Monitor - Rules]
      summary: List alarm rules
      security: []
      description: >
        Returns all rules, or filtered by collection_id / tag_index.
        Source: `clarity:backend/src-tauri/src/monitor/api.rs:368-395`.
      parameters:
        - name: collection_id
          in: query
          schema: { type: string }
        - name: tag_index
          in: query
          schema: { type: integer }
        - name: enabled
          in: query
          schema: { type: boolean }
      responses:
        "200":
          description: Rule list
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/RuleDefinition"

  /exactapi/monitor/rule/{id}:
    get:
      tags: [Monitor - Rules]
      summary: Get alarm rule by ID
      security: []
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: string }
      responses:
        "200":
          description: Rule
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/RuleDefinition"
        "404":
          description: Rule not found
    put:
      tags: [Monitor - Rules]
      summary: Update alarm rule
      security: []
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: string }
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateRuleRequest"
      responses:
        "200":
          description: Update result
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
        "400":
          description: Invalid rule
    delete:
      tags: [Monitor - Rules]
      summary: Delete alarm rule
      security: []
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: string }
      responses:
        "200":
          description: Delete result
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
        "404":
          description: Rule not found

  # ──────────────────────────────────────────────────────────────
  # Monitor - Config
  # ──────────────────────────────────────────────────────────────

  /exactapi/monitor/config:
    get:
      tags: [Monitor - Config]
      summary: Get monitoring configuration
      security: []
      description: "Returns `{enabled: bool}`."
      responses:
        "200":
          description: Monitor config
          content:
            application/json:
              schema:
                type: object
                properties:
                  enabled:
                    type: boolean
    put:
      tags: [Monitor - Config]
      summary: Update monitoring configuration
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                enabled:
                  type: boolean
      responses:
        "200":
          description: Update result
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean

  /exactapi/monitor/collections:
    get:
      tags: [Monitor - Config]
      summary: List monitored collections
      security: []
      description: Returns all collections registered in the monitor config.
      responses:
        "200":
          description: Collection list
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  properties:
                    id:
                      type: string
                    enabled:
                      type: boolean
                    description:
                      type: string
                    tick_rate_ms:
                      type: integer
                      format: int64

  /exactapi/monitor/collections/{id}/enable:
    put:
      tags: [Monitor - Config]
      summary: Enable collection monitoring
      security: []
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: string }
          description: URL-encoded collection ID
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                tick_rate_ms:
                  type: integer
                  format: int64
                  description: Optional tick rate in milliseconds
      responses:
        "200":
          description: Enable result
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean

  /exactapi/monitor/collections/{id}/disable:
    put:
      tags: [Monitor - Config]
      summary: Disable collection monitoring
      security: []
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: string }
      responses:
        "200":
          description: Disable result
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean

  /exactapi/monitor/collections/{id}/tick-rate:
    put:
      tags: [Monitor - Config]
      summary: Set collection tick rate
      security: []
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: string }
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [tick_rate_ms]
              properties:
                tick_rate_ms:
                  type: integer
                  format: int64
      responses:
        "200":
          description: Tick rate updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean

  /exactapi/monitor/status:
    get:
      tags: [Monitor - Config]
      summary: Monitor system status
      security: []
      description: >
        Returns `{enabled, collection_count, rule_count, last_snapshot_ms, tick_number}`.
      responses:
        "200":
          description: System status
          content:
            application/json:
              schema:
                type: object
                properties:
                  enabled:
                    type: boolean
                  collection_count:
                    type: integer
                  rule_count:
                    type: integer
                  last_snapshot_ms:
                    type: integer
                    format: int64
                  tick_number:
                    type: integer

  # ──────────────────────────────────────────────────────────────
  # Backup
  # ──────────────────────────────────────────────────────────────
  # Note: Backup routes currently have no JWT auth middleware applied.
  # Source: clarity:backend/src-tauri/src/backup/api.rs

  /api/admin/backup/status:
    get:
      tags: [Backup]
      summary: Backup status
      security: []
      description: >
        Returns last backup time, running state, and last sqlite/timeseries backup results.
        Source: `clarity:backend/src-tauri/src/backup/api.rs`.
      responses:
        "200":
          description: Backup status
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BackupState"

  /api/admin/backup/list:
    get:
      tags: [Backup]
      summary: List backup files
      security: []
      description: Returns arrays of `sqlite_backups` and `timeseries_backups` file info.
      responses:
        "200":
          description: Backup file list
          content:
            application/json:
              schema:
                type: object
                properties:
                  sqlite_backups:
                    type: array
                    items:
                      $ref: "#/components/schemas/BackupFileInfo"
                  timeseries_backups:
                    type: array
                    items:
                      $ref: "#/components/schemas/BackupFileInfo"

  /api/admin/backup/config:
    get:
      tags: [Backup]
      summary: Get backup configuration
      security: []
      description: >
        Source: `clarity:backend/src-tauri/src/backup/state.rs`.
      responses:
        "200":
          description: Backup config
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BackupConfig"
    put:
      tags: [Backup]
      summary: Update backup configuration
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/BackupConfig"
      responses:
        "200":
          description: Config updated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SuccessOrError"

  /api/admin/backup/restore/sqlite:
    post:
      tags: [Backup]
      summary: Restore SQLite backup
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [file_name]
              properties:
                file_name:
                  type: string
                  description: Name of the backup file to restore
      responses:
        "200":
          description: Restore result
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SuccessOrError"

  /api/admin/backup/restore/timeseries:
    post:
      tags: [Backup]
      summary: Restore timeseries backup
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [file_name]
              properties:
                file_name:
                  type: string
                  description: Name of the backup archive to restore
      responses:
        "200":
          description: Restore result
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SuccessOrError"

  # ──────────────────────────────────────────────────────────────
  # PI Connector
  # ──────────────────────────────────────────────────────────────

  /exactapi/pi/list_asset_servers:
    post:
      tags: [PI Connector]
      summary: List PI asset servers
      description: >
        Returns available PI Web API asset servers.
        Source: `clarity:backend/src-tauri/src/connectors/webpi/webpi_meta_connector.rs:1580`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PiConnectionRequest"
      responses:
        "200":
          description: Asset servers
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        "401":
          $ref: "#/components/responses/Unauthorized"

  /exactapi/pi/list_databases:
    post:
      tags: [PI Connector]
      summary: List PI asset databases
      description: >
        Returns asset databases for a given server.
        Source: `clarity:backend/src-tauri/src/connectors/webpi/webpi_meta_connector.rs:1585`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              allOf:
                - $ref: "#/components/schemas/PiConnectionRequest"
                - type: object
                  properties:
                    web_id:
                      type: string
                      description: Server WebId
      responses:
        "200":
          description: Asset databases
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        "401":
          $ref: "#/components/responses/Unauthorized"

  /exactapi/pi/list_children:
    post:
      tags: [PI Connector]
      summary: List PI element children
      description: >
        Returns child elements for a given element or database.
        Source: `clarity:backend/src-tauri/src/connectors/webpi/webpi_meta_connector.rs:1590`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              allOf:
                - $ref: "#/components/schemas/PiConnectionRequest"
                - type: object
                  properties:
                    web_id:
                      type: string
                    is_database:
                      type: boolean
      responses:
        "200":
          description: Child elements
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        "401":
          $ref: "#/components/responses/Unauthorized"

  /exactapi/pi/list_attributes:
    post:
      tags: [PI Connector]
      summary: List PI element attributes
      description: >
        Source: `clarity:backend/src-tauri/src/connectors/webpi/webpi_meta_connector.rs:1595`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              allOf:
                - $ref: "#/components/schemas/PiConnectionRequest"
                - type: object
                  properties:
                    web_id:
                      type: string
      responses:
        "200":
          description: Element attributes
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        "401":
          $ref: "#/components/responses/Unauthorized"

  /exactapi/pi/onboard_unit:
    post:
      tags: [PI Connector]
      summary: Onboard PI unit into Clarity
      description: >
        Creates Clarity collection and tag mappings from a PI element hierarchy.
        Source: `clarity:backend/src-tauri/src/connectors/webpi/webpi_meta_connector.rs:1600`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Onboard result
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        "401":
          $ref: "#/components/responses/Unauthorized"

  /exactapi/pi/start_backfill:
    post:
      tags: [PI Connector]
      summary: Start PI historical data backfill
      description: >
        Initiates an async backfill task that fetches historical data from PI
        with exponential-backoff retry and event-density-based windowing.
        Source: `clarity:backend/src-tauri/src/connectors/webpi/webpi_meta_connector.rs:1610`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Backfill started
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        "401":
          $ref: "#/components/responses/Unauthorized"

  /exactapi/pi/dump_hierarchy:
    post:
      tags: [PI Connector]
      summary: Dump PI element hierarchy
      description: >
        Returns the full PI element/attribute hierarchy from a given root element.
        Source: `clarity:backend/src-tauri/src/connectors/webpi/webpi_meta_connector.rs:1617`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              allOf:
                - $ref: "#/components/schemas/PiConnectionRequest"
                - type: object
                  properties:
                    web_id:
                      type: string
      responses:
        "200":
          description: Hierarchy dump
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        "401":
          $ref: "#/components/responses/Unauthorized"

  /exactapi/pi/map_tags:
    post:
      tags: [PI Connector]
      summary: Map PI tags to Clarity tags
      description: >
        Creates or updates tag mappings from PI attribute metadata.
        Source: `clarity:backend/src-tauri/src/connectors/webpi/webpi_meta_connector.rs:1623`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Mapping result
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        "401":
          $ref: "#/components/responses/Unauthorized"

  /exactapi/pi/all_hierarchy_stream:
    get:
      tags: [PI Connector]
      summary: Stream full PI hierarchy (SSE)
      description: >
        Server-sent events stream of the complete PI hierarchy traversal.
        Source: `clarity:backend/src-tauri/src/connectors/webpi/webpi_meta_connector.rs:1795`.
      responses:
        "200":
          description: SSE stream
          content:
            text/event-stream:
              schema:
                type: string
        "401":
          $ref: "#/components/responses/Unauthorized"
    post:
      tags: [PI Connector]
      summary: Stream full PI hierarchy (SSE, POST)
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PiConnectionRequest"
      responses:
        "200":
          description: SSE stream
          content:
            text/event-stream:
              schema:
                type: string

  /exactapi/pi/dump_and_stream:
    get:
      tags: [PI Connector]
      summary: Dump hierarchy and stream results (SSE)
      description: >
        Source: `clarity:backend/src-tauri/src/connectors/webpi/webpi_meta_connector.rs:1800`.
      responses:
        "200":
          description: SSE stream
          content:
            text/event-stream:
              schema:
                type: string
    post:
      tags: [PI Connector]
      summary: Dump hierarchy and stream results (SSE, POST)
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PiConnectionRequest"
      responses:
        "200":
          description: SSE stream
          content:
            text/event-stream:
              schema:
                type: string

  # ──────────────────────────────────────────────────────────────
  # ADK (Google ADK proxy)
  # ──────────────────────────────────────────────────────────────

  /apps/{path}:
    post:
      tags: [ADK]
      summary: Create ADK session (proxy)
      description: >
        Proxies to `https://localhost:8000/apps/{path}` (pulse_multi_agents).
        Used by the chat interface to create Google ADK agent sessions.
        Source: `clarity:backend/src-tauri/src/api/google_adk.rs:44-68`.
      security: []
      parameters:
        - name: path
          in: path
          required: true
          schema:
            type: string
          description: ADK app path (forwarded verbatim)
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: ADK session response (forwarded)
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true

  /run_sse:
    post:
      tags: [ADK]
      summary: Run ADK agent (SSE proxy)
      description: >
        Proxies to ADK SSE endpoint.
        Source: `clarity:backend/src-tauri/src/api/google_adk.rs:108-111`.
      security: []
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: ADK SSE response (forwarded)
          content:
            text/event-stream:
              schema:
                type: string

  # ──────────────────────────────────────────────────────────────
  # Process Logs
  # ──────────────────────────────────────────────────────────────

  /process-logs:
    get:
      tags: [Process Logs]
      summary: Process log viewer
      security: []
      description: >
        Returns an HTML page for viewing sub-process logs.
        Source: `clarity:backend/src-tauri/src/process_manager/http_api.rs`.
      responses:
        "200":
          description: HTML log viewer
          content:
            text/html:
              schema:
                type: string

  /process-logs/ws:
    get:
      tags: [Process Logs]
      summary: Process log WebSocket stream
      security: []
      description: >
        WebSocket endpoint; streams `LogEntry` objects as JSON messages
        `{type: "log", entry: {...}}`.
        Source: `clarity:backend/src-tauri/src/process_manager/http_api.rs`.
      responses:
        "101":
          description: WebSocket upgrade

  # ──────────────────────────────────────────────────────────────
  # OPC Network
  # ──────────────────────────────────────────────────────────────

  /opc-network:
    get:
      tags: [Connections]
      summary: OPC network info
      security: []
      description: >
        Returns a newly generated ObjectId. OPC-UA connector is not yet fully implemented.
        Source: `clarity:backend/src-tauri/src/main.rs:3118-3129`.
      responses:
        "200":
          description: OPC network object
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string

  # ──────────────────────────────────────────────────────────────
  # Attachments
  # ──────────────────────────────────────────────────────────────

  /exactapi/attachments:
    get:
      tags: [Attachments]
      summary: List attachment containers
      description: >
        Returns all containers (folders). Default containers: `tasks`, `incidents`,
        `uploads`, `mail`, `pulselogo`.
        Source: `clarity:backend/src-tauri/src/sqlite_api/api/warp_attachments.rs:445`.
      responses:
        "200":
          description: Container list
          content:
            application/json:
              schema:
                type: array
                items:
                  type: string
        "401":
          $ref: "#/components/responses/Unauthorized"
    post:
      tags: [Attachments]
      summary: Create attachment container
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
      responses:
        "200":
          description: Container created
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        "401":
          $ref: "#/components/responses/Unauthorized"

  /exactapi/attachments/{container}:
    get:
      tags: [Attachments]
      summary: Get container info
      parameters:
        - name: container
          in: path
          required: true
          schema: { type: string }
      responses:
        "200":
          description: Container info
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        "401":
          $ref: "#/components/responses/Unauthorized"
    delete:
      tags: [Attachments]
      summary: Delete container
      parameters:
        - name: container
          in: path
          required: true
          schema: { type: string }
      responses:
        "200":
          description: Container deleted
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        "401":
          $ref: "#/components/responses/Unauthorized"

  /exactapi/attachments/{container}/files:
    get:
      tags: [Attachments]
      summary: List files in container
      parameters:
        - name: container
          in: path
          required: true
          schema: { type: string }
      responses:
        "200":
          description: File list
          content:
            application/json:
              schema:
                type: array
                items:
                  type: string
        "401":
          $ref: "#/components/responses/Unauthorized"

  /exactapi/attachments/{container}/files/{filename}:
    get:
      tags: [Attachments]
      summary: Get file metadata
      parameters:
        - name: container
          in: path
          required: true
          schema: { type: string }
        - name: filename
          in: path
          required: true
          schema: { type: string }
      responses:
        "200":
          description: File info
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        "401":
          $ref: "#/components/responses/Unauthorized"
    delete:
      tags: [Attachments]
      summary: Delete file
      parameters:
        - name: container
          in: path
          required: true
          schema: { type: string }
        - name: filename
          in: path
          required: true
          schema: { type: string }
      responses:
        "200":
          description: File deleted
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        "401":
          $ref: "#/components/responses/Unauthorized"

  /exactapi/attachments/{container}/download/{filename}:
    get:
      tags: [Attachments]
      summary: Download file
      parameters:
        - name: container
          in: path
          required: true
          schema: { type: string }
        - name: filename
          in: path
          required: true
          schema: { type: string }
      responses:
        "200":
          description: File content
          content:
            application/octet-stream:
              schema:
                type: string
                format: binary
        "401":
          $ref: "#/components/responses/Unauthorized"

  /exactapi/attachments/{container}/upload:
    post:
      tags: [Attachments]
      summary: Upload file(s)
      description: >
        Accepts multipart/form-data. Single-file mode also supported (raw body).
        Source: `clarity:backend/src-tauri/src/sqlite_api/api/warp_attachments.rs:366`.
      parameters:
        - name: container
          in: path
          required: true
          schema: { type: string }
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              properties:
                file:
                  type: string
                  format: binary
      responses:
        "200":
          description: Upload result
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: "uploaded"
                  count:
                    type: integer
        "400":
          description: No files uploaded
        "401":
          $ref: "#/components/responses/Unauthorized"

  # ──────────────────────────────────────────────────────────────
  # SQLite CRUD — selected models
  # ──────────────────────────────────────────────────────────────

  /exactapi/units:
    get:
      tags: [SQLite CRUD]
      summary: List units
      responses:
        "200":
          description: Unit records
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  additionalProperties: true
        "401":
          $ref: "#/components/responses/Unauthorized"
    post:
      tags: [SQLite CRUD]
      summary: Create unit
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Created unit
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        "401":
          $ref: "#/components/responses/Unauthorized"

  /exactapi/units/{id}:
    get:
      tags: [SQLite CRUD]
      summary: Get unit by ID
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: string }
      responses:
        "200":
          description: Unit record
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        "401":
          $ref: "#/components/responses/Unauthorized"

  /exactapi/units/{id}/equipment:
    get:
      tags: [SQLite CRUD]
      summary: List equipment for unit
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: string }
      responses:
        "200":
          description: Equipment list
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  additionalProperties: true
        "401":
          $ref: "#/components/responses/Unauthorized"

  /exactapi/units/{id}/dashboards:
    get:
      tags: [Dashboards]
      summary: List dashboards for unit
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: string }
      responses:
        "200":
          description: Dashboard list
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  additionalProperties: true
        "401":
          $ref: "#/components/responses/Unauthorized"

  /exactapi/units/{id}/incidents:
    get:
      tags: [SQLite CRUD]
      summary: List incidents for unit
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: string }
      responses:
        "200":
          description: Incident list
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  additionalProperties: true
        "401":
          $ref: "#/components/responses/Unauthorized"

  /exactapi/units/{id}/tagmeta:
    get:
      tags: [Tags]
      summary: List tag metadata for unit
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: string }
      responses:
        "200":
          description: Tag metadata list
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  additionalProperties: true
        "401":
          $ref: "#/components/responses/Unauthorized"

  /exactapi/sites:
    get:
      tags: [SQLite CRUD]
      summary: List sites
      responses:
        "200":
          description: Site records
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  additionalProperties: true
        "401":
          $ref: "#/components/responses/Unauthorized"
    post:
      tags: [SQLite CRUD]
      summary: Create site
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Created site
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        "401":
          $ref: "#/components/responses/Unauthorized"

  /exactapi/orgs:
    get:
      tags: [SQLite CRUD]
      summary: List organisations
      responses:
        "200":
          description: Org records
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  additionalProperties: true
        "401":
          $ref: "#/components/responses/Unauthorized"
    post:
      tags: [SQLite CRUD]
      summary: Create organisation
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Created org
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        "401":
          $ref: "#/components/responses/Unauthorized"

  /exactapi/tagmeta:
    get:
      tags: [Tags]
      summary: List all tag metadata
      responses:
        "200":
          description: Tagmeta records
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  additionalProperties: true
        "401":
          $ref: "#/components/responses/Unauthorized"
    post:
      tags: [Tags]
      summary: Create tag metadata record
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Created tagmeta
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        "401":
          $ref: "#/components/responses/Unauthorized"

  /exactapi/dashboards:
    get:
      tags: [Dashboards]
      summary: List all dashboards
      responses:
        "200":
          description: Dashboard records
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  additionalProperties: true
        "401":
          $ref: "#/components/responses/Unauthorized"
    post:
      tags: [Dashboards]
      summary: Create dashboard
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Created dashboard
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        "401":
          $ref: "#/components/responses/Unauthorized"

  /exactapi/dashboards/{id}:
    get:
      tags: [Dashboards]
      summary: Get dashboard by ID
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: string }
      responses:
        "200":
          description: Dashboard record
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        "401":
          $ref: "#/components/responses/Unauthorized"

  /exactapi/connections:
    get:
      tags: [Connections]
      summary: List connections
      responses:
        "200":
          description: Connection records
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  additionalProperties: true
        "401":
          $ref: "#/components/responses/Unauthorized"
    post:
      tags: [Connections]
      summary: Create connection
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Created connection
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        "401":
          $ref: "#/components/responses/Unauthorized"

  /exactapi/incidents:
    get:
      tags: [SQLite CRUD]
      summary: List incidents
      responses:
        "200":
          description: Incident records
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  additionalProperties: true
        "401":
          $ref: "#/components/responses/Unauthorized"
    post:
      tags: [SQLite CRUD]
      summary: Create incident
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Created incident
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        "401":
          $ref: "#/components/responses/Unauthorized"

  # ──────────────────────────────────────────────────────────────
  # Order Portal  (server: http://localhost:8080)
  # ──────────────────────────────────────────────────────────────

  /health:
    get:
      tags: [Order Portal]
      summary: Order Portal health check
      security: []
      servers:
        - url: "http://localhost:8080"
      description: >
        Source: `clarity:order_portal/app/main.py:49`.
      responses:
        "200":
          description: Health OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: "ok"

  /orders:
    post:
      tags: [Order Portal]
      summary: Create order
      security: []
      servers:
        - url: "http://localhost:8080"
      description: >
        Creates a new purchase order with status `pending`.
        Source: `clarity:order_portal/app/routes/orders.py`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/OrderCreate"
      responses:
        "201":
          description: Order created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OrderResponse"
        "409":
          description: order_id already exists

  /orders/{order_id}:
    get:
      tags: [Order Portal]
      summary: Get order
      security: []
      servers:
        - url: "http://localhost:8080"
      description: >
        Source: `clarity:order_portal/app/routes/orders.py`.
      parameters:
        - name: order_id
          in: path
          required: true
          schema: { type: string }
      responses:
        "200":
          description: Order
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OrderResponse"
        "404":
          description: Order not found

  /activate:
    post:
      tags: [Order Portal]
      summary: Activate license
      security: []
      servers:
        - url: "http://localhost:8080"
      description: >
        Validates the machine fingerprint challenge and issues a signed JWT license token.
        Challenge is `base64url(JSON({order_id, fp_cpu, fp_board, fp_disk, nonce}))`.
        Source: `clarity:order_portal/app/routes/activate.py`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [challenge]
              properties:
                challenge:
                  type: string
                  description: base64url-encoded JSON challenge from the Tauri app
      responses:
        "200":
          description: License token
          content:
            application/json:
              schema:
                type: object
                properties:
                  token:
                    type: string
                    description: Signed Ed25519 JWT license token
        "400":
          description: Invalid challenge
        "404":
          description: Order not found
        "409":
          description: Already activated / revoked / expired / duplicate nonce

  /renew:
    post:
      tags: [Order Portal]
      summary: Renew license
      security: []
      servers:
        - url: "http://localhost:8080"
      description: >
        Issues a new JWT with the same hardware fingerprint but a fresh expiry date.
        Source: `clarity:order_portal/app/routes/renew.py`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [order_id, new_expires_at]
              properties:
                order_id:
                  type: string
                new_expires_at:
                  type: integer
                  format: int64
                  description: New expiry as Unix timestamp (seconds)
                old_token:
                  type: string
                  description: Optional — old token to verify before issuing renewal
      responses:
        "200":
          description: Renewed token
          content:
            application/json:
              schema:
                type: object
                properties:
                  token:
                    type: string
        "403":
          description: Revoked order
        "409":
          description: Not yet activated / in transfer state

  /revoke:
    post:
      tags: [Order Portal]
      summary: Revoke license (admin)
      security: []
      servers:
        - url: "http://localhost:8080"
      description: >
        Marks an order as revoked. Future renewal attempts will be rejected.
        Source: `clarity:order_portal/app/routes/revoke.py`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [order_id]
              properties:
                order_id:
                  type: string
      responses:
        "200":
          description: Order revoked
          content:
            application/json:
              schema:
                type: object
                properties:
                  order_id:
                    type: string
                  status:
                    type: string
                    example: "revoked"
        "409":
          description: Already revoked

  /transfer:
    post:
      tags: [Order Portal]
      summary: Transfer license to new machine (admin)
      security: []
      servers:
        - url: "http://localhost:8080"
      description: >
        Clears stored hardware fingerprints and resets order to `pending`.
        Customer must re-activate on the new machine.
        Source: `clarity:order_portal/app/routes/transfer.py`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [order_id]
              properties:
                order_id:
                  type: string
      responses:
        "200":
          description: Transfer initiated
          content:
            application/json:
              schema:
                type: object
                properties:
                  order_id:
                    type: string
                  status:
                    type: string
                    example: "pending"
                  message:
                    type: string
        "403":
          description: Revoked order
        "409":
          description: Order is already pending

  /status/{order_id}:
    get:
      tags: [Order Portal]
      summary: Get license status
      security: []
      servers:
        - url: "http://localhost:8080"
      description: >
        Returns current order status and days remaining.
        Source: `clarity:order_portal/app/routes/status.py`.
      parameters:
        - name: order_id
          in: path
          required: true
          schema: { type: string }
      responses:
        "200":
          description: License status
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/StatusResponse"
        "404":
          description: Order not found

# ──────────────────────────────────────────────────────────────
# Components
# ──────────────────────────────────────────────────────────────

components:

  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: >
        JWT issued by `POST /exactapi/login`. Claims include `sub` (email),
        `role` (`admin` | `read-write` | `read-only`), `units_id`, `sites_id`,
        `orgs_id`, `exp`, `token_never_expires`.
        Source: `clarity:backend/src-tauri/src/auth.rs`.

  parameters:
    OrgQuery:
      name: organization
      in: query
      schema: { type: string }
      description: Organisation name (optional — resolved from TAG_SCOPE_MAP if omitted)
    SiteQuery:
      name: site
      in: query
      schema: { type: string }
      description: Site name (optional)
    UnitQuery:
      name: unit
      in: query
      schema: { type: string }
      description: Unit name (optional)
    GridQuery:
      name: grid
      in: query
      schema: { type: string }
      description: Grid name (optional)
    OrgRequired:
      name: organization
      in: query
      required: true
      schema: { type: string }
    SiteRequired:
      name: site
      in: query
      required: true
      schema: { type: string }
    UnitRequired:
      name: unit
      in: query
      required: true
      schema: { type: string }
    GridRequired:
      name: grid
      in: query
      required: true
      schema: { type: string }

  responses:
    Unauthorized:
      description: Missing or invalid JWT Bearer token
      content:
        application/json:
          schema:
            type: object
            properties:
              error:
                type: string
                example: "Unauthorized - Missing or Invalid Token"
    RateLimited:
      description: Rate limit exceeded
      content:
        application/json:
          schema:
            type: object
            properties:
              error:
                type: string
              code:
                type: string
                example: "RATE_LIMIT_EXCEEDED"

  schemas:

    # ── Auth ──

    RegisterRequest:
      type: object
      required: [name, email, password]
      properties:
        name:
          type: string
        email:
          type: string
          format: email
        password:
          type: string
      description: >
        Source: `clarity:backend/src-tauri/src/main.rs:2349-2352`.

    LoginRequest:
      type: object
      required: [email, password]
      properties:
        email:
          type: string
          format: email
        password:
          type: string
        name:
          type: string
          description: Optional — informational only
      description: >
        Source: `clarity:backend/src-tauri/src/main.rs:2371-2373`.

    LoginResponse:
      type: object
      properties:
        token:
          type: string
          description: Signed JWT (HS256). Payload includes sub, role, units_id, sites_id, orgs_id, exp.
      description: >
        Source: `clarity:backend/src-tauri/src/main.rs:2380`.

    UserInfo:
      type: object
      properties:
        user_id:
          type: string
          description: Subject claim (email)
        role:
          type: string
          enum: [admin, read-write, read-only]
      description: >
        Source: `clarity:backend/src-tauri/src/main.rs:2469`.

    SuccessMessage:
      type: object
      properties:
        success:
          type: boolean
        message:
          type: string

    ErrorMessage:
      type: object
      properties:
        success:
          type: boolean
          example: false
        error:
          type: string

    SuccessOrError:
      type: object
      properties:
        success:
          type: boolean
        message:
          type: string
        error:
          type: string

    # ── Collections ──

    CollectionRequest:
      type: object
      required: [organization, site, unit, grid, interval_ms, tags]
      properties:
        organization:
          type: string
        site:
          type: string
        unit:
          type: string
        grid:
          type: string
        interval_ms:
          type: integer
          format: int64
          description: Sample interval in milliseconds
        interval:
          type: integer
          format: int64
          description: Deprecated — use interval_ms
        tags:
          type: array
          items:
            type: string
        descriptions:
          type: array
          items:
            type: string
          description: Optional description per tag (same length as tags)
      description: >
        Source: `clarity:backend/src-tauri/src/main.rs` (CollectionRequest struct).

    CollectionResponse:
      type: object
      properties:
        organization:
          type: string
        site:
          type: string
        unit:
          type: string
        grid:
          type: string
        metadata:
          $ref: "#/components/schemas/CollectionMetadata"
        meta_data:
          $ref: "#/components/schemas/CollectionMetadata"
          description: Alias for metadata (backwards compat)
      description: >
        Source: `clarity:backend/src-tauri/src/main.rs`.

    CollectionMetadata:
      type: object
      properties:
        id:
          type: string
        interval_ms:
          type: integer
          format: int64
        interval:
          type: integer
          format: int64
          description: Deprecated
        tags:
          type: array
          items:
            type: string
        descriptions:
          type: array
          items:
            type: string
      description: >
        Source: `clarity:backend/src-tauri/src/api/storage.rs` (CollectionMetadata struct).

    # ── Write ──

    WriteRequest:
      type: object
      properties:
        organization:
          type: string
        site:
          type: string
        unit:
          type: string
        grid:
          type: string
        tags:
          type: object
          additionalProperties:
            type: array
            items:
              type: array
              items:
                oneOf:
                  - type: integer
                    format: int64
                  - type: number
                    nullable: true
              minItems: 2
              maxItems: 2
          description: "Map of tag_name → array of [timestamp_ms, value] pairs"
      description: >
        Tag-keyed write format. Scope can be omitted; resolved from TAG_SCOPE_MAP.
        Source: `clarity:backend/src-tauri/src/main.rs` (WriteRequest struct).

    BulkWriteRequest:
      type: object
      properties:
        organization:
          type: string
        site:
          type: string
        unit:
          type: string
        grid:
          type: string
        data:
          type: array
          items:
            $ref: "#/components/schemas/DataPointBatch"
      description: >
        Columnar batch write. Source: `clarity:backend/src-tauri/src/main.rs` (BulkWriteRequest struct).

    DataPointBatch:
      type: object
      required: [timestamps, tag_values]
      properties:
        timestamps:
          type: array
          items:
            type: integer
            format: int64
        tag_values:
          type: object
          additionalProperties:
            type: array
            items:
              type: number
              nullable: true
      description: >
        Source: `clarity:backend/src-tauri/src/main.rs` (DataPointBatch struct).

    # ── Query ──

    QueryRequest:
      type: object
      required: [organization, site, unit, grid, tags, start, end]
      properties:
        organization:
          type: string
        site:
          type: string
        unit:
          type: string
        grid:
          type: string
        tags:
          type: array
          items:
            type: string
        start:
          type: integer
          format: int64
          description: Start timestamp ms (Unix epoch)
        end:
          type: integer
          format: int64
          description: End timestamp ms (Unix epoch)
        pipeline:
          $ref: "#/components/schemas/AggregationPipeline"
      description: >
        Used by `fast_query_optimised`. All scope fields required.
        Source: `clarity:backend/src-tauri/src/main.rs` (QueryRequest struct).

    QueryRequestKairosAlike:
      type: object
      required: [tags, start, end]
      properties:
        organization:
          type: string
        site:
          type: string
        unit:
          type: string
        grid:
          type: string
        tags:
          type: array
          items:
            type: string
        start:
          type: integer
          format: int64
        end:
          type: integer
          format: int64
        pipeline:
          $ref: "#/components/schemas/AggregationPipeline"
      description: >
        Used by `fast_query` and `fast_query_binary`. Scope fields optional;
        resolved from TAG_SCOPE_MAP when omitted.
        Source: `clarity:backend/src-tauri/src/main.rs` (QueryRequestKairosAlike struct).

    AggregationPipeline:
      type: object
      additionalProperties:
        type: array
        items:
          $ref: "#/components/schemas/AggregationStep"
      description: >
        Map of tag_name → ordered list of aggregation steps.
        Source: `clarity:backend/src-tauri/src/api/aggregator.rs`.

    AggregationStep:
      type: object
      required: [op]
      properties:
        op:
          type: string
          enum:
            [Mean, Avg, Sum, Min, Max, Count, First, Last, Percentile, Histogram,
             LeastSquares, Rate, Scale, Score, Filter, Threshold]
          description: Aggregation operation name
        bucket:
          type: integer
          format: int64
          description: Bucket size in milliseconds (required for time-bucketed ops)
        params:
          type: object
          additionalProperties: true
          description: Additional op-specific parameters (e.g. percentile value, scale factor)
      description: >
        Single step in an aggregation pipeline.
        Source: `clarity:backend/src-tauri/src/api/aggregator.rs`.

    TimeSeriesResult:
      type: object
      additionalProperties:
        type: array
        items:
          type: array
          items:
            oneOf:
              - type: integer
                format: int64
              - type: number
                nullable: true
          minItems: 2
          maxItems: 2
      description: "Map of tag_name → [[timestamp_ms, value], ...]"

    LastListRequest:
      type: object
      required: [tags]
      properties:
        organization:
          type: string
        site:
          type: string
        unit:
          type: string
        grid:
          type: string
        tags:
          type: array
          items:
            type: string
      description: >
        Source: `clarity:backend/src-tauri/src/main.rs` (LastListRequest struct).

    LastListResult:
      type: object
      additionalProperties:
        type: array
        items:
          type: array
          items:
            oneOf:
              - type: integer
                format: int64
              - type: number
                nullable: true
          minItems: 2
          maxItems: 2
          description: "[[timestamp_ms, value]] — single element or empty array"
      description: "Map of tag_name → [[timestamp_ms, value]] (single latest point per tag)"

    SensorLastListRequest:
      type: object
      required: [query]
      properties:
        query:
          type: object
          required: [vars]
          properties:
            vars:
              type: array
              items:
                type: string
              description: List of tag names
      description: >
        Kairos-compatible request format.
        Source: `clarity:backend/src-tauri/src/main.rs` (SensorLastListRequest struct).

    SensorLastListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            type: object
            properties:
              tag:
                type: string
              cached:
                type: string
                example: "true"
              data:
                type: array
                items:
                  type: array
                  items:
                    type: number
                description: "[[timestamp_ms, value]] or [[]] if no data"

    # ── Ingest ──

    IngestPayload:
      type: object
      additionalProperties: true
      description: >
        Payload format for ingest endpoint. Structure mirrors the tag-keyed write format.
        Auth token required. Rate-limited: 1 000 req/min per client_id.
        Payload limit: 10 MB. Token cached for 3 600 s.
        Source: `clarity:backend/src-tauri/src/processing_api/ingest.rs`.

    # ── Elog ──

    ElogDataRequest:
      type: object
      properties:
        elogId:
          type: string
        startTime:
          type: integer
          format: int64
        endTime:
          type: integer
          format: int64
        customerId:
          type: string
        agg:
          type: string
      description: >
        Source: `clarity:backend/src-tauri/src/processing_api/elog.rs`.

    DownloadCsvRequest:
      type: object
      properties:
        tagList:
          type: array
          items:
            type: string
        startTime:
          type: integer
          format: int64
        endTime:
          type: integer
          format: int64
        agg:
          type: string
        unitId:
          type: string
      description: >
        Source: `clarity:backend/src-tauri/src/processing_api/elog.rs`.

    SummaryDownloadRequest:
      type: object
      properties:
        unitsId:
          type: array
          items:
            type: string
        startTime:
          type: integer
          format: int64
        endTime:
          type: integer
          format: int64
      description: >
        Source: `clarity:backend/src-tauri/src/processing_api/elog.rs`.

    # ── EMS ──

    KpiParamsRequest:
      type: object
      properties:
        startTime:
          type: integer
          format: int64
        endTime:
          type: integer
          format: int64
        unitsId:
          type: string
        tagType:
          type: string
        agg:
          type: string
      description: >
        TODO-VERIFY: Source: `clarity:backend/src-tauri/src/processing_api/ems.rs` (KpiParamsRequest struct).

    # ── Monitor ──

    CreateRuleRequest:
      type: object
      required: [collection_id, tag_index, tag_name, name, rule_type, rule_config]
      properties:
        collection_id:
          type: string
          description: ID of the time-series collection this rule monitors
        tag_index:
          type: integer
          description: Index of the tag within the collection
        tag_name:
          type: string
        name:
          type: string
        description:
          type: string
          default: ""
        enabled:
          type: boolean
          default: true
        rule_type:
          type: string
          description: "Alarm rule type (e.g. threshold, deviation, missing)"
        rule_config:
          type: object
          additionalProperties: true
          description: Rule-type-specific configuration JSON
        window_duration_ms:
          type: integer
          format: int64
          default: 10000
          description: Evaluation window size in milliseconds
        missing_behavior:
          type: string
          default: "ignore"
        missing_threshold:
          type: integer
          nullable: true
      description: >
        Source: `clarity:backend/src-tauri/src/monitor/api.rs:46-63`.

    RuleDefinition:
      allOf:
        - $ref: "#/components/schemas/CreateRuleRequest"
        - type: object
          properties:
            id:
              type: string
            created_at:
              type: integer
              format: int64
            updated_at:
              type: integer
              format: int64
      description: >
        Source: `clarity:backend/src-tauri/src/monitor/types.rs` (RuleDefinition struct).

    AlarmSummary:
      type: object
      properties:
        collection_count:
          type: integer
        triggered_tags:
          type: integer
        timestamp_ms:
          type: integer
          format: int64
        tick_number:
          type: integer
        evaluation_duration_ms:
          type: integer
          format: int64
      description: >
        Source: `clarity:backend/src-tauri/src/monitor/api.rs:182-190`.

    # ── Backup ──

    BackupConfig:
      type: object
      properties:
        backup_path:
          type: string
          nullable: true
          description: Custom backup directory path; defaults to `{app_data}/backups`
        scheduler:
          $ref: "#/components/schemas/SchedulerConfig"
        sqlite_backup:
          $ref: "#/components/schemas/SqliteBackupConfig"
        timeseries_backup:
          $ref: "#/components/schemas/TimeseriesBackupConfig"
      description: >
        Source: `clarity:backend/src-tauri/src/backup/state.rs:7-16`.

    SchedulerConfig:
      type: object
      properties:
        check_interval_seconds:
          type: integer
          default: 3600
        timeseries_backup_interval_hours:
          type: integer
          default: 24

    SqliteBackupConfig:
      type: object
      properties:
        enabled:
          type: boolean
          default: true
        interval_minutes:
          type: integer
          default: 180
        num_backups_to_keep:
          type: integer
          default: 6

    TimeseriesBackupConfig:
      type: object
      properties:
        enabled:
          type: boolean
          default: true
        incremental_only:
          type: boolean
          default: true

    BackupState:
      type: object
      properties:
        last_backup_time:
          type: string
          format: date-time
          nullable: true
        last_sqlite_backup:
          $ref: "#/components/schemas/BackupResult"
        last_timeseries_backup:
          $ref: "#/components/schemas/BackupResult"
        last_timeseries_backup_time:
          type: string
          format: date-time
          nullable: true
        is_running:
          type: boolean
      description: >
        Source: `clarity:backend/src-tauri/src/backup/state.rs:119-126`.

    BackupResult:
      type: object
      properties:
        success:
          type: boolean
        timestamp:
          type: string
          format: date-time
        file_name:
          type: string
          nullable: true
        file_size:
          type: integer
          format: int64
          nullable: true
        checksum:
          type: string
          nullable: true
          description: SHA-256 hex digest
        error_message:
          type: string
          nullable: true
      description: >
        Source: `clarity:backend/src-tauri/src/backup/state.rs:128-136`.

    BackupFileInfo:
      type: object
      properties:
        name:
          type: string
        size:
          type: integer
          format: int64
        created:
          type: string
          format: date-time
          nullable: true
        checksum:
          type: string
          nullable: true
        is_incremental:
          type: boolean
          nullable: true
      description: >
        Source: `clarity:backend/src-tauri/src/backup/state.rs:146-153`.

    # ── PI Connector ──

    PiConnectionRequest:
      type: object
      properties:
        api_url:
          type: string
          description: PI Web API base URL
        username:
          type: string
        password:
          type: string
          description: Optional — if Basic Auth required
        verify_ssl:
          type: boolean
          default: true
      description: >
        Source: `clarity:backend/src-tauri/src/connectors/webpi/webpi_meta_connector.rs:878-881`.

    # ── Order Portal ──

    OrderCreate:
      type: object
      required: [order_id, customer_id, expires_at]
      properties:
        order_id:
          type: string
        customer_id:
          type: string
        expires_at:
          type: integer
          format: int64
          description: License expiry as Unix timestamp (seconds)
        tolerance:
          type: integer
          description: Clock drift tolerance in seconds
      description: >
        Source: `clarity:order_portal/app/routes/orders.py`.

    OrderResponse:
      type: object
      properties:
        order_id:
          type: string
        customer_id:
          type: string
        status:
          type: string
          enum: [pending, active, revoked, expired, transferred]
        expires_at:
          type: integer
          format: int64
        tolerance:
          type: integer

    StatusResponse:
      type: object
      properties:
        order_id:
          type: string
        status:
          type: string
          enum: [pending, active, revoked, expired, transferred]
        expires_at:
          type: integer
          format: int64
        days_remaining:
          type: integer
        tolerance:
          type: integer
      description: >
        Source: `clarity:order_portal/app/routes/status.py`.
