openapi: 3.1.0
info:
  title: DraftFilter
  version: 1.0.0
servers:
  - url: https://api.draftfilter.com
security:
  - bearerApiKey: []
  - apiKeyHeader: []
paths:
  /v1/score-jobs:
    get:
      operationId: listScoreJobs
      summary: List score jobs
      security:
        - bearerApiKey: []
        - apiKeyHeader: []
      parameters:
        - name: limit
          in: query
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 50
        - name: cursor
          in: query
          required: false
          schema:
            type: string
            minLength: 1
      responses:
        "200":
          description: Cursor-paginated score job summaries for the authenticated API key.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ScoreJobListResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "422":
          $ref: "#/components/responses/ValidationError"
    post:
      operationId: createScoreJob
      summary: Create a score job
      security:
        - bearerApiKey: []
        - apiKeyHeader: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ScoreJobCreateRequest"
      responses:
        "202":
          description: Score job accepted.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ScoreJobCreateResponse"
        "409":
          $ref: "#/components/responses/Conflict"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "402":
          $ref: "#/components/responses/PaymentRequired"
        "403":
          $ref: "#/components/responses/Forbidden"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "422":
          $ref: "#/components/responses/ValidationError"
  /v1/score-jobs/{jobId}:
    get:
      operationId: getScoreJob
      summary: Get a score job
      security:
        - bearerApiKey: []
        - apiKeyHeader: []
      parameters:
        - name: jobId
          in: path
          required: true
          schema:
            type: string
            pattern: "^job_[A-Za-z0-9][A-Za-z0-9_-]*$"
      responses:
        "200":
          description: Score job state and public results when complete.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ScoreJobResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
  /v1/score-jobs/{jobId}/cancel:
    post:
      operationId: cancelScoreJob
      summary: Cancel a queued score job
      security:
        - bearerApiKey: []
        - apiKeyHeader: []
      parameters:
        - name: jobId
          in: path
          required: true
          schema:
            type: string
            pattern: "^job_[A-Za-z0-9][A-Za-z0-9_-]*$"
      responses:
        "200":
          description: Score job cancellation result or current terminal state.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ScoreJobCancelResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "409":
          $ref: "#/components/responses/Conflict"
  /v1/rewrite-jobs:
    get:
      operationId: listRewriteJobs
      summary: List rewrite jobs
      security:
        - bearerApiKey: []
        - apiKeyHeader: []
      parameters:
        - name: limit
          in: query
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 50
        - name: cursor
          in: query
          required: false
          schema:
            type: string
            minLength: 1
      responses:
        "200":
          description: Cursor-paginated rewrite job summaries for the authenticated API key.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/RewriteJobListResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "422":
          $ref: "#/components/responses/ValidationError"
    post:
      operationId: createRewriteJob
      summary: Create a rewrite job
      security:
        - bearerApiKey: []
        - apiKeyHeader: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/RewriteJobCreateRequest"
      responses:
        "202":
          description: Rewrite job accepted.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/RewriteJobCreateResponse"
        "409":
          $ref: "#/components/responses/Conflict"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "402":
          $ref: "#/components/responses/PaymentRequired"
        "403":
          $ref: "#/components/responses/Forbidden"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "422":
          $ref: "#/components/responses/ValidationError"
  /v1/rewrite-jobs/{jobId}:
    get:
      operationId: getRewriteJob
      summary: Get a rewrite job
      security:
        - bearerApiKey: []
        - apiKeyHeader: []
      parameters:
        - name: jobId
          in: path
          required: true
          schema:
            type: string
            pattern: "^job_[A-Za-z0-9][A-Za-z0-9_-]*$"
      responses:
        "200":
          description: Rewrite job state and public results when complete.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/RewriteJobResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
  /v1/rewrite-jobs/{jobId}/cancel:
    post:
      operationId: cancelRewriteJob
      summary: Cancel a queued rewrite job
      security:
        - bearerApiKey: []
        - apiKeyHeader: []
      parameters:
        - name: jobId
          in: path
          required: true
          schema:
            type: string
            pattern: "^job_[A-Za-z0-9][A-Za-z0-9_-]*$"
      responses:
        "200":
          description: Rewrite job cancellation result or current terminal state.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/RewriteJobCancelResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "409":
          $ref: "#/components/responses/Conflict"
