openapi: 3.1.0
info:
  title: LLM Signal Agent API
  version: v1
  description: >
    Agent-first endpoints for bootstrap, install health checks, and deterministic action plans.
    Use a site-scoped `siteId` + `apiKey` (request body) and optionally `X-LLMSIGNAL-KEY`.
servers:
  - url: https://www.llmsignal.app
paths:
  /api/agent/v1/bootstrap:
    get:
      summary: Get bootstrap payload
      operationId: getAgentBootstrap
      parameters:
        - $ref: "#/components/parameters/siteIdQuery"
        - $ref: "#/components/parameters/apiKeyQuery"
        - name: domain
          in: query
          required: false
          schema:
            type: string
      responses:
        "200":
          description: Bootstrap payload
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BootstrapResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "500":
          $ref: "#/components/responses/InternalError"
    post:
      summary: Get bootstrap payload
      operationId: postAgentBootstrap
      security:
        - llmSignalKeyHeader: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/BootstrapRequest"
      responses:
        "200":
          description: Bootstrap payload
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BootstrapResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "500":
          $ref: "#/components/responses/InternalError"

  /api/agent/v1/plan:
    get:
      summary: Get deterministic action plan from site telemetry
      operationId: getAgentPlan
      parameters:
        - $ref: "#/components/parameters/siteIdQuery"
        - $ref: "#/components/parameters/apiKeyQuery"
        - $ref: "#/components/parameters/persistQuery"
      responses:
        "200":
          description: Site action plan
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlanResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "500":
          $ref: "#/components/responses/InternalError"
    post:
      summary: Get deterministic action plan from site telemetry
      operationId: postAgentPlan
      security:
        - llmSignalKeyHeader: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PlanRequest"
      responses:
        "200":
          description: Site action plan
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlanResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "500":
          $ref: "#/components/responses/InternalError"

  /api/agent/v1/status:
    get:
      summary: Get install status + diagnostics
      operationId: getAgentStatus
      parameters:
        - $ref: "#/components/parameters/siteIdQuery"
        - $ref: "#/components/parameters/apiKeyQuery"
      responses:
        "200":
          description: Site install health
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/StatusResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "500":
          $ref: "#/components/responses/InternalError"
    post:
      summary: Get install status + diagnostics
      operationId: postAgentStatus
      security:
        - llmSignalKeyHeader: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/StatusRequest"
      responses:
        "200":
          description: Site install health
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/StatusResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "500":
          $ref: "#/components/responses/InternalError"

