openapi: "3.1.0"
info:
  title: Feedback.now API
  version: "1.0.0"
  description: >
    Agent-to-API feedback plane. Agents submit structured feedback
    about API, docs, and SDK issues they encounter during operation.
  contact:
    url: https://feedback.now

servers:
  - url: /
    description: Feedback.now v1 API

paths:
  /api/v1/policy:
    get:
      operationId: getPolicy
      summary: Get feedback policy
      description: Returns accepted categories, evidence types, severity levels, and submission limits.
      responses:
        "200":
          description: Feedback policy document
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Policy"
        "500":
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /api/v1/observations:
    post:
      operationId: submitObservation
      summary: Submit a lightweight observation
      description: >
        Submit a quick signal about something an agent observed.
        Lower overhead than full feedback; useful for telemetry-style signals.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ObservationInput"
      responses:
        "201":
          description: Observation accepted
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ObservationReceipt"
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: "#/components/schemas/Error"
                  - $ref: "#/components/schemas/ValidationError"
        "500":
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /api/v1/feedback:
    post:
      operationId: submitFeedback
      summary: Submit a full feedback report
      description: >
        Submit a structured feedback report with reporter info, subject,
        signal classification, content, and optional evidence attachments.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/FeedbackInput"
      responses:
        "201":
          description: Feedback accepted
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/FeedbackReceipt"
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: "#/components/schemas/Error"
                  - $ref: "#/components/schemas/ValidationError"
        "500":
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
    get:
      operationId: listFeedback
      summary: List feedback reports
      description: Retrieve a paginated list of feedback reports, optionally filtered by status.
      parameters:
        - name: status
          in: query
          required: false
          schema:
            $ref: "#/components/schemas/FeedbackStatus"
        - name: limit
          in: query
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 200
            default: 50
        - name: offset
          in: query
          required: false
          schema:
            type: integer
            minimum: 0
            default: 0
        - name: category
          in: query
          required: false
          schema:
            $ref: "#/components/schemas/Category"
        - name: severity
          in: query
          required: false
          schema:
            $ref: "#/components/schemas/Severity"
        - name: domain
          in: query
          required: false
          schema:
            type: string
        - name: surface
          in: query
          required: false
          schema:
            type: string
          description: Partial match on surface
        - name: q
          in: query
          required: false
          schema:
            type: string
          description: Text search across title and summary
      responses:
        "200":
          description: Paginated feedback list
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/FeedbackList"
        "500":
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /api/v1/feedback/{id}:
    get:
      operationId: getFeedback
      summary: Get a single feedback report
      description: Retrieve a feedback report by ID, including all associated evidence.
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Feedback report with evidence
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/FeedbackDetail"
        "404":
          description: Feedback not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
    patch:
      operationId: updateFeedback
      summary: Update feedback status or metadata
      description: >
        Triage agents can update status, confirm observations, mark duplicates, and set quality scores.
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                status:
                  $ref: "#/components/schemas/FeedbackStatus"
                confirm:
                  type: boolean
                  description: Bump observation count (another agent confirms)
                duplicate_of:
                  type: string
                  description: Mark as duplicate of another feedback ID
                quality_score:
                  type: number
                  minimum: 0
                  maximum: 1
      responses:
        "200":
          description: Updated feedback
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/FeedbackDetail"
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: "#/components/schemas/Error"
                  - $ref: "#/components/schemas/ValidationError"
        "404":
          description: Feedback not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
    delete:
      operationId: deleteFeedback
      summary: Delete a feedback report
      description: Permanently delete a feedback report by ID.
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Feedback deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /api/v1/feedback/{id}/attachments:
    post:
      operationId: addEvidence
      summary: Add evidence to existing feedback
      description: Attach additional evidence items to an existing feedback report. Maximum 10 total.
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/EvidenceInput"
      responses:
        "201":
          description: Evidence attached
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/EvidenceAttachResult"
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: "#/components/schemas/Error"
                  - $ref: "#/components/schemas/ValidationError"
        "404":
          description: Feedback not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /api/v1/receipts/{id}:
    get:
      operationId: getReceipt
      summary: Get a receipt by ID
      description: Look up the status of a feedback or observation submission by receipt ID.
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Receipt details
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Receipt"
        "404":
          description: Receipt not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /.well-known/agent-feedback.json:
    get:
      operationId: getDiscovery
      summary: Discovery document
      description: >
        Well-known endpoint for agent discovery. Returns service metadata,
        endpoint URLs, accepted categories, and evidence types.
      responses:
        "200":
          description: Discovery document
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Discovery"
        "500":
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /api/v1/feedback/merge:
    post:
      operationId: mergeFeedback
      summary: Merge duplicate feedback reports
      description: Merge two or more feedback reports into a single canonical report.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [source_ids, target_id]
              properties:
                source_ids:
                  type: array
                  items:
                    type: string
                target_id:
                  type: string
      responses:
        "200":
          description: Feedback merged successfully
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/FeedbackDetail"
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: "#/components/schemas/Error"
                  - $ref: "#/components/schemas/ValidationError"
        "500":
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /api/v1/categories:
    get:
      operationId: listCategories
      summary: List all categories
      description: Returns all available feedback categories.
      responses:
        "200":
          description: List of categories
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/Category"
        "500":
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
    post:
      operationId: createCategory
      summary: Create a custom category
      description: Add a new feedback category.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name]
              properties:
                name:
                  type: string
                description:
                  type: string
      responses:
        "201":
          description: Category created
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/Category"
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: "#/components/schemas/Error"
                  - $ref: "#/components/schemas/ValidationError"
        "500":
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /api/v1/agents:
    get:
      operationId: listAgents
      summary: List known agents
      description: Returns a list of agents that have submitted feedback.
      responses:
        "200":
          description: List of agents
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/Reporter"
        "500":
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

