{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://artifacts.apis.io/difficat-com/json-schema/difficat-com-snapshot-compare-request-schema.json",
  "title": "SnapshotCompareRequest",
  "x-generated": "2026-11-05",
  "x-method": "derived",
  "x-generator": "derive-json-schema.py",
  "x-source": "openapi/difficat-com-openapi.yml#/components/schemas/SnapshotCompareRequest",
  "type": "object",
  "required": [
    "targetUrl"
  ],
  "properties": {
    "targetUrl": {
      "type": "string",
      "format": "uri",
      "maxLength": 2048,
      "description": "The live site, Site B."
    },
    "urls": {
      "type": "array",
      "minItems": 1,
      "maxItems": 50000,
      "description": "Pairs to compare: each source a page of the snapshot (its path, or its full URL on the snapshot’s origin), each target on targetUrl. Without it every stored page is paired by path.",
      "items": {
        "type": "object",
        "required": [
          "source",
          "target"
        ],
        "properties": {
          "source": {
            "type": "string",
            "description": "Page URL on Site A."
          },
          "target": {
            "type": "string",
            "description": "The same page's URL on Site B."
          }
        }
      }
    },
    "threshold": {
      "type": "number",
      "minimum": 0,
      "maximum": 1,
      "description": "Visual diff tolerance as a fraction from 0 (exact) to 1: 0.05 means 5%. The web app takes a percentage; the API does not. When omitted, the snapshot’s stored tolerance applies (snapshot A’s for two snapshots), and the default tolerance when it stored none."
    },
    "maxPriceUsd": {
      "type": "number",
      "exclusiveMinimum": 0,
      "description": "Optional spend cap in USD. A job priced above it is refused before any challenge, so an auto-paying client is never asked for more."
    },
    "dryRun": {
      "type": "boolean",
      "description": "Quote only: runs every check and the pricing, then answers the page count and exact amount. Nothing is charged, the free allowance is not used and no job starts; the same body without dryRun is priced from the retained result."
    },
    "callbackUrl": {
      "type": "string",
      "format": "uri",
      "maxLength": 2048,
      "description": "Optional https URL (port 443) that receives one JSON POST when the job ends (jobId, status, summary, links; never the jobToken), a nudge to read the job. Public hosts only, no redirects followed, retried twice; delivery never affects the job."
    }
  }
}