openapi: 3.1.0
info:
  title: OPS API
  version: 0.1.0-draft
  description: |
    Operations Management (OPS).

    Contract notes:
    - API Gateway base path: `/ops`
    - API Gateway exposes only `GET /ping`, `GET /stat`, `GET|POST /maintenance/list`, `GET|POST /maintenance/get`.
    - `ping` and `stat` require no authentication.
    - `maintenance/list` and `maintenance/get` require a valid session (`x-session-guid` header or `session_guid` body field).
    - All destructive operations (schedule, start, end, cancel, update maintenance; vacuum all/org/cancel) are Direct Lambda only and require a secret code. They are not covered by this OpenAPI file.
    - EventBridge sweep rules (`maintenance_sweep`, `vacuum_sweep`) and async workers (`vacuum_all_worker`, `vacuum_org_worker`) are system-internal.
    - Maintenance mode blocks all other services (503) but OPS remains available.
    - Maintenance check in other services fails open (infra issues do not cause global outage).
  license:
    name: Proprietary
    url: https://g3nretailstack.com/license
servers:
  - url: https://api.g3nretailstack.com/ops
security: []
components:
  securitySchemes:
    sessionAuth:
      type: apiKey
      in: header
      name: x-session-guid
  schemas:
    BuildMeta:
      type: object
      properties:
        build_major: { type: string }
        build_minor: { type: string }
        build_id: { type: string }
      required: [build_major, build_minor, build_id]
    Stats:
      type: object
      properties:
        call: { type: string }
        service: { type: string, enum: [ops] }
        request_id: { type: string }
        timestamp_utc: { type: string, format: date-time }
        build: { $ref: '#/components/schemas/BuildMeta' }
        actor: { type: string }
        session_fingerprint:
          type: string
          description: Non-reversible SHA-256 fingerprint of a caller-provided `session_guid`.
        orgcode: { type: string }
        cccode: { type: string }
      required: [call, service, timestamp_utc, build]
    ErrorMessage:
      type: object
      properties:
        en_US: { type: string }
      required: [en_US]
    ErrorTag:
      type: object
      properties:
        tag: { type: string }
        message: { $ref: '#/components/schemas/ErrorMessage' }
      required: [tag, message]
    Error:
      type: object
      properties:
        error_code: { type: string }
        http_status: { type: integer }
        retryable: { type: boolean }
        request_id: { type: string }
        trace_id: { type: string }
        major: { $ref: '#/components/schemas/ErrorTag' }
        minor: { $ref: '#/components/schemas/ErrorTag' }
        details: {}
      required: [major]
    Envelope:
      type: object
      properties:
        success: { type: boolean }
        data: {}
        stats: { $ref: '#/components/schemas/Stats' }
        error: { $ref: '#/components/schemas/Error' }
        revision: { type: string }
      required: [success]
    ErrorResponse:
      allOf:
        - $ref: '#/components/schemas/Envelope'
        - type: object
          properties:
            success: { type: boolean, const: false }
          required: [success, error, stats]
    OkResponse:
      allOf:
        - $ref: '#/components/schemas/Envelope'
        - type: object
          properties:
            success: { type: boolean, const: true }
            data: {}
          required: [success, data, stats]
    MaintenanceDetails:
      type: object
      properties:
        maintenance_id: { type: string }
        description: { type: string }
        started_at: { type: string, format: date-time }
        estimated_end_utc: { type: string, format: date-time }
      required: [maintenance_id, description, started_at]
    PingData:
      type: object
      properties:
        ok: { type: boolean, const: true }
        maintenance_active: { type: boolean }
        maintenance: { $ref: '#/components/schemas/MaintenanceDetails' }
      required: [ok, maintenance_active]
    PingSuccess:
      allOf:
        - $ref: '#/components/schemas/OkResponse'
        - type: object
          properties:
            data: { $ref: '#/components/schemas/PingData' }
          required: [data]
    StatData:
      type: object
      properties:
        ok: { type: boolean, const: true }
      required: [ok]
    StatSuccess:
      allOf:
        - $ref: '#/components/schemas/OkResponse'
        - type: object
          properties:
            data: { $ref: '#/components/schemas/StatData' }
          required: [data]
    MaintenanceUpdate:
      type: object
      properties:
        timestamp_utc: { type: string, format: date-time }
        text: { type: string }
        remaining_seconds: { type: integer }
      required: [timestamp_utc, text]
    MaintenanceStatusHistory:
      type: object
      properties:
        status: { type: string, enum: [scheduled, active, ended, cancelled] }
        timestamp_utc: { type: string, format: date-time }
      required: [status, timestamp_utc]
    MaintenanceListItem:
      type: object
      properties:
        maintenance_id: { type: string }
        status: { type: string, enum: [scheduled, active, ended, cancelled] }
        description: { type: string }
        estimated_duration_seconds: { type: integer }
        scheduled_start_utc: { type: string, format: date-time }
        actual_start_utc: { type: string, format: date-time }
        actual_end_utc: { type: string, format: date-time }
        created_at: { type: string, format: date-time }
      required: [maintenance_id, status, description, estimated_duration_seconds, scheduled_start_utc, created_at]
    MaintenanceListData:
      type: object
      properties:
        items:
          type: array
          items: { $ref: '#/components/schemas/MaintenanceListItem' }
        next_token: { type: string }
      required: [items]
    MaintenanceListSuccess:
      allOf:
        - $ref: '#/components/schemas/OkResponse'
        - type: object
          properties:
            data: { $ref: '#/components/schemas/MaintenanceListData' }
          required: [data]
    MaintenanceGetData:
      type: object
      properties:
        maintenance_id: { type: string }
        status: { type: string, enum: [scheduled, active, ended, cancelled] }
        description: { type: string }
        estimated_duration_seconds: { type: integer }
        scheduled_start_utc: { type: string, format: date-time }
        actual_start_utc: { type: string, format: date-time }
        actual_end_utc: { type: string, format: date-time }
        end_message: { type: string }
        updates:
          type: array
          items: { $ref: '#/components/schemas/MaintenanceUpdate' }
        status_history:
          type: array
          items: { $ref: '#/components/schemas/MaintenanceStatusHistory' }
        revision: { type: integer }
        created_at: { type: string, format: date-time }
        updated_at: { type: string, format: date-time }
      required: [maintenance_id, status, description, estimated_duration_seconds, scheduled_start_utc, revision, created_at, updated_at]
    MaintenanceGetSuccess:
      allOf:
        - $ref: '#/components/schemas/OkResponse'
        - type: object
          properties:
            data: { $ref: '#/components/schemas/MaintenanceGetData' }
          required: [data, revision]
    MaintenanceGetRequest:
      type: object
      properties:
        maintenance_id: { type: string }
        session_guid: { type: string }
      required: [maintenance_id]
    MaintenanceListRequest:
      type: object
      properties:
        limit:
          type: integer
          minimum: 1
          maximum: 200
          default: 50
        next_token: { type: string }
        session_guid: { type: string }