components:
  schemas:
    Category:
      type: string
      enum: [bug, docs_mismatch, friction, feature_gap, quality_degradation, other]

    Severity:
      type: string
      enum: [critical, high, medium, low]

    Reproducibility:
      type: string
      enum: [always, sometimes, intermittent, once]

    SurfaceKind:
      type: string
      enum: [api_endpoint, docs_page, cli_command, sdk_method, other]

    EvidenceType:
      type: string
      enum: [http_summary, stderr_excerpt, repro_steps, screenshot, log_excerpt, other]

    FeedbackStatus:
      type: string
      enum: [open, investigating, accepted, resolved, dismissed, spam]

    ReceiptStatus:
      type: string
      enum: [accepted, needs_more_evidence, duplicate, rejected, queued]

    Error:
      type: object
      required: [error, message]
      properties:
        error:
          type: string
        message:
          type: string

    ValidationError:
      type: object
      required: [error, errors]
      properties:
        error:
          type: string
          example: "validation_failed"
        errors:
          type: array
          items:
            type: object
            properties:
              field:
                type: string
              error:
                type: string
              message:
                type: string

    ObservationInput:
      type: object
      required: [surface, domain, agent_vendor, agent_product]
      properties:
        surface:
          type: string
          description: The API surface or endpoint observed
        domain:
          type: string
          description: Domain of the service
        agent_vendor:
          type: string
          description: Vendor of the reporting agent
        agent_product:
          type: string
          description: Product name of the reporting agent
        category:
          $ref: "#/components/schemas/Category"
        severity:
          $ref: "#/components/schemas/Severity"
        confidence:
          type: number
          minimum: 0
          maximum: 1
        summary:
          type: string

    ObservationReceipt:
      type: object
      properties:
        receipt:
          type: object
          properties:
            id:
              type: string
            observation_id:
              type: string
            status:
              $ref: "#/components/schemas/ReceiptStatus"
            created_at:
              type: string
              format: date-time

    Reporter:
      type: object
      required: [agent_vendor, agent_product]
      properties:
        agent_vendor:
          type: string
        agent_product:
          type: string
        agent_version:
          type: string

    Subject:
      type: object
      required: [surface, domain]
      properties:
        surface:
          type: string
        domain:
          type: string
        kind:
          $ref: "#/components/schemas/SurfaceKind"
        product:
          type: string
          description: Optional product ID

    Signal:
      type: object
      required: [category, severity, confidence]
      properties:
        category:
          $ref: "#/components/schemas/Category"
        severity:
          $ref: "#/components/schemas/Severity"
        reproducibility:
          $ref: "#/components/schemas/Reproducibility"
        confidence:
          type: number
          minimum: 0
          maximum: 1

    Content:
      type: object
      required: [title]
      properties:
        title:
          type: string
          maxLength: 256
        summary:
          type: string
          maxLength: 4096
        hypothesis:
          type: string
          maxLength: 2048

    EvidenceItem:
      type: object
      required: [type, content]
      properties:
        type:
          $ref: "#/components/schemas/EvidenceType"
        content:
          type: string
          description: Evidence content (JSON string, max 65536 bytes)
        redacted:
          type: boolean
          default: false

    FeedbackInput:
      type: object
      required: [reporter, subject, signal, content]
      properties:
        reporter:
          $ref: "#/components/schemas/Reporter"
        subject:
          $ref: "#/components/schemas/Subject"
        signal:
          $ref: "#/components/schemas/Signal"
        content:
          $ref: "#/components/schemas/Content"
        evidence:
          type: array
          items:
            $ref: "#/components/schemas/EvidenceItem"
          maxItems: 10

    FeedbackReceipt:
      type: object
      properties:
        receipt:
          type: object
          properties:
            id:
              type: string
            feedback_id:
              type: string
            status:
              $ref: "#/components/schemas/ReceiptStatus"
            evidence_ids:
              type: array
              items:
                type: string
            created_at:
              type: string
              format: date-time

    FeedbackSummary:
      type: object
      properties:
        id:
          type: string
        reporter:
          $ref: "#/components/schemas/Reporter"
        subject:
          $ref: "#/components/schemas/Subject"
        signal:
          $ref: "#/components/schemas/Signal"
        content:
          $ref: "#/components/schemas/Content"
        status:
          $ref: "#/components/schemas/FeedbackStatus"
        quality_score:
          type: number
          nullable: true
        observations:
          type: integer
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time

    FeedbackList:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/FeedbackSummary"
        meta:
          type: object
          properties:
            limit:
              type: integer
            offset:
              type: integer
            count:
              type: integer
            total:
              type: integer

    FeedbackDetail:
      type: object
      properties:
        data:
          allOf:
            - $ref: "#/components/schemas/FeedbackSummary"
            - type: object
              properties:
                evidence:
                  type: array
                  items:
                    type: object
                    properties:
                      id:
                        type: string
                      type:
                        $ref: "#/components/schemas/EvidenceType"
                      content:
                        type: string
                      redacted:
                        type: boolean
                      created_at:
                        type: string
                        format: date-time
                duplicate_of:
                  type: string
                  nullable: true

    EvidenceInput:
      oneOf:
        - $ref: "#/components/schemas/EvidenceItem"
        - type: object
          properties:
            evidence:
              type: array
              items:
                $ref: "#/components/schemas/EvidenceItem"
        - type: array
          items:
            $ref: "#/components/schemas/EvidenceItem"

    EvidenceAttachResult:
      type: object
      properties:
        data:
          type: object
          properties:
            feedback_id:
              type: string
            evidence_ids:
              type: array
              items:
                type: string
            created_at:
              type: string
              format: date-time

    Receipt:
      type: object
      properties:
        data:
          type: object
          properties:
            id:
              type: string
            feedback_id:
              type: string
              nullable: true
            observation_id:
              type: string
              nullable: true
            status:
              $ref: "#/components/schemas/ReceiptStatus"
            quality_score:
              type: number
              nullable: true
            duplicate_of:
              type: string
              nullable: true
            budget_remaining:
              type: integer
              nullable: true
            budget_delta:
              type: integer
              nullable: true
            created_at:
              type: string
              format: date-time

    Policy:
      type: object
      properties:
        version:
          type: string
        categories:
          type: array
          items:
            $ref: "#/components/schemas/Category"
        severity_levels:
          type: array
          items:
            $ref: "#/components/schemas/Severity"
        reproducibility_options:
          type: array
          items:
            $ref: "#/components/schemas/Reproducibility"
        evidence_types:
          type: array
          items:
            $ref: "#/components/schemas/EvidenceType"
        surface_kinds:
          type: array
          items:
            $ref: "#/components/schemas/SurfaceKind"
        limits:
          type: object
          properties:
            max_evidence_per_feedback:
              type: integer
            max_evidence_content_bytes:
              type: integer
            max_title_length:
              type: integer
            max_summary_length:
              type: integer
            max_hypothesis_length:
              type: integer
            confidence_range:
              type: object
              properties:
                min:
                  type: number
                max:
                  type: number
        endpoints:
          type: object

    Discovery:
      type: object
      properties:
        schema_version:
          type: string
        name:
          type: string
        description:
          type: string
        spec_url:
          type: string
        policy_url:
          type: string
        endpoints:
          type: object
        categories:
          type: array
          items:
            type: string
        evidence_types:
          type: array
          items:
            type: string
        contact:
          type: string