components:
  securitySchemes:
    bearerApiKey:
      type: http
      scheme: bearer
      description: "Customer API key supplied as Authorization: Bearer."
    apiKeyHeader:
      type: apiKey
      in: header
      name: X-Api-Key
  schemas:
    ScoreJobCreateRequest:
      type: object
      additionalProperties: false
      required:
        - items
      properties:
        idempotencyKey:
          type:
            - string
            - "null"
          minLength: 1
          maxLength: 128
          description: Reusing the same key with the same score request for the same API key returns the existing job. Reusing it with a different request returns 409.
        items:
          type: array
          minItems: 1
          maxItems: 100
          description: Total text across all items must not exceed 100000 characters.
          items:
            $ref: "#/components/schemas/ScoreItem"
    ScoreItem:
      type: object
      additionalProperties: false
      required:
        - id
        - text
      properties:
        id:
          type: string
          minLength: 1
          maxLength: 128
        text:
          type: string
          minLength: 1
          maxLength: 20000
    RewriteJobCreateRequest:
      type: object
      additionalProperties: false
      required:
        - items
      properties:
        idempotencyKey:
          type:
            - string
            - "null"
          minLength: 1
          maxLength: 128
          description: Reusing the same key with the same rewrite request for the same API key returns the existing job. Reusing it with a different request returns 409.
        candidateCount:
          type: integer
          minimum: 1
          maximum: 4
          default: 1
          description: Number of rewrite candidates to generate per item before selecting the least AI-like option.
        items:
          type: array
          minItems: 1
          maxItems: 10
          description: Total text across all items must not exceed 20000 characters.
          items:
            $ref: "#/components/schemas/RewriteItem"
    RewriteItem:
      type: object
      additionalProperties: false
      required:
        - id
        - text
      properties:
        id:
          type: string
          minLength: 1
          maxLength: 128
        text:
          type: string
          minLength: 1
          maxLength: 5000
    ScoreJobCreateResponse:
      type: object
      additionalProperties: false
      required:
        - jobId
        - state
      properties:
        jobId:
          type: string
        state:
          enum:
            - queued
            - running
            - succeeded
            - failed
            - cancelled
    RewriteJobCreateResponse:
      type: object
      additionalProperties: false
      required:
        - jobId
        - state
      properties:
        jobId:
          type: string
        state:
          enum:
            - queued
            - running
            - succeeded
            - failed
            - cancelled
    ScoreJobListResponse:
      type: object
      additionalProperties: false
      required:
        - items
        - nextCursor
      properties:
        items:
          type: array
          items:
            $ref: "#/components/schemas/ScoreJobSummary"
        nextCursor:
          type:
            - string
            - "null"
    ScoreJobSummary:
      type: object
      additionalProperties: false
      required:
        - jobId
        - state
        - createdAt
        - updatedAt
        - completedAt
        - itemCount
        - inputBytes
        - resultSummary
        - errorSummary
      properties:
        jobId:
          type: string
        state:
          enum:
            - queued
            - running
            - succeeded
            - failed
            - cancelled
        createdAt:
          type: string
        updatedAt:
          type: string
        completedAt:
          type:
            - string
            - "null"
        itemCount:
          type: integer
          minimum: 0
        inputBytes:
          type: integer
          minimum: 0
        resultSummary:
          anyOf:
            - $ref: "#/components/schemas/ResultSummary"
            - type: "null"
        errorSummary:
          anyOf:
            - $ref: "#/components/schemas/ErrorSummary"
            - type: "null"
    RewriteJobListResponse:
      type: object
      additionalProperties: false
      required:
        - items
        - nextCursor
      properties:
        items:
          type: array
          items:
            $ref: "#/components/schemas/RewriteJobSummary"
        nextCursor:
          type:
            - string
            - "null"
    RewriteJobSummary:
      $ref: "#/components/schemas/ScoreJobSummary"
    ScoreJobCancelResponse:
      $ref: "#/components/schemas/ScoreJobResponse"
    RewriteJobCancelResponse:
      $ref: "#/components/schemas/RewriteJobResponse"
    ScoreJobResponse:
      type: object
      additionalProperties: false
      required:
        - jobId
        - state
      properties:
        jobId:
          type: string
        state:
          enum:
            - queued
            - running
            - succeeded
            - failed
            - cancelled
        results:
          type: array
          items:
            $ref: "#/components/schemas/PublicScoreResult"
        error:
          $ref: "#/components/schemas/ErrorSummary"
    RewriteJobResponse:
      type: object
      additionalProperties: false
      required:
        - jobId
        - state
      properties:
        jobId:
          type: string
        state:
          enum:
            - queued
            - running
            - succeeded
            - failed
            - cancelled
        results:
          type: array
          items:
            $ref: "#/components/schemas/PublicRewriteResult"
        error:
          $ref: "#/components/schemas/ErrorSummary"
    PublicScoreResult:
      type: object
      additionalProperties: false
      required:
        - id
        - is_ai
      properties:
        id:
          type: string
        is_ai:
          type: integer
          minimum: 0
          maximum: 100
    PublicRewriteResult:
      type: object
      additionalProperties: false
      required:
        - id
        - original_is_ai
        - rewritten_is_ai
        - rewrittenText
      properties:
        id:
          type: string
        original_is_ai:
          type: integer
          minimum: 0
          maximum: 100
        rewritten_is_ai:
          type: integer
          minimum: 0
          maximum: 100
        rewrittenText:
          type: string
    ErrorSummary:
      type: object
      additionalProperties: false
      required:
        - code
        - message
        - retryable
      properties:
        code:
          type: string
        message:
          type: string
        retryable:
          type: boolean
    ResultSummary:
      type: object
      additionalProperties: false
      required:
        - succeededItems
        - failedItems
      properties:
        succeededItems:
          type: integer
          minimum: 0
        failedItems:
          type: integer
          minimum: 0
    ErrorResponse:
      type: object
      additionalProperties: false
      required:
        - error
      properties:
        error:
          type: object
          additionalProperties: false
          required:
            - code
            - message
          properties:
            code:
              type: string
            message:
              type: string
  responses:
    BadRequest:
      description: Request could not be accepted.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
    Unauthorized:
      description: Missing or invalid API key.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
    Forbidden:
      description: API key does not have the required scope.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
    PaymentRequired:
      description: Billing is disabled for the account.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
    NotFound:
      description: Job was not found.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
    Conflict:
      description: The request conflicts with the current job state or idempotency key.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
    ValidationError:
      description: Request validation failed.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
    TooManyRequests:
      description: The request exceeded a rate limit.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
