{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://artifacts.apis.io/snaplab-dev/json-schema/snaplab-dev-render-options-schema.json",
  "title": "RenderOptions",
  "description": "Exactly one of `url` or `html`/`template` must be given.",
  "x-generated": "2026-10-31",
  "x-method": "derived",
  "x-generator": "derive-json-schema.py",
  "x-source": "openapi/snaplab-dev-openapi.json#/components/schemas/RenderOptions",
  "type": "object",
  "properties": {
    "url": {
      "type": "string",
      "maxLength": 2048,
      "format": "uri",
      "description": "Absolute http(s) URL to capture. Private, loopback, link-local and metadata addresses are refused."
    },
    "html": {
      "type": "string",
      "maxLength": 2000000,
      "description": "Raw HTML rendered instead of navigating. Subresources are still SSRF-filtered."
    },
    "template": {
      "type": "string",
      "maxLength": 2000000,
      "description": "HTML with `{{ path.to.value }}` placeholders, substituted from `data` and HTML-escaped."
    },
    "data": {
      "type": "object",
      "additionalProperties": true,
      "description": "Values for `template` (or for `html`, when no template is given)."
    },
    "css": {
      "type": "string",
      "maxLength": 500000,
      "description": "Stylesheet injected into `<head>` before load. html/template mode only."
    },
    "googleFonts": {
      "type": "string",
      "maxLength": 300,
      "description": "Comma-separated Google Fonts families, e.g. `Inter,Playfair Display` (max 6)."
    },
    "format": {
      "type": "string",
      "enum": [
        "png",
        "jpeg",
        "webp",
        "avif",
        "pdf"
      ],
      "default": "png"
    },
    "viewport": {
      "type": "string",
      "enum": [
        "desktop",
        "laptop",
        "tablet",
        "mobile",
        "wide"
      ],
      "default": "desktop",
      "description": "Preset size. `width`/`height`/`deviceScaleFactor` override individual axes."
    },
    "width": {
      "type": "integer",
      "minimum": 120,
      "maximum": 3840
    },
    "height": {
      "type": "integer",
      "minimum": 120,
      "maximum": 4320
    },
    "deviceScaleFactor": {
      "type": "number",
      "minimum": 0.5,
      "maximum": 3,
      "description": "Pixel ratio. 2 = retina."
    },
    "mobile": {
      "type": "boolean",
      "description": "Sugar for `viewport: \"mobile\"` (390x844 @3x, touch emulation)."
    },
    "fullPage": {
      "type": "boolean",
      "default": true,
      "description": "Capture the whole scrollable document. Ignored when `selector` is set."
    },
    "selector": {
      "type": "string",
      "maxLength": 512,
      "description": "Capture just this element. PDFs stay vector/selectable."
    },
    "quality": {
      "type": "integer",
      "minimum": 1,
      "maximum": 100,
      "description": "jpeg/webp/avif only."
    },
    "waitUntil": {
      "type": "string",
      "enum": [
        "auto",
        "load",
        "domcontentloaded",
        "networkidle"
      ],
      "default": "auto",
      "description": "\"auto\" waits for load, then for the network to be quiet for 150ms, capped at 400ms. \"networkidle\" is the full Playwright wait and costs ~0.5s more."
    },
    "waitForSelector": {
      "type": "string",
      "maxLength": 512,
      "description": "Wait (up to 10s) for this selector before capturing."
    },
    "delayMs": {
      "type": "integer",
      "minimum": 0,
      "maximum": 30000
    },
    "scrollY": {
      "type": "integer",
      "minimum": 0,
      "maximum": 50000,
      "description": "Scroll down this many pixels first (triggers lazy loading)."
    },
    "hideSelectors": {
      "type": "array",
      "maxItems": 20,
      "items": {
        "type": "string",
        "maxLength": 512
      },
      "description": "Elements to hide before capturing. Comma-separated in a query string."
    },
    "darkMode": {
      "oneOf": [
        {
          "type": "boolean"
        },
        {
          "type": "string",
          "enum": [
            "prefers",
            "force"
          ]
        }
      ],
      "description": "`true` (= `\"force\"`) emulates `prefers-color-scheme: dark` AND turns on Chromium's auto dark mode, so a page with no dark theme comes back dark; a page that has one is left alone. `\"prefers\"` is the media query only."
    },
    "dismissBanners": {
      "type": "boolean",
      "default": true,
      "description": "Hide known cookie/consent/chat banners with a curated stylesheet."
    },
    "blockAds": {
      "type": "boolean",
      "default": false,
      "description": "Abort requests to known ad, analytics and session-replay hosts."
    },
    "customCss": {
      "type": "string",
      "maxLength": 20000,
      "description": "CSS injected after load."
    },
    "customJs": {
      "type": "string",
      "maxLength": 20000,
      "description": "JavaScript evaluated after load."
    },
    "cookies": {
      "type": "array",
      "maxItems": 40,
      "items": {
        "type": "object",
        "required": [
          "name",
          "value"
        ],
        "properties": {
          "name": {
            "type": "string",
            "maxLength": 256
          },
          "value": {
            "type": "string",
            "maxLength": 4096
          },
          "domain": {
            "type": "string",
            "maxLength": 256,
            "description": "Defaults to the target host."
          },
          "path": {
            "type": "string",
            "maxLength": 256,
            "default": "/"
          },
          "secure": {
            "type": "boolean"
          },
          "httpOnly": {
            "type": "boolean"
          },
          "sameSite": {
            "type": "string",
            "enum": [
              "Strict",
              "Lax",
              "None"
            ]
          },
          "expires": {
            "type": "integer",
            "description": "Unix seconds. Omit for a session cookie."
          }
        },
        "additionalProperties": false
      },
      "description": "Per-request cookies. Renders with cookies are never cached or shared."
    },
    "headers": {
      "type": "object",
      "additionalProperties": {
        "type": "string",
        "maxLength": 8192
      },
      "description": "Extra request headers."
    },
    "basicAuth": {
      "type": "object",
      "required": [
        "username",
        "password"
      ],
      "properties": {
        "username": {
          "type": "string"
        },
        "password": {
          "type": "string"
        }
      },
      "additionalProperties": false
    },
    "pdf": {
      "type": "object",
      "description": "PDF-only geometry. Ignored unless `format` is `pdf`.",
      "properties": {
        "format": {
          "type": "string",
          "enum": [
            "Letter",
            "Legal",
            "Tabloid",
            "Ledger",
            "A0",
            "A1",
            "A2",
            "A3",
            "A4",
            "A5",
            "A6"
          ],
          "default": "A4"
        },
        "landscape": {
          "type": "boolean",
          "default": false
        },
        "printBackground": {
          "type": "boolean",
          "default": true
        },
        "scale": {
          "type": "number",
          "minimum": 0.1,
          "maximum": 2,
          "default": 1
        },
        "preferCSSPageSize": {
          "type": "boolean",
          "default": false,
          "description": "Honour the page's own `@page size` rule instead of `format`."
        },
        "margin": {
          "type": "object",
          "description": "CSS lengths (`0.4in`, `1cm`, `12mm`…).",
          "properties": {
            "top": {
              "type": "string",
              "default": "0.4in"
            },
            "right": {
              "type": "string",
              "default": "0.4in"
            },
            "bottom": {
              "type": "string",
              "default": "0.4in"
            },
            "left": {
              "type": "string",
              "default": "0.4in"
            }
          },
          "additionalProperties": false
        },
        "headerTemplate": {
          "type": "string",
          "maxLength": 20000,
          "description": "HTML drawn at the top of every page, inside the top margin. It inherits none of the document's CSS, so set `font-size` explicitly and add your own side padding. Any element carrying the classes `pageNumber`, `totalPages`, `date`, `title` or `url` is filled in by Chromium."
        },
        "footerTemplate": {
          "type": "string",
          "maxLength": 20000,
          "description": "Same as `headerTemplate`, at the bottom of every page. Also accepts a built-in preset name: `@pageNumbers` (centred “Page N of M”) or `@pageNumbersUrl` (page numbers left, document URL right)."
        },
        "displayHeaderFooter": {
          "type": "boolean",
          "description": "Draw the running header and footer. Defaults to `true` as soon as either template is set; pass `false` to suppress both."
        },
        "pageRanges": {
          "type": "string",
          "maxLength": 200,
          "description": "Sheets to keep, in print-dialog syntax — `1-3`, `1,4,7-9`. Default: every page."
        },
        "media": {
          "type": "string",
          "enum": [
            "screen",
            "print"
          ],
          "default": "screen",
          "description": "Which stylesheet the paper is printed under. `print` honours the document's own print CSS (`@media print` rules, page breaks, hidden nav); `screen` keeps the on-screen look. Screenshots are always screen media."
        },
        "tagged": {
          "type": "boolean",
          "default": false,
          "description": "Emit a tagged (accessible) PDF with structure tags for screen readers."
        },
        "outline": {
          "type": "boolean",
          "default": false,
          "description": "Embed a document outline (PDF bookmarks) built from the page's headings."
        },
        "width": {
          "type": "string",
          "maxLength": 40,
          "description": "Exact paper width as a CSS length. Replaces `format`; ignored unless `height` is set too."
        },
        "height": {
          "type": "string",
          "maxLength": 40,
          "description": "Exact paper height as a CSS length."
        },
        "omitBackground": {
          "type": "boolean",
          "default": false,
          "description": "Drop background colours and images. The same knob as `printBackground: false`, and it wins over it."
        }
      },
      "additionalProperties": false
    },
    "filename": {
      "type": "string",
      "maxLength": 120,
      "description": "Suggested download name, sanitised. Sets `content-disposition: inline; filename=\"…\"` on the returned bytes and on the hosted `/i/c/{id}` file (`?download=1` there makes it an `attachment`). Include the extension you want — it is used verbatim."
    },
    "cache": {
      "type": "boolean",
      "default": true,
      "description": "Set false to force a fresh render."
    },
    "responseType": {
      "type": "string",
      "enum": [
        "binary",
        "json"
      ],
      "default": "binary",
      "description": "`binary` streams the bytes; `json` stores the render and answers with a hosted URL."
    }
  },
  "additionalProperties": false
}