components:
  securitySchemes:
    llmSignalKeyHeader:
      type: apiKey
      in: header
      name: X-LLMSIGNAL-KEY

  parameters:
    siteIdQuery:
      name: siteId
      in: query
      required: true
      schema:
        type: string
    apiKeyQuery:
      name: apiKey
      in: query
      required: true
      schema:
        type: string
    persistQuery:
      name: persist
      in: query
      required: false
      schema:
        type: boolean

  responses:
    BadRequest:
      description: Invalid payload
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
    Unauthorized:
      description: Missing or invalid credentials
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
    InternalError:
      description: Server error
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"

  schemas:
    BootstrapRequest:
      type: object
      required: [siteId, apiKey]
      properties:
        siteId:
          type: string
        apiKey:
          type: string
        domain:
          type: string

    BootstrapResponse:
      type: object
      required: [ok, version, site, install, notes]
      properties:
        ok:
          type: boolean
          const: true
        version:
          type: string
          example: v1
        site:
          type: object
          required: [id, domain, createdAt]
          properties:
            id:
              type: string
            domain:
              type: string
            installedAt:
              type: string
              format: date-time
              nullable: true
            installLastSeenAt:
              type: string
              format: date-time
              nullable: true
            verifiedAt:
              type: string
              format: date-time
              nullable: true
            createdAt:
              type: string
              format: date-time
        install:
          type: object
          required: [snippet, scriptSrc, commands]
          properties:
            snippet:
              type: string
            scriptSrc:
              type: string
            commands:
              type: object
              additionalProperties:
                type: string
        notes:
          type: array
          items:
            type: string

    StatusRequest:
      type: object
      required: [siteId, apiKey]
      properties:
        siteId:
          type: string
        apiKey:
          type: string
    PlanRequest:
      type: object
      required: [siteId, apiKey]
      properties:
        siteId:
          type: string
        apiKey:
          type: string
        persist:
          type: boolean
          description: Optional. Persist run and actions to agent history tables.

    StatusResponse:
      type: object
      required: [ok, version, site, health, recentEvent, diagnostics]
      properties:
        ok:
          type: boolean
          const: true
        version:
          type: string
          example: v1
        site:
          type: object
          required: [id, domain, installed]
          properties:
            id:
              type: string
            domain:
              type: string
            installed:
              type: boolean
            installedAt:
              type: string
              format: date-time
              nullable: true
            installLastSeenAt:
              type: string
              format: date-time
              nullable: true
            verifiedAt:
              type: string
              format: date-time
              nullable: true
        health:
          type: object
          required: [status, eventsLast24h, eventsLast7d, lastEventAt]
          properties:
            status:
              type: string
              enum: [needs_install, healthy, idle]
            eventsLast24h:
              type: integer
            eventsLast7d:
              type: integer
            lastEventAt:
              type: string
              format: date-time
              nullable: true
        recentEvent:
          type: object
          nullable: true
          properties:
            occurredAt:
              type: string
              format: date-time
            aiSystem:
              type: string
            status:
              type: string
            source:
              type: string
            path:
              type: string
        diagnostics:
          type: array
          items:
            type: object
            required: [code, level, message]
            properties:
              code:
                type: string
              level:
                type: string
                enum: [info, warn]
              message:
                type: string
              fix:
                type: string
                nullable: true

    PlanResponse:
      type: object
      required: [ok, version, plan, runStored, runId]
      properties:
        ok:
          type: boolean
          const: true
        version:
          type: string
          example: v1
        runStored:
          type: boolean
        runId:
          type: string
          nullable: true
        plan:
          type: object
          required: [generatedAt, site, health, actions, summary, outcome]
          properties:
            generatedAt:
              type: string
              format: date-time
            site:
              type: object
              required: [id, domain, installed]
              properties:
                id:
                  type: string
                domain:
                  type: string
                installed:
                  type: boolean
                installedAt:
                  type: string
                  format: date-time
                  nullable: true
                installLastSeenAt:
                  type: string
                  format: date-time
                  nullable: true
                verifiedAt:
                  type: string
                  format: date-time
                  nullable: true
            health:
              type: object
              required:
                [
                  status,
                  eventsLast24h,
                  eventsLast7d,
                  unverifiedEventsLast24h,
                  unverifiedRateLast24h,
                  enabledPrompts,
                  lastPromptRunAt,
                  lastEventAt,
                ]
              properties:
                status:
                  type: string
                  enum: [needs_install, healthy, idle, attention]
                eventsLast24h:
                  type: integer
                eventsLast7d:
                  type: integer
                unverifiedEventsLast24h:
                  type: integer
                unverifiedRateLast24h:
                  type: number
                enabledPrompts:
                  type: integer
                lastPromptRunAt:
                  type: string
                  format: date-time
                  nullable: true
                lastEventAt:
                  type: string
                  format: date-time
                  nullable: true
            actions:
              type: array
              items:
                type: object
                required: [id, title, priority, automation, reason, steps]
                properties:
                  id:
                    type: string
                  title:
                    type: string
                  priority:
                    type: string
                    enum: [high, medium, low]
                  automation:
                    type: string
                    enum: [manual, assist, auto_safe]
                  reason:
                    type: string
                  steps:
                    type: array
                    items:
                      type: string
                  commands:
                    type: object
                    nullable: true
                    properties:
                      bash:
                        type: string
                        nullable: true
                      powershell:
                        type: string
                        nullable: true
            summary:
              type: string
            outcome:
              type: string
              enum: [healthy, attention_needed, needs_install]

    ErrorResponse:
      type: object
      required: [error]
      properties:
        error:
          type: string
