openapi: "3.1.0"
info:
  title: NextPDF Connect API
  version: "1.0.0-alpha"
  description: |
    RESTful API for NextPDF PDF 2.0 engine. Exposes PDF generation,
    manipulation, and analysis as stateless HTTP operations.
  contact:
    name: NextPDF Support
    email: jerry@nextpdf.dev
    url: https://nextpdf.dev/docs/connect
  license:
    name: Apache-2.0
    url: https://www.apache.org/licenses/LICENSE-2.0

servers:
  - url: http://localhost:8080
    description: Local development

security:
  - apiKey: []

paths:
  /healthz:
    get:
      operationId: healthCheck
      summary: Liveness probe
      description: Returns 200 if the server process is alive.
      tags: [System]
      security: []
      responses:
        "200":
          description: Server is alive
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HealthResponse"
        "503":
          description: Server is unhealthy

  /readyz:
    get:
      operationId: readinessCheck
      summary: Readiness probe
      description: Returns 200 if the server is ready to accept requests.
      tags: [System]
      security: []
      responses:
        "200":
          description: Server is ready
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ReadinessResponse"
        "503":
          description: Server is not ready

  /api/v1/render:
    post:
      operationId: renderPdf
      summary: Render a PDF document
      description: |
        Synchronous stateless render. Accepts a document specification
        with an operations array and returns the generated PDF binary.
      tags: [Render]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/RenderRequest"
      responses:
        "200":
          description: PDF generated successfully
          content:
            application/pdf:
              schema:
                type: string
                format: binary
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "422":
          $ref: "#/components/responses/ValidationError"
        "413":
          $ref: "#/components/responses/PayloadTooLarge"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "500":
          $ref: "#/components/responses/InternalError"
        "504":
          $ref: "#/components/responses/RequestTimeout"

  /api/v1/capabilities:
    get:
      operationId: getCapabilities
      summary: List available capabilities
      description: Returns operations available to the authenticated client based on their tier.
      tags: [System]
      responses:
        "200":
          description: Available capabilities
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CapabilitiesResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"

  /api/v1/jobs:
    post:
      operationId: submitJob
      summary: Submit async render job
      description: |
        Submit a PDF render job for asynchronous processing. Returns
        immediately with a job ID for status polling.
      tags: [Jobs]
      parameters:
        - name: Idempotency-Key
          in: header
          required: false
          schema:
            type: string
          description: Unique key for idempotent submission
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/RenderRequest"
      responses:
        "201":
          description: Job submitted and processed
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/JobResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "409":
          description: Idempotency conflict
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ProblemDetails"
        "422":
          $ref: "#/components/responses/ValidationError"
        "429":
          $ref: "#/components/responses/TooManyRequests"

  /api/v1/jobs/{id}:
    get:
      operationId: getJobStatus
      summary: Get job status
      tags: [Jobs]
      parameters:
        - $ref: "#/components/parameters/JobId"
      responses:
        "200":
          description: Job status
          headers:
            Retry-After:
              schema:
                type: integer
              description: Seconds until next poll (for non-terminal jobs)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/JobResponse"
        "404":
          $ref: "#/components/responses/NotFound"
    delete:
      operationId: cancelJob
      summary: Cancel or delete a job
      tags: [Jobs]
      parameters:
        - $ref: "#/components/parameters/JobId"
      responses:
        "200":
          description: Job cancelled
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/JobResponse"
        "204":
          description: Job deleted (completed job removed)
        "404":
          $ref: "#/components/responses/NotFound"

  /api/v1/jobs/{id}/result:
    get:
      operationId: getJobResult
      summary: Download job result
      tags: [Jobs]
      parameters:
        - $ref: "#/components/parameters/JobId"
      responses:
        "200":
          description: Completed PDF
          content:
            application/pdf:
              schema:
                type: string
                format: binary
        "404":
          $ref: "#/components/responses/NotFound"
        "409":
          description: Job not yet completed
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ProblemDetails"

  # ── Session Endpoints (stateful, disabled by default) ─────────────────

  /api/v1/sessions:
    post:
      operationId: createSession
      summary: Create a new document session
      description: |
        Creates a multi-step document building session. The session holds
        server-side state allowing incremental operations (add text, pages,
        images, etc.) before a final render to PDF.

        Sessions are disabled by default. Enable with `NEXTPDF_SESSIONS_ENABLED=true`.
      tags: [Sessions]
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SessionCreateRequest"
      responses:
        "201":
          description: Session created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SessionResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "422":
          $ref: "#/components/responses/ValidationError"
        "429":
          description: Maximum active sessions per client exceeded
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ProblemDetails"

  /api/v1/sessions/{sessionId}:
    get:
      operationId: getSession
      summary: Get session metadata
      tags: [Sessions]
      parameters:
        - $ref: "#/components/parameters/SessionId"
      responses:
        "200":
          description: Session metadata
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SessionResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
    delete:
      operationId: destroySession
      summary: Destroy session and its document
      tags: [Sessions]
      parameters:
        - $ref: "#/components/parameters/SessionId"
      responses:
        "200":
          description: Session destroyed
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SuccessEnvelope"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"

  /api/v1/sessions/{sessionId}/pages:
    post:
      operationId: sessionAddPage
      summary: Add a page to the session document
      tags: [Sessions]
      parameters:
        - $ref: "#/components/parameters/SessionId"
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SessionOperationPayload"
      responses:
        "200":
          description: Page added, updated session returned
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SessionResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "409":
          $ref: "#/components/responses/Conflict"
        "422":
          $ref: "#/components/responses/ValidationError"

  /api/v1/sessions/{sessionId}/text:
    post:
      operationId: sessionAddText
      summary: Add text to the session document
      tags: [Sessions]
      parameters:
        - $ref: "#/components/parameters/SessionId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SessionOperationPayload"
      responses:
        "200":
          description: Text added, updated session returned
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SessionResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "409":
          $ref: "#/components/responses/Conflict"
        "422":
          $ref: "#/components/responses/ValidationError"

  /api/v1/sessions/{sessionId}/images:
    post:
      operationId: sessionAddImage
      summary: Add an image to the session document
      tags: [Sessions]
      parameters:
        - $ref: "#/components/parameters/SessionId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SessionOperationPayload"
      responses:
        "200":
          description: Image added, updated session returned
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SessionResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "409":
          $ref: "#/components/responses/Conflict"
        "422":
          $ref: "#/components/responses/ValidationError"

  /api/v1/sessions/{sessionId}/tables:
    post:
      operationId: sessionAddTable
      summary: Add a table to the session document
      tags: [Sessions]
      parameters:
        - $ref: "#/components/parameters/SessionId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SessionOperationPayload"
      responses:
        "200":
          description: Table added, updated session returned
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SessionResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "409":
          $ref: "#/components/responses/Conflict"
        "422":
          $ref: "#/components/responses/ValidationError"

  /api/v1/sessions/{sessionId}/html:
    post:
      operationId: sessionAddHtml
      summary: Add an HTML block to the session document
      tags: [Sessions]
      parameters:
        - $ref: "#/components/parameters/SessionId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SessionOperationPayload"
      responses:
        "200":
          description: HTML added, updated session returned
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SessionResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "409":
          $ref: "#/components/responses/Conflict"
        "422":
          $ref: "#/components/responses/ValidationError"

  /api/v1/sessions/{sessionId}/font:
    put:
      operationId: sessionSetFont
      summary: Set the active font for the session document
      tags: [Sessions]
      parameters:
        - $ref: "#/components/parameters/SessionId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SessionOperationPayload"
      responses:
        "200":
          description: Font set, updated session returned
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SessionResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "409":
          $ref: "#/components/responses/Conflict"
        "422":
          $ref: "#/components/responses/ValidationError"

  /api/v1/sessions/{sessionId}/render:
    post:
      operationId: sessionRender
      summary: Render session document to PDF
      description: |
        Replays all accumulated operations against the session document and
        returns the generated PDF. The session is marked as rendered (terminal
        state) — no further operations are allowed.
      tags: [Sessions]
      parameters:
        - $ref: "#/components/parameters/SessionId"
      responses:
        "200":
          description: PDF rendered successfully
          content:
            application/pdf:
              schema:
                type: string
                format: binary
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "409":
          $ref: "#/components/responses/Conflict"
        "422":
          $ref: "#/components/responses/ValidationError"

  # ── Capability Operations (tier-gated) ────────────────────────────────

  /api/v1/extract-text:
    post:
      operationId: extractText
      summary: Extract text from a PDF
      tags: [Capabilities — Core]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CapabilityPayload"
      responses:
        "200":
          description: Extracted text data
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SuccessEnvelope"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "422":
          $ref: "#/components/responses/ValidationError"

  /api/v1/merge:
    post:
      operationId: mergePdfs
      summary: Merge multiple PDFs into one
      tags: [Capabilities — Core]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CapabilityPayload"
      responses:
        "200":
          description: Merged PDF binary
          content:
            application/pdf:
              schema:
                type: string
                format: binary
        "401":
          $ref: "#/components/responses/Unauthorized"
        "422":
          $ref: "#/components/responses/ValidationError"

  /api/v1/split:
    post:
      operationId: splitPdf
      summary: Split a PDF into parts
      tags: [Capabilities — Core]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CapabilityPayload"
      responses:
        "200":
          description: Split PDF binary
          content:
            application/pdf:
              schema:
                type: string
                format: binary
        "401":
          $ref: "#/components/responses/Unauthorized"
        "422":
          $ref: "#/components/responses/ValidationError"

  /api/v1/sign:
    post:
      operationId: signPdf
      summary: Digitally sign a PDF
      description: Requires Pro or Enterprise tier.
      tags: [Capabilities — Pro]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CapabilityPayload"
      responses:
        "200":
          description: Signed PDF binary
          content:
            application/pdf:
              schema:
                type: string
                format: binary
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "422":
          $ref: "#/components/responses/ValidationError"

  /api/v1/fill-form:
    post:
      operationId: fillForm
      summary: Fill PDF form fields
      description: Requires Pro or Enterprise tier.
      tags: [Capabilities — Pro]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CapabilityPayload"
      responses:
        "200":
          description: Filled PDF binary
          content:
            application/pdf:
              schema:
                type: string
                format: binary
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "422":
          $ref: "#/components/responses/ValidationError"

  /api/v1/redact:
    post:
      operationId: redactPdf
      summary: Redact content from a PDF
      description: Requires Pro or Enterprise tier.
      tags: [Capabilities — Pro]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CapabilityPayload"
      responses:
        "200":
          description: Redacted PDF binary
          content:
            application/pdf:
              schema:
                type: string
                format: binary
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "422":
          $ref: "#/components/responses/ValidationError"

  /api/v1/compare:
    post:
      operationId: compareDocuments
      summary: Compare two PDF documents
      description: Requires Pro or Enterprise tier.
      tags: [Capabilities — Pro]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CapabilityPayload"
      responses:
        "200":
          description: Comparison results
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SuccessEnvelope"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "422":
          $ref: "#/components/responses/ValidationError"

  /api/v1/check-accessibility:
    post:
      operationId: checkAccessibility
      summary: Check PDF accessibility compliance
      description: Requires Pro or Enterprise tier.
      tags: [Capabilities — Pro]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CapabilityPayload"
      responses:
        "200":
          description: Accessibility report
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SuccessEnvelope"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "422":
          $ref: "#/components/responses/ValidationError"

  /api/v1/optimize:
    post:
      operationId: optimizePdf
      summary: Optimize a PDF for size or quality
      description: Requires Pro or Enterprise tier.
      tags: [Capabilities — Pro]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CapabilityPayload"
      responses:
        "200":
          description: Optimized PDF binary
          content:
            application/pdf:
              schema:
                type: string
                format: binary
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "422":
          $ref: "#/components/responses/ValidationError"

  /api/v1/compliance-check:
    post:
      operationId: complianceCheck
      summary: Validate PDF/A and regulatory compliance
      description: Requires Enterprise tier.
      tags: [Capabilities — Enterprise]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CapabilityPayload"
      responses:
        "200":
          description: Compliance report
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SuccessEnvelope"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "422":
          $ref: "#/components/responses/ValidationError"

  /api/v1/forensic-analyze:
    post:
      operationId: forensicAnalyze
      summary: Forensic analysis of a PDF document
      description: Requires Enterprise tier.
      tags: [Capabilities — Enterprise]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CapabilityPayload"
      responses:
        "200":
          description: Forensic analysis report
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SuccessEnvelope"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "422":
          $ref: "#/components/responses/ValidationError"

  /api/v1/ai-certify:
    post:
      operationId: aiCertify
      summary: AI-readiness certification for a PDF
      description: Requires Enterprise tier.
      tags: [Capabilities — Enterprise]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CapabilityPayload"
      responses:
        "200":
          description: Certification report
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SuccessEnvelope"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "422":
          $ref: "#/components/responses/ValidationError"

