openapi: 3.0.3
info:
  title: PDFEditor.pk Developer API
  version: "1.0.0"
  description: |
    REST API for the PDFEditor.pk PDF toolchain. Authenticate every
    request (except /health) with `Authorization: Bearer <api_key>`.
    Uploads use `multipart/form-data`; successful responses stream the
    resulting file with `Content-Disposition: attachment`.
  contact:
    name: PDFEditor.pk API support
    email: api@pdfeditor.pk
    url: https://pdfeditor.pk/docs/api
  license:
    name: Proprietary
    url: https://pdfeditor.pk/terms

servers:
  - url: https://pdfeditor.pk
    description: Production

tags:
  - name: System
    description: Liveness and operational endpoints.
  - name: Manipulate
    description: Operate on existing PDFs (merge, split, compress).
  - name: Convert
    description: Convert PDFs to other formats.

components:
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: API key
      description: |
        Long-lived API key issued from your dashboard. Format:
        `pdl_live_xxxxxxxxxxxxxxxx`. SHA-256 hashed at rest.

  schemas:
    Error:
      type: object
      required: [error, code]
      properties:
        error:
          type: string
          example: "Unauthorized: API key not recognized."
        code:
          type: string
          example: invalid_api_key
    Health:
      type: object
      required: [status, service, version]
      properties:
        status:
          type: string
          example: ok
        service:
          type: string
          example: pdfeditor-api
        version:
          type: string
          example: v1

  responses:
    Unauthorized:
      description: Missing or invalid API key.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            missing:
              value:
                error: Missing Authorization Bearer header.
                code: missing_api_key
            invalid:
              value:
                error: Unauthorized API key not recognized.
                code: invalid_api_key
    Forbidden:
      description: Key was revoked.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    BadRequest:
      description: Request was malformed.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    PayloadTooLarge:
      description: Upload exceeded the 200 MB limit.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    EncryptedPdf:
      description: Input PDF is password-protected.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    RateLimited:
      description: Per-key quota exceeded.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
      headers:
        Retry-After:
          schema: { type: integer }
          description: Seconds until the next request is allowed.
    ServerError:
      description: Processing failed.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'

security:
  - BearerAuth: []

paths:
  /api/v1/health:
    get:
      tags: [System]
      summary: Liveness probe
      description: Public endpoint that confirms the API service is reachable. No auth required.
      security: []
      responses:
        "200":
          description: Service is up.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Health'

  /api/v1/merge:
    post:
      tags: [Manipulate]
      summary: Merge multiple PDFs into one
      description: Combine two or more PDF files in upload order into a single PDF.
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required: [file]
              properties:
                file:
                  type: array
                  minItems: 2
                  items:
                    type: string
                    format: binary
              encoding:
                file:
                  contentType: application/pdf
      responses:
        "200":
          description: The merged PDF.
          content:
            application/pdf:
              schema:
                type: string
                format: binary
        "400": { $ref: '#/components/responses/BadRequest' }
        "401": { $ref: '#/components/responses/Unauthorized' }
        "403": { $ref: '#/components/responses/Forbidden' }
        "413": { $ref: '#/components/responses/PayloadTooLarge' }
        "422": { $ref: '#/components/responses/EncryptedPdf' }
        "429": { $ref: '#/components/responses/RateLimited' }
        "500": { $ref: '#/components/responses/ServerError' }

  /api/v1/split:
    post:
      tags: [Manipulate]
      summary: Split a PDF into multiple PDFs
      description: |
        With no `pages` field every page becomes its own PDF.
        With `pages` (e.g. `1-3,5,8-10`) each comma-separated segment becomes one output PDF.
        The response is always a ZIP archive.
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required: [file]
              properties:
                file:
                  type: string
                  format: binary
                pages:
                  type: string
                  description: Optional comma-separated ranges, 1-indexed.
                  example: 1-3,5,8-10
      responses:
        "200":
          description: ZIP of the resulting PDFs.
          content:
            application/zip:
              schema:
                type: string
                format: binary
        "400": { $ref: '#/components/responses/BadRequest' }
        "401": { $ref: '#/components/responses/Unauthorized' }
        "403": { $ref: '#/components/responses/Forbidden' }
        "413": { $ref: '#/components/responses/PayloadTooLarge' }
        "422": { $ref: '#/components/responses/EncryptedPdf' }
        "429": { $ref: '#/components/responses/RateLimited' }
        "500": { $ref: '#/components/responses/ServerError' }

  /api/v1/compress:
    post:
      tags: [Manipulate]
      summary: Compress a PDF
      description: Re-serialize the PDF with stream compression. Future versions add raster downsampling per quality preset.
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required: [file]
              properties:
                file:
                  type: string
                  format: binary
                quality:
                  type: string
                  enum: [low, medium, high]
                  default: medium
      responses:
        "200":
          description: The compressed PDF.
          content:
            application/pdf:
              schema:
                type: string
                format: binary
        "400": { $ref: '#/components/responses/BadRequest' }
        "401": { $ref: '#/components/responses/Unauthorized' }
        "403": { $ref: '#/components/responses/Forbidden' }
        "413": { $ref: '#/components/responses/PayloadTooLarge' }
        "422": { $ref: '#/components/responses/EncryptedPdf' }
        "429": { $ref: '#/components/responses/RateLimited' }
        "500": { $ref: '#/components/responses/ServerError' }

  /api/v1/convert/pdf-to-jpg:
    post:
      tags: [Convert]
      summary: Convert PDF pages to JPG
      description: |
        Single-page PDFs return a JPG directly; multi-page PDFs return a
        ZIP containing one JPG per page.
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required: [file]
              properties:
                file:
                  type: string
                  format: binary
                dpi:
                  type: integer
                  enum: [72, 96, 150, 200, 300]
                  default: 150
      responses:
        "200":
          description: One JPG (single-page PDFs) or a ZIP of JPGs.
          content:
            image/jpeg:
              schema:
                type: string
                format: binary
            application/zip:
              schema:
                type: string
                format: binary
        "400": { $ref: '#/components/responses/BadRequest' }
        "401": { $ref: '#/components/responses/Unauthorized' }
        "403": { $ref: '#/components/responses/Forbidden' }
        "413": { $ref: '#/components/responses/PayloadTooLarge' }
        "422": { $ref: '#/components/responses/EncryptedPdf' }
        "429": { $ref: '#/components/responses/RateLimited' }
        "500": { $ref: '#/components/responses/ServerError' }

  /api/v1/convert/pdf-to-word:
    post:
      tags: [Convert]
      summary: Convert PDF to DOCX
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required: [file]
              properties:
                file:
                  type: string
                  format: binary
      responses:
        "200":
          description: A Word (.docx) document.
          content:
            application/vnd.openxmlformats-officedocument.wordprocessingml.document:
              schema:
                type: string
                format: binary
        "400": { $ref: '#/components/responses/BadRequest' }
        "401": { $ref: '#/components/responses/Unauthorized' }
        "403": { $ref: '#/components/responses/Forbidden' }
        "413": { $ref: '#/components/responses/PayloadTooLarge' }
        "422": { $ref: '#/components/responses/EncryptedPdf' }
        "429": { $ref: '#/components/responses/RateLimited' }
        "500": { $ref: '#/components/responses/ServerError' }
