openapi: 3.1.0
info:
  # Do not change the title, if the title changes, the import paths will be broken
  title: Api
  version: 1.0.0
  description: |
    Status State API - agent-facing status monitoring for 20 developer
    infrastructure vendors. Most routes are free. Full per-vendor snapshots
    and change history are paid via the x402 protocol (USDC on Base,
    $0.01/call, no API key or signup required). See /.well-known/agent.json
    and /.well-known/x402 for machine-readable payment discovery.
servers:
  - url: /api
    description: Base API path
tags:
  - name: health
    description: Health and payment-subsystem status
  - name: monitors
    description: Vendor status monitors (free summaries + paid full detail)
paths:
  /healthz:
    get:
      operationId: healthCheck
      tags: [health]
      summary: Health check
      description: Returns server health, DB connectivity, and payment subsystem status
      responses:
        "200":
          description: Healthy
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HealthStatus"
  /monitors:
    get:
      operationId: listMonitors
      tags: [monitors]
      summary: List all monitors (free)
      description: |
        Free endpoint. Returns the 20 tracked vendors with their current
        overall status only - no component breakdown or incident detail.
        Use GET /monitors/{slug} (paid) for the full snapshot.
      responses:
        "200":
          description: List of monitor summaries
          content:
            application/json:
              schema:
                type: object
                required: [monitors]
                properties:
                  monitors:
                    type: array
                    items:
                      $ref: "#/components/schemas/MonitorSummary"
  /site/status/{slug}:
    get:
      operationId: getSiteStatus
      tags: [monitors]
      summary: Minimal status teaser (free)
      description: |
        Free single-vendor teaser: slug, name, and overall status only.
        Returns 404 for an unknown slug (no payment is ever involved on
        this route).
      parameters:
        - $ref: "#/components/parameters/MonitorSlug"
      responses:
        "200":
          description: Minimal status
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MonitorSummary"
        "404":
          description: Unknown monitor slug
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
  /monitors/{slug}:
    get:
      operationId: getMonitorDetail
      tags: [monitors]
      summary: Full monitor snapshot (paid)
      description: |
        Paid via x402 (see /.well-known/x402). Returns the full normalized
        snapshot: overall status, per-component status, and the active
        incident if any. The slug is validated against known monitors
        before the payment challenge is issued, so an unknown slug returns
        404 without ever charging.
      parameters:
        - $ref: "#/components/parameters/MonitorSlug"
      responses:
        "200":
          description: Full monitor snapshot
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MonitorDetail"
        "402":
          description: Payment required (x402 challenge)
        "404":
          description: Unknown monitor slug
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
  /monitors/{slug}/changes:
    get:
      operationId: getMonitorChanges
      tags: [monitors]
      summary: Change history (paid)
      description: |
        Paid via x402. Returns the most recent detected real status changes
        for a monitor (up to 20, newest first). Extraction-quality
        improvements never appear here - only changes the fingerprinting
        logic judged to be real vendor-side changes.
      parameters:
        - $ref: "#/components/parameters/MonitorSlug"
      responses:
        "200":
          description: Change history
          content:
            application/json:
              schema:
                type: object
                required: [slug, changes]
                properties:
                  slug:
                    type: string
                  changes:
                    type: array
                    items:
                      $ref: "#/components/schemas/ChangeRecord"
        "402":
          description: Payment required (x402 challenge)
        "404":
          description: Unknown monitor slug
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
components:
  parameters:
    MonitorSlug:
      name: slug
      in: path
      required: true
      description: |
        Vendor slug from the free GET /api/monitors catalog. An unknown slug
        is rejected with 404 before any payment challenge is issued, so
        discovery probes must substitute a real slug (see example) to reach
        the 402 challenge on paid routes.
      schema:
        type: string
        pattern: "^[a-z0-9-]{1,64}$"
        example: openai
      example: openai
  schemas:
    HealthStatus:
      type: object
      required: [status, database, monitorCount, erroringCount, payments]
      properties:
        status:
          type: string
          enum: [ok, degraded]
        database:
          type: string
          enum: [ok, error]
        monitorCount:
          type: number
          nullable: true
          description: Enabled monitors in the catalog, or null if the count query failed
        erroringCount:
          type: number
          nullable: true
          description: Monitors whose last check recorded an error, or null if the count query failed
        payments:
          type: object
          required: [configured, facilitatorReachable]
          properties:
            configured:
              type: boolean
            facilitatorReachable:
              type: boolean
              nullable: true
            network:
              type: string
    StatusLevel:
      type: string
      enum:
        - operational
        - degraded
        - partial_outage
        - major_outage
        - maintenance
        - unknown
    MonitorSummary:
      type: object
      required: [slug, name, homepageUrl, overallStatus, lastCheckedAt]
      properties:
        slug:
          type: string
        name:
          type: string
        homepageUrl:
          type: string
        overallStatus:
          $ref: "#/components/schemas/StatusLevel"
        lastCheckedAt:
          type: string
          format: date-time
          nullable: true
    StatusComponent:
      type: object
      required: [name, status]
      properties:
        name:
          type: string
        status:
          $ref: "#/components/schemas/StatusLevel"
    Incident:
      type: object
      required: [name, status]
      properties:
        name:
          type: string
        status:
          type: string
        impact:
          type: string
          nullable: true
        startedAt:
          type: string
          format: date-time
          nullable: true
        updatedAt:
          type: string
          format: date-time
          nullable: true
        url:
          type: string
          nullable: true
    MonitorDetail:
      type: object
      required: [slug, name, homepageUrl, overallStatus, components, activeIncident, lastCheckedAt]
      properties:
        slug:
          type: string
        name:
          type: string
        homepageUrl:
          type: string
        overallStatus:
          $ref: "#/components/schemas/StatusLevel"
        components:
          type: array
          items:
            $ref: "#/components/schemas/StatusComponent"
        activeIncident:
          $ref: "#/components/schemas/Incident"
          nullable: true
        lastCheckedAt:
          type: string
          format: date-time
          nullable: true
        checkIntervalMinutes:
          type: number
    ChangeRecord:
      type: object
      required: [id, changeType, previousStatus, newStatus, createdAt]
      properties:
        id:
          type: number
        changeType:
          type: string
          enum:
            - status_change
            - component_change
            - incident_opened
            - incident_updated
            - incident_resolved
        previousStatus:
          type: string
          nullable: true
        newStatus:
          type: string
          nullable: true
        detail:
          nullable: true
        createdAt:
          type: string
          format: date-time
    ErrorResponse:
      type: object
      required: [error]
      properties:
        error:
          type: string
