openapi: 3.0.3
info:
  title: Redact PDF AI - Redaction API
  version: 1.0.0
  description: |
    Programmatic redaction API for PDFs.

    Core model:
    - Create an async Job by uploading one or more files.
    - Poll the Job until it reaches a terminal state.
    - Download outputs per Document.

servers:
  - url: https://www.redact-pdf.ai
paths:
  /v1/demo:
    get:
      summary: Keyless demo (no API key)
      description: >
        Try the API with no API key and no upload. Returns a description of a
        real redaction performed on a fixed synthetic-PII sample document,
        including the detected PII categories and a link to the redacted PDF.
        Zero per-request cost; handles no user data.
      security: []
      responses:
        '200':
          description: Demo result
          content:
            application/json:
              schema:
                type: object
                properties:
                  status: { type: string }
                  message: { type: string }
                  sample_input: { type: string }
                  detected_pii:
                    type: array
                    items:
                      type: object
                      properties:
                        category: { type: string }
                        example: { type: string }
                        masked: { type: boolean }
                  redacted_pdf_path: { type: string }

  /v1/demo/sample.pdf:
    get:
      summary: Keyless demo — redacted sample PDF (no API key)
      description: The redacted output of the built-in synthetic-PII sample.
      security: []
      responses:
        '200':
          description: Redacted sample PDF
          content:
            application/pdf:
              schema:
                type: string
                format: binary

  /v1/me:
    get:
      summary: Get current API identity
      description: Validate an API key and return basic account info.
      security:
        - apiKeyAuth: []
      responses:
        '200':
          description: Authenticated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Me'
        '401':
          description: Unauthorized

  /v1/jobs:
    post:
      summary: Create a redaction job
      description: Upload one or more PDFs to create an async redaction job.
      security:
        - apiKeyAuth: []
      parameters:
        - name: X-Idempotency-Key
          in: header
          required: false
          description: Prevent duplicate job creation on retries.
          schema:
            type: string
            maxLength: 200
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              properties:
                files:
                  type: array
                  items:
                    type: string
                    format: binary
                  description: PDF files to redact.
                pii_categories:
                  type: string
                  description: JSON array string of PII categories. If omitted, user defaults apply.
                  example: '["Person","Email","PhoneNumber"]'
                pii_included_terms:
                  type: string
                  description: >
                    JSON array string of terms that must ALWAYS be redacted, even when
                    the AI would not classify them as PII (whole-word, case-insensitive).
                    If omitted, the API key owner's saved defaults apply. Takes
                    precedence over pii_excluded_terms on conflict.
                  example: '["Project Titan","ACME-1234"]'
                pii_excluded_terms:
                  type: string
                  description: >
                    JSON array string of terms that must NEVER be redacted, even when the
                    AI detects them as PII. If omitted, the API key owner's saved defaults
                    apply.
                  example: '["Acme Corp","support@acme.com"]'
                retention:
                  type: string
                  enum: [ephemeral, studio]
                  default: ephemeral
                  description: |
                    Retention mode.
                    - ephemeral: delete originals after processing; keep outputs for a short time
                    - studio: keep originals + masks so you can open in the Studio
              required:
                - files
      responses:
        '200':
          description: Job created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Job'
        '400':
          description: Invalid request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized
        '402':
          description: Payment required (quota exceeded)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          description: Rate limited
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /v1/jobs/init:
    post:
      summary: Reserve a job for direct-to-blob upload (large PDFs)
      description: >
        Two-step upload that never streams file bytes through the API. Send file
        metadata; receive a job id plus one short-lived write URL per file. PUT
        each file directly to its URL, then call POST /v1/jobs/{job_id}/commit.
        PDFs only — images use the multipart POST /v1/jobs path.
      security:
        - apiKeyAuth: []
      parameters:
        - name: X-Idempotency-Key
          in: header
          required: false
          schema:
            type: string
            maxLength: 200
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [files]
              properties:
                files:
                  type: array
                  items:
                    type: object
                    required: [filename, content_type, size_bytes]
                    properties:
                      filename:
                        type: string
                      content_type:
                        type: string
                        example: application/pdf
                      size_bytes:
                        type: integer
                pii_categories:
                  type: array
                  items:
                    type: string
                pii_included_terms:
                  type: array
                  items:
                    type: string
                pii_excluded_terms:
                  type: array
                  items:
                    type: string
                retention:
                  type: string
                  enum: [ephemeral, studio]
                  default: ephemeral
      responses:
        '200':
          description: Job reserved; upload URLs returned
          content:
            application/json:
              schema:
                type: object
                properties:
                  job_id:
                    type: string
                    format: uuid
                  uploads:
                    type: array
                    items:
                      type: object
                      properties:
                        document_id:
                          type: string
                          format: uuid
                        filename:
                          type: string
                        upload_url:
                          type: string
        '401':
          description: Unauthorized
        '402':
          description: Payment required (quota exceeded)
        '413':
          description: A file exceeds the size limit

  /v1/jobs/{job_id}/commit:
    post:
      summary: Commit a direct-to-blob upload
      description: >
        Verify the uploaded blobs landed and release the job to processing.
        Idempotent — safe to retry after fixing a missed upload.
      security:
        - apiKeyAuth: []
      parameters:
        - name: job_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Job released (or errored if no blob landed)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Job'
        '401':
          description: Unauthorized
        '404':
          description: Not found

  /v1/jobs/{job_id}:
    get:
      summary: Get job status
      security:
        - apiKeyAuth: []
      parameters:
        - name: job_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Job status
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Job'
        '401':
          description: Unauthorized
        '404':
          description: Not found

    delete:
      summary: Delete a job
      description: Purge a job and associated files (subject to retention policy).
      security:
        - apiKeyAuth: []
      parameters:
        - name: job_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
        '401':
          description: Unauthorized
        '404':
          description: Not found

  /v1/documents/{doc_id}/output:
    get:
      summary: Download redacted output
      security:
        - apiKeyAuth: []
      parameters:
        - name: doc_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Redacted PDF
          content:
            application/pdf:
              schema:
                type: string
                format: binary
        '401':
          description: Unauthorized
        '404':
          description: Not found

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

  schemas:
    Error:
      type: object
      properties:
        error:
          type: string
        code:
          type: string
          example: quota_exceeded
        request_id:
          type: string
          example: req_123

    Me:
      type: object
      properties:
        user_id:
          type: string
          example: user_123
        email:
          type: string
          example: you@company.com

    Document:
      type: object
      properties:
        id:
          type: string
          format: uuid
        file_name:
          type: string
          example: contract.pdf
        status:
          type: string
          example: uploaded
          enum: [uploaded, analyzing, redacting, redacted, error]
        page_count:
          type: integer
          example: 3
        error_message:
          type: string
          nullable: true

    Job:
      type: object
      properties:
        job_id:
          type: string
          format: uuid
        status:
          type: string
          enum: [uploaded, analyzing, redacting, redacted, error]
          example: analyzing
        retention:
          type: string
          enum: [ephemeral, studio]
          example: ephemeral
        created_at:
          type: string
          format: date-time
        documents:
          type: array
          items:
            $ref: '#/components/schemas/Document'
