openapi: 3.1.0
info:
  title: Second-Pass Extraction API
  description: Gap-finding and second-pass extraction that compares initial extraction results against a naive free-form extraction to discover missing data points.
  version: 1.0.0

servers:
  - url: https://second-pass-extraction.your-subdomain.workers.dev
    description: Production

security:
  - UniversalApiKey: []

paths:
  /health:
    get:
      operationId: getHealth
      summary: Health check
      security: []
      responses:
        "200":
          description: Service is healthy
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HealthResponse"

  /v1/second-pass:
    post:
      operationId: createSecondPass
      summary: Create a second-pass extraction run
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SecondPassRequest"
      responses:
        "200":
          description: Second-pass run created and queued
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SecondPassResponse"
        "400":
          description: Invalid request body
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /v1/second-pass/{secondPassId}:
    get:
      operationId: getSecondPass
      summary: Get run status and summary
      parameters:
        - $ref: "#/components/parameters/SecondPassId"
      responses:
        "200":
          description: Run details
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SecondPassDetailResponse"
        "404":
          description: Run not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /v1/second-pass/{secondPassId}/gaps:
    get:
      operationId: listGaps
      summary: List discovered gaps for a run
      parameters:
        - $ref: "#/components/parameters/SecondPassId"
      responses:
        "200":
          description: List of gaps
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/GapListResponse"
        "404":
          description: Run not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /v1/second-pass/{secondPassId}/gaps/{gapId}/approve:
    post:
      operationId: approveGap
      summary: Approve a gap
      parameters:
        - $ref: "#/components/parameters/SecondPassId"
        - $ref: "#/components/parameters/GapId"
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/GapActionRequest"
      responses:
        "200":
          description: Gap approved
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/GapActionResponse"
        "404":
          description: Run or gap not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "409":
          description: Gap already reviewed
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /v1/second-pass/{secondPassId}/gaps/{gapId}/reject:
    post:
      operationId: rejectGap
      summary: Reject a gap
      parameters:
        - $ref: "#/components/parameters/SecondPassId"
        - $ref: "#/components/parameters/GapId"
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/GapActionRequest"
      responses:
        "200":
          description: Gap rejected
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/GapActionResponse"
        "404":
          description: Run or gap not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "409":
          description: Gap already reviewed
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /v1/config:
    get:
      operationId: getConfig
      summary: Get current service configuration
      responses:
        "200":
          description: Service configuration
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ConfigResponse"

components:
  securitySchemes:
    UniversalApiKey:
      type: apiKey
      in: header
      name: X-Universal-API-Key

  parameters:
    SecondPassId:
      name: secondPassId
      in: path
      required: true
      schema:
        type: string
      description: Unique identifier for the second-pass run

    GapId:
      name: gapId
      in: path
      required: true
      schema:
        type: string
      description: Unique identifier for the gap

  schemas:
    SecondPassRequest:
      type: object
      required:
        - documentId
        - documentContent
      properties:
        secondPassId:
          type: string
          description: Custom ID for the run (auto-generated if omitted)
        documentId:
          type: string
          description: Identifier for the source document
        documentContent:
          type: string
          description: Full text content of the document
        firstPassData:
          description: Results from the initial structured extraction
          type: object
          additionalProperties: true
        artifactId:
          type: string
          description: Fibery artifact ID for attribute lookup
        artifactName:
          type: string
          description: Fibery artifact name for attribute lookup
        options:
          type: object
          properties:
            extractionModel:
              type: string
              description: Override the default extraction model
            judgeModel:
              type: string
              description: Override the default judge model
          additionalProperties: false

    SecondPassResponse:
      type: object
      required:
        - ok
        - secondPassId
        - documentId
        - status
      properties:
        ok:
          type: boolean
        secondPassId:
          type: string
        documentId:
          type: string
        status:
          type: string
          enum:
            - queued

    Gap:
      type: object
      required:
        - gapId
        - suggestedAttributeName
        - value
        - context
        - locationInDocument
        - reason
        - confidence
        - status
      properties:
        gapId:
          type: string
        suggestedAttributeName:
          type: string
        value:
          type: string
        context:
          type: string
        locationInDocument:
          type: string
        reason:
          type: string
        confidence:
          type: number
          minimum: 0
          maximum: 1
        existingRelatedAttributes:
          type: array
          items:
            type: string
        status:
          type: string
          enum:
            - pending
            - approved
            - rejected
        reviewedBy:
          type: string
        reviewedAt:
          type: string
          format: date-time
        reviewNotes:
          type: string

    GapListResponse:
      type: object
      required:
        - ok
        - gaps
      properties:
        ok:
          type: boolean
        gaps:
          type: array
          items:
            $ref: "#/components/schemas/Gap"

    GapActionRequest:
      type: object
      properties:
        reviewedBy:
          type: string
        reviewNotes:
          type: string

    GapActionResponse:
      type: object
      required:
        - ok
        - gapId
        - status
      properties:
        ok:
          type: boolean
        gapId:
          type: string
        status:
          type: string
          enum:
            - approved
            - rejected
        reviewedBy:
          type: string
        reviewedAt:
          type: string
          format: date-time

    SecondPassDetailResponse:
      type: object
      required:
        - ok
        - secondPassId
        - documentId
        - status
      properties:
        ok:
          type: boolean
        secondPassId:
          type: string
        documentId:
          type: string
        status:
          type: string
          enum:
            - queued
            - running
            - completed
            - failed
        createdAt:
          type: string
          format: date-time
        completedAt:
          type: string
          format: date-time
        gapCount:
          type: integer
        extractionModel:
          type: string
        judgeModel:
          type: string

    ConfigResponse:
      type: object
      required:
        - ok
        - defaultExtractionModel
        - defaultJudgeModel
      properties:
        ok:
          type: boolean
        defaultExtractionModel:
          type: string
        defaultJudgeModel:
          type: string

    HealthResponse:
      type: object
      required:
        - ok
        - service
      properties:
        ok:
          type: boolean
        service:
          type: string

    ErrorResponse:
      type: object
      required:
        - ok
        - error
      properties:
        ok:
          type: boolean
          enum:
            - false
        error:
          type: string