components:
  securitySchemes:
    apiKey:
      type: apiKey
      in: header
      name: Authorization
      description: "Bearer token: `Bearer npk_live_{kid}_{secret}`"

  parameters:
    JobId:
      name: id
      in: path
      required: true
      schema:
        type: string
      description: Job identifier
    SessionId:
      name: sessionId
      in: path
      required: true
      schema:
        type: string
        pattern: "^sess_[a-f0-9]{24}$"
      description: Session identifier (sess_ prefix + 24 hex characters)

  schemas:
    RenderRequest:
      type: object
      required: [operations]
      properties:
        page_size:
          type: string
          default: A4
          description: ISO page size name
        orientation:
          type: string
          enum: [portrait, landscape]
          default: portrait
        operations:
          type: array
          items:
            $ref: "#/components/schemas/Operation"
          minItems: 1
          description: Ordered list of PDF operations
        output:
          type: object
          properties:
            compress:
              type: boolean
              default: true

    Operation:
      type: object
      required: [type]
      properties:
        type:
          type: string
          enum:
            - set_font
            - add_text
            - add_image
            - add_page
            - add_html
            - add_table
      discriminator:
        propertyName: type
      description: A single PDF operation

    HealthResponse:
      type: object
      properties:
        status:
          type: string
          enum: [ok, degraded]
        timestamp:
          type: string
          format: date-time

    ReadinessResponse:
      type: object
      properties:
        status:
          type: string
          enum: [ready, not_ready]
        checks:
          type: object
          additionalProperties:
            type: boolean

    CapabilitiesResponse:
      type: object
      properties:
        data:
          type: object
          properties:
            tier:
              type: string
              enum: [core, pro, enterprise]
            operations:
              type: array
              items:
                type: string
        meta:
          $ref: "#/components/schemas/ResponseMeta"

    JobResponse:
      type: object
      properties:
        data:
          type: object
          properties:
            job_id:
              type: string
            status:
              type: string
              enum: [pending, running, completed, failed, cancelled]
            created_at:
              type: string
              format: date-time
            started_at:
              type: ["string", "null"]
              format: date-time
            completed_at:
              type: ["string", "null"]
              format: date-time
            error:
              type: ["string", "null"]
            progress:
              type: ["integer", "null"]
              minimum: 0
              maximum: 100
            poll_url:
              type: string
            result_url:
              type: ["string", "null"]
        meta:
          $ref: "#/components/schemas/ResponseMeta"

    SessionCreateRequest:
      type: object
      properties:
        page_size:
          type: string
          default: A4
          description: ISO page size name
        orientation:
          type: string
          enum: [portrait, landscape]
          default: portrait
        document_id:
          type: string
          description: Custom document identifier (auto-generated if omitted)

    SessionData:
      type: object
      properties:
        session_id:
          type: string
          description: Unique session identifier
        status:
          type: string
          enum: [active, rendered, expired]
        document_id:
          type: string
        operation_count:
          type: integer
          minimum: 0
        created_at:
          type: string
          format: date-time
        last_activity_at:
          type: string
          format: date-time
        page_size:
          type: ["string", "null"]
        orientation:
          type: ["string", "null"]

    SessionResponse:
      type: object
      properties:
        data:
          $ref: "#/components/schemas/SessionData"
        meta:
          $ref: "#/components/schemas/ResponseMeta"

    SessionOperationPayload:
      type: object
      description: |
        Operation-specific payload. Fields vary by operation type.
        Passed directly to the underlying PDF tool.
      additionalProperties: true

    CapabilityPayload:
      type: object
      description: |
        Operation-specific payload for capability endpoints.
        Fields depend on the specific operation being invoked.
      additionalProperties: true

    SuccessEnvelope:
      type: object
      properties:
        data:
          type: object
          additionalProperties: true
        meta:
          $ref: "#/components/schemas/ResponseMeta"

    ResponseMeta:
      type: object
      properties:
        request_id:
          type: string
        timestamp:
          type: string
          format: date-time
        duration_ms:
          type: number
        api_version:
          type: string

    ProblemDetails:
      type: object
      description: |
        RFC 9457 Problem Details for HTTP APIs.
        The `type` field is always a resolvable HTTPS URI from the
        NextPDF error registry at https://nextpdf.dev/errors/.
      required: [type, title, status, detail]
      properties:
        type:
          type: string
          format: uri
          description: Error type URI — resolves to human-readable documentation
          example: "https://nextpdf.dev/errors/bad-request"
        title:
          type: string
          description: Short, human-readable summary of the problem type
          example: "Bad Request"
        status:
          type: integer
          description: HTTP status code
          example: 400
        detail:
          type: string
          description: Human-readable explanation specific to this occurrence
          example: "The 'page_size' field must be a valid ISO page size name."
        instance:
          type: string
          description: URI reference identifying the specific occurrence
          example: "/api/v1/render"
        request_id:
          type: string
          description: Unique request identifier for tracing and support
          example: "019563ea-7e71-7000-8000-abcdef012345"
        errors:
          type: array
          description: Field-level validation errors (present only for 422 responses)
          items:
            $ref: "#/components/schemas/ValidationError"

    ValidationError:
      type: object
      properties:
        field:
          type: string
        code:
          type: string
        message:
          type: string

  headers:
    X-Request-Id:
      description: Unique request identifier for tracing and support
      schema:
        type: string
        example: "019563ea-7e71-7000-8000-abcdef012345"

  responses:
    BadRequest:
      description: Bad request
      headers:
        X-Request-Id:
          $ref: "#/components/headers/X-Request-Id"
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/ProblemDetails"
          example:
            type: "https://nextpdf.dev/errors/bad-request"
            title: "Bad Request"
            status: 400
            detail: "Request body is not valid JSON."
            instance: "/api/v1/render"
            request_id: "019563ea-7e71-7000-8000-abcdef012345"

    Unauthorized:
      description: Missing or invalid API key
      headers:
        X-Request-Id:
          $ref: "#/components/headers/X-Request-Id"
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/ProblemDetails"
          example:
            type: "https://nextpdf.dev/errors/unauthorized"
            title: "Unauthorized"
            status: 401
            detail: "API key is missing or invalid."
            instance: "/api/v1/render"
            request_id: "019563ea-7e71-7000-8000-abcdef012345"

    Forbidden:
      description: Insufficient tier for this operation
      headers:
        X-Request-Id:
          $ref: "#/components/headers/X-Request-Id"
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/ProblemDetails"
          example:
            type: "https://nextpdf.dev/errors/capability-denied"
            title: "Capability Denied"
            status: 403
            detail: "This operation requires Pro tier or higher."
            instance: "/api/v1/sign"
            request_id: "019563ea-7e71-7000-8000-abcdef012345"

    ValidationError:
      description: Request validation failed
      headers:
        X-Request-Id:
          $ref: "#/components/headers/X-Request-Id"
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/ProblemDetails"
          example:
            type: "https://nextpdf.dev/errors/validation-failed"
            title: "Validation Failed"
            status: 422
            detail: "Request body failed schema validation."
            instance: "/api/v1/render"
            request_id: "019563ea-7e71-7000-8000-abcdef012345"
            errors:
              - field: "page_size"
                code: "INVALID_ENUM"
                message: "'B99' is not a recognized page size."

    TooManyRequests:
      description: Rate limit exceeded
      headers:
        X-Request-Id:
          $ref: "#/components/headers/X-Request-Id"
        Retry-After:
          schema:
            type: integer
          description: Seconds until the rate limit resets
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/ProblemDetails"
          example:
            type: "https://nextpdf.dev/errors/client-rate-exceeded"
            title: "Too Many Requests"
            status: 429
            detail: "Rate limit exceeded. Retry after 12 seconds."
            instance: "/api/v1/render"
            request_id: "019563ea-7e71-7000-8000-abcdef012345"

    PayloadTooLarge:
      description: Request body exceeds size limit
      headers:
        X-Request-Id:
          $ref: "#/components/headers/X-Request-Id"
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/ProblemDetails"
          example:
            type: "https://nextpdf.dev/errors/payload-too-large"
            title: "Payload Too Large"
            status: 413
            detail: "Request body exceeds the maximum allowed size."
            instance: "/api/v1/render"
            request_id: "019563ea-7e71-7000-8000-abcdef012345"

    Conflict:
      description: Session is no longer active (rendered or expired)
      headers:
        X-Request-Id:
          $ref: "#/components/headers/X-Request-Id"
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/ProblemDetails"
          example:
            type: "https://nextpdf.dev/errors/idempotency-conflict"
            title: "Conflict"
            status: 409
            detail: "This Idempotency-Key was already used with a different request body."
            instance: "/api/v1/jobs"
            request_id: "019563ea-7e71-7000-8000-abcdef012345"

    NotFound:
      description: Resource not found
      headers:
        X-Request-Id:
          $ref: "#/components/headers/X-Request-Id"
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/ProblemDetails"
          example:
            type: "https://nextpdf.dev/errors/not-found"
            title: "Not Found"
            status: 404
            detail: "The requested resource does not exist."
            instance: "/api/v1/jobs/nonexistent"
            request_id: "019563ea-7e71-7000-8000-abcdef012345"

    RequestTimeout:
      description: Request processing exceeded timeout
      headers:
        X-Request-Id:
          $ref: "#/components/headers/X-Request-Id"
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/ProblemDetails"
          example:
            type: "https://nextpdf.dev/errors/request-timeout"
            title: "Request Timeout"
            status: 504
            detail: "Request processing exceeded the 60-second timeout."
            instance: "/api/v1/render"
            request_id: "019563ea-7e71-7000-8000-abcdef012345"

    InternalError:
      description: Internal server error
      headers:
        X-Request-Id:
          $ref: "#/components/headers/X-Request-Id"
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/ProblemDetails"
          example:
            type: "https://nextpdf.dev/errors/internal"
            title: "Internal Server Error"
            status: 500
            detail: "An unexpected error occurred. Please try again or contact support."
            instance: "/api/v1/render"
            request_id: "019563ea-7e71-7000-8000-abcdef012345"