paths:
  /ping:
    get:
      operationId: opsPing
      summary: Health check with maintenance status
      x-route-class: Tier A
      x-qps-target: 1000
      x-concurrency-target: 1000
      x-latency-p95-ms: 100
      description: |
        Returns `{ ok: true }` plus current maintenance status.
        No authentication required.
      responses:
        '200':
          description: Ping response
          content:
            application/json:
              schema: { $ref: '#/components/schemas/PingSuccess' }
        '500':
          description: Internal error
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ErrorResponse' }
  /stat:
    get:
      operationId: opsStat
      summary: Service status check
      x-route-class: Tier A
      x-qps-target: 1000
      x-concurrency-target: 1000
      x-latency-p95-ms: 100
      description: |
        Returns `{ ok: true }`. No authentication required.
      responses:
        '200':
          description: Stat response
          content:
            application/json:
              schema: { $ref: '#/components/schemas/StatSuccess' }
        '500':
          description: Internal error
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ErrorResponse' }
  /maintenance/list:
    get:
      operationId: opsMaintenanceList
      summary: List maintenance records
      security:
        - sessionAuth: []
      x-route-class: Tier B
      x-qps-target: 100
      x-concurrency-target: 200
      x-latency-p95-ms: 300
      description: |
        Lists maintenance records in reverse chronological order (GSI1).
        Supports pagination via `next_token`.
      parameters:
        - name: limit
          in: query
          schema: { type: integer, minimum: 1, maximum: 200, default: 50 }
        - name: next_token
          in: query
          schema: { type: string }
      responses:
        '200':
          description: Maintenance list
          content:
            application/json:
              schema: { $ref: '#/components/schemas/MaintenanceListSuccess' }
        '401':
          description: Invalid or missing session
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ErrorResponse' }
        '500':
          description: Internal error
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ErrorResponse' }
    post:
      operationId: opsMaintenanceListPost
      summary: List maintenance records (POST)
      security:
        - sessionAuth: []
      x-route-class: Tier B
      x-qps-target: 100
      x-concurrency-target: 200
      x-latency-p95-ms: 300
      description: |
        POST variant of maintenance/list. Accepts body parameters.
      requestBody:
        content:
          application/json:
            schema: { $ref: '#/components/schemas/MaintenanceListRequest' }
      responses:
        '200':
          description: Maintenance list
          content:
            application/json:
              schema: { $ref: '#/components/schemas/MaintenanceListSuccess' }
        '401':
          description: Invalid or missing session
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ErrorResponse' }
        '500':
          description: Internal error
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ErrorResponse' }
  /maintenance/get:
    get:
      operationId: opsMaintenanceGet
      summary: Get a single maintenance record
      security:
        - sessionAuth: []
      x-route-class: Tier B
      x-qps-target: 100
      x-concurrency-target: 200
      x-latency-p95-ms: 300
      description: |
        Returns a single maintenance record by `maintenance_id`.
        On success, `revision` is set on the envelope.
      parameters:
        - name: maintenance_id
          in: query
          required: true
          schema: { type: string }
      responses:
        '200':
          description: Maintenance record
          content:
            application/json:
              schema: { $ref: '#/components/schemas/MaintenanceGetSuccess' }
        '400':
          description: Missing maintenance_id
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ErrorResponse' }
        '401':
          description: Invalid or missing session
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ErrorResponse' }
        '404':
          description: Maintenance record not found
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ErrorResponse' }
        '500':
          description: Internal error
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ErrorResponse' }
    post:
      operationId: opsMaintenanceGetPost
      summary: Get a single maintenance record (POST)
      security:
        - sessionAuth: []
      x-route-class: Tier B
      x-qps-target: 100
      x-concurrency-target: 200
      x-latency-p95-ms: 300
      description: |
        POST variant of maintenance/get. Accepts `maintenance_id` in the body.
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/MaintenanceGetRequest' }
      responses:
        '200':
          description: Maintenance record
          content:
            application/json:
              schema: { $ref: '#/components/schemas/MaintenanceGetSuccess' }
        '400':
          description: Missing maintenance_id
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ErrorResponse' }
        '401':
          description: Invalid or missing session
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ErrorResponse' }
        '404':
          description: Maintenance record not found
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ErrorResponse' }
        '500':
          description: Internal error
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ErrorResponse' }
