openapi: 3.1.0
info:
  title: Document Batch Workflow API
  description: Orchestrates the full document processing pipeline — parse, classify, extract, second-pass review, normalize, and rename — with durable workflow steps.
  version: 1.0.0
  contact:
    name: AdviceOS

servers:
  - url: https://document-batch-workflow.your-subdomain.workers.dev
    description: Production

security: []

tags:
  - name: Health
  - name: Runs
  - name: Review

paths:
  /health:
    get:
      operationId: getHealth
      tags: [Health]
      summary: Health check
      responses:
        "200":
          description: Service is healthy
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: ok
                  timestamp:
                    type: string
                    format: date-time

  /api/runs:
    post:
      operationId: createRun
      tags: [Runs]
      summary: Create a new batch run
      security:
        - ApiKeyAuth: []
        - AccessAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateRunRequest"
      responses:
        "201":
          description: Batch run created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/RunResponse"
        "400":
          description: Invalid request body
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "500":
          description: Internal error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

    get:
      operationId: listRuns
      tags: [Runs]
      summary: List runs
      security:
        - ApiKeyAuth: []
        - AccessAuth: []
      parameters:
        - name: status
          in: query
          schema:
            type: string
            enum: [running, completed, completed_with_failures, failed]
        - name: limit
          in: query
          schema:
            type: integer
            default: 50
            minimum: 1
            maximum: 200
        - name: offset
          in: query
          schema:
            type: integer
            default: 0
            minimum: 0
      responses:
        "200":
          description: List of runs
          content:
            application/json:
              schema:
                type: object
                properties:
                  runs:
                    type: array
                    items:
                      $ref: "#/components/schemas/RunResponse"
                  total:
                    type: integer
                  limit:
                    type: integer
                  offset:
                    type: integer
        "401":
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /api/runs/{runId}:
    get:
      operationId: getRun
      tags: [Runs]
      summary: Get run details
      security:
        - ApiKeyAuth: []
        - AccessAuth: []
      parameters:
        - name: runId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Run details
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/RunResponse"
        "401":
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "404":
          description: Run not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /api/runs/{runId}/review:
    post:
      operationId: submitReview
      tags: [Review]
      summary: Submit classification review resolution
      description: Resolves an ambiguous classification by selecting the correct artifact for a document.
      security:
        - ApiKeyAuth: []
        - AccessAuth: []
      parameters:
        - name: runId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ReviewRequest"
      responses:
        "200":
          description: Review accepted
          content:
            application/json:
              schema:
                type: object
                properties:
                  accepted:
                    type: boolean
                  documentId:
                    type: string
                    format: uuid
        "400":
          description: Invalid review payload
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "404":
          description: Run not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "409":
          description: Review already submitted or document not awaiting review
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /api/runs/{runId}/normalization-review:
    post:
      operationId: submitNormalizationReview
      tags: [Review]
      summary: Submit normalization conflict resolution
      description: Resolves conflicts found during the normalization stage.
      security:
        - ApiKeyAuth: []
        - AccessAuth: []
      parameters:
        - name: runId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/NormalizationReviewRequest"
      responses:
        "200":
          description: Normalization review accepted
          content:
            application/json:
              schema:
                type: object
                properties:
                  accepted:
                    type: boolean
                  runId:
                    type: string
                    format: uuid
        "400":
          description: Invalid resolution payload
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "404":
          description: Run not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "409":
          description: Review already submitted or run not awaiting normalization review
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

components:
  securitySchemes:
    AdminAuth:
      type: apiKey
      in: header
      name: X-Admin-API-Key
      description: Admin endpoint access key

    WorkflowAuth:
      type: apiKey
      in: header
      name: X-Workflow-API-Key
      description: Inter-workflow authentication key

    ApiKeyAuth:
      type: apiKey
      in: header
      name: X-API-Key
      description: Universal API key for external callers

    AccessAuth:
      type: apiKey
      in: header
      name: X-Access-Key
      description: Orchestrator or browser-origin access key

  schemas:
    RunItemInput:
      type: object
      required:
        - fileName
      properties:
        fileName:
          type: string
          description: Name of the document file
        r2Key:
          type: string
          description: Existing R2 key if file is already stored
        contentType:
          type: string
          description: MIME type of the document
          example: application/pdf

    CreateRunRequest:
      type: object
      required:
        - items
      properties:
        items:
          type: array
          items:
            $ref: "#/components/schemas/RunItemInput"
          minItems: 1
          description: Documents to process in this batch
        skipSecondPass:
          type: boolean
          default: false
          description: Skip the second-pass extraction step for all documents
        metadata:
          type: object
          additionalProperties: true
          description: Arbitrary metadata attached to the run

    DocumentStatus:
      type: string
      enum:
        - pending
        - running
        - awaiting_review
        - awaiting_gap_review
        - extracted
        - failed

    DocumentRecord:
      type: object
      properties:
        id:
          type: string
          format: uuid
        fileName:
          type: string
        r2Key:
          type: string
        contentType:
          type: string
        status:
          $ref: "#/components/schemas/DocumentStatus"
        classification:
          type: object
          properties:
            artifactId:
              type: string
            artifactName:
              type: string
            confidence:
              type: number
            ambiguous:
              type: boolean
          nullable: true
        extraction:
          type: object
          additionalProperties: true
          nullable: true
        secondPassGaps:
          type: array
          items:
            type: object
            additionalProperties: true
          nullable: true
        error:
          type: string
          nullable: true
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time

    RunStatus:
      type: string
      enum:
        - running
        - completed
        - completed_with_failures
        - failed
        - awaiting_normalization_review

    RunResponse:
      type: object
      properties:
        id:
          type: string
          format: uuid
        status:
          $ref: "#/components/schemas/RunStatus"
        documents:
          type: array
          items:
            $ref: "#/components/schemas/DocumentRecord"
        normalizedData:
          type: object
          additionalProperties: true
          nullable: true
        renamedFileKeys:
          type: array
          items:
            type: object
            properties:
              documentId:
                type: string
                format: uuid
              originalName:
                type: string
              renamedKey:
                type: string
          nullable: true
        finalArtifactKey:
          type: string
          nullable: true
        metadata:
          type: object
          additionalProperties: true
          nullable: true
        skipSecondPass:
          type: boolean
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time

    ReviewRequest:
      type: object
      required:
        - documentId
        - artifactId
      properties:
        documentId:
          type: string
          format: uuid
          description: The document awaiting review
        artifactId:
          type: string
          description: The selected artifact ID to assign

    NormalizationResolution:
      type: object
      required:
        - field
        - value
      properties:
        field:
          type: string
          description: The conflicting field name
        value:
          description: The resolved value
          oneOf:
            - type: string
            - type: number
            - type: boolean

    NormalizationReviewRequest:
      type: object
      required:
        - resolutions
      properties:
        resolutions:
          type: array
          items:
            $ref: "#/components/schemas/NormalizationResolution"
          minItems: 1

    ErrorResponse:
      type: object
      required:
        - error
      properties:
        error:
          type: string
        message:
          type: string
        code:
          type: string
