openapi: 3.0.3
info:
  title: SnapForge API
  description: High-performance Screenshot, PDF & OG Image generation API
  version: 1.0.0
  contact:
    url: https://github.com/adrian-vega-research/snapforge
  license:
    name: MIT
    url: https://opensource.org/licenses/MIT

servers:
  - url: https://snapforge.click
    description: Live demo server

security:
  - ApiKeyHeader: []
  - ApiKeyQuery: []

paths:
  /v1/screenshot:
    post:
      summary: Capture screenshot
      description: Capture a screenshot of any URL or HTML content
      tags: [Screenshot]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [url]
              properties:
                url:
                  type: string
                  example: https://example.com
                width:
                  type: integer
                  default: 1280
                height:
                  type: integer
                  default: 800
                fullPage:
                  type: boolean
                  default: false
                format:
                  type: string
                  enum: [png, jpeg, webp]
                  default: png
                quality:
                  type: integer
                  minimum: 1
                  maximum: 100
                  default: 80
                deviceScaleFactor:
                  type: number
                  default: 1
                delay:
                  type: integer
                  description: Wait ms before capture
                  default: 0
                selector:
                  type: string
                  description: CSS selector to capture specific element
                darkMode:
                  type: boolean
                  default: false
                blockAds:
                  type: boolean
                  default: false
      responses:
        '200':
          description: Screenshot image
          content:
            image/png: {}
            image/jpeg: {}
            image/webp: {}
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/RateLimited'
    get:
      summary: Capture screenshot (URL params)
      description: Capture screenshot using URL query parameters (useful for embeds)
      tags: [Screenshot]
      parameters:
        - name: url
          in: query
          required: true
          schema:
            type: string
        - name: api_key
          in: query
          required: true
          schema:
            type: string
        - name: width
          in: query
          schema:
            type: integer
        - name: height
          in: query
          schema:
            type: integer
        - name: fullPage
          in: query
          schema:
            type: boolean
        - name: format
          in: query
          schema:
            type: string
            enum: [png, jpeg, webp]
      responses:
        '200':
          description: Screenshot image
          content:
            image/png: {}

  /v1/pdf:
    post:
      summary: Generate PDF
      description: Generate PDF from URL or HTML content
      tags: [PDF]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                url:
                  type: string
                html:
                  type: string
                format:
                  type: string
                  enum: [A4, Letter, Legal, Tabloid]
                  default: A4
                landscape:
                  type: boolean
                  default: false
                printBackground:
                  type: boolean
                  default: true
                margin:
                  type: object
                  properties:
                    top: { type: string }
                    bottom: { type: string }
                    left: { type: string }
                    right: { type: string }
                headerTemplate:
                  type: string
                footerTemplate:
                  type: string
                displayHeaderFooter:
                  type: boolean
                  default: false
      responses:
        '200':
          description: PDF document
          content:
            application/pdf: {}
        '401':
          $ref: '#/components/responses/Unauthorized'

  /v1/og-image:
    post:
      summary: Generate OG Image
      description: Generate Open Graph images from templates or custom HTML
      tags: [OG Image]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                title:
                  type: string
                  example: My Blog Post
                subtitle:
                  type: string
                template:
                  type: string
                  enum: [basic, gradient, dark, minimal, blog]
                  default: basic
                customHtml:
                  type: string
                  description: Custom HTML template (rendered with JS disabled by default; not cached)
                logo:
                  type: string
                  description: HTTPS URL to a logo image (SSRF-checked, inlined; dropped on failure)
                bgColor:
                  type: string
                  description: Background color (sanitised; #hex/rgb()/hsl()/named only)
                bgGradient:
                  type: string
                  description: CSS linear/radial gradient (sanitised grammar)
                textColor:
                  type: string
                fontSize:
                  type: integer
                  default: 48
                format:
                  type: string
                  enum: [png, jpeg, webp, avif]
                  default: png
                quality:
                  type: integer
                  minimum: 1
                  maximum: 100
                  default: 90
                width:
                  type: integer
                  default: 1200
                height:
                  type: integer
                  default: 630
                dsf:
                  type: number
                  description: Device scale factor (1-2). Subject to a pixel budget clamp.
                  default: 2
      responses:
        '200':
          description: OG image. Templated requests are cacheable.
          headers:
            Cache-Control:
              description: 'public, max-age, immutable for templated images; no-store for customHtml'
              schema:
                type: string
            ETag:
              description: Strong content-hash ETag (templated images); supports If-None-Match revalidation
              schema:
                type: string
          content:
            image/png: {}
            image/jpeg: {}
            image/webp: {}
            image/avif: {}
        '304':
          description: Not Modified (If-None-Match matched the ETag)
        '401':
          $ref: '#/components/responses/Unauthorized'
        '413':
          description: Payload too large (input field exceeded a size cap)

  /health:
    get:
      summary: Health check
      tags: [System]
      security: []
      responses:
        '200':
          description: Service is healthy
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: ok
                  timestamp:
                    type: string

components:
  securitySchemes:
    ApiKeyHeader:
      type: apiKey
      in: header
      name: X-API-Key
    ApiKeyQuery:
      type: apiKey
      in: query
      name: api_key

  responses:
    Unauthorized:
      description: Invalid or missing API key
      content:
        application/json:
          schema:
            type: object
            properties:
              error:
                type: object
                properties:
                  message: { type: string }
                  code: { type: string }
    RateLimited:
      description: Daily request limit exceeded
      content:
        application/json:
          schema:
            type: object
            properties:
              error:
                type: object
                properties:
                  message: { type: string }
                  code: { type: string }
