{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://artifacts.apis.io/difficat-com/json-schema/difficat-com-stored-compare-request-schema.json",
  "title": "StoredCompareRequest",
  "description": "Give pairs, or snapshotId with otherSnapshotId.",
  "x-generated": "2026-11-05",
  "x-method": "derived",
  "x-generator": "derive-json-schema.py",
  "x-source": "openapi/difficat-com-openapi.yml#/components/schemas/StoredCompareRequest",
  "type": "object",
  "properties": {
    "pairs": {
      "type": "array",
      "minItems": 1,
      "maxItems": 200,
      "description": "The snapshots to compare, pair by pair. Pairs may span sets and reuse one snapshot.",
      "items": {
        "type": "object",
        "required": [
          "a",
          "b"
        ],
        "properties": {
          "a": {
            "$ref": "#/$defs/SnapshotRef"
          },
          "b": {
            "$ref": "#/$defs/SnapshotRef"
          }
        }
      }
    },
    "snapshotId": {
      "type": "string",
      "description": "Site A, a set the account owns or may read."
    },
    "otherSnapshotId": {
      "type": "string",
      "description": "Site B, a set whose pages are paired with Site A’s by path."
    },
    "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."
    }
  },
  "$defs": {
    "SnapshotRef": {
      "type": "object",
      "required": [
        "snapshotId",
        "n"
      ],
      "properties": {
        "snapshotId": {
          "type": "string",
          "description": "The snapshot set id, snap-<uuid>."
        },
        "n": {
          "type": "integer",
          "description": "A page number from the set’s page list."
        }
      }
    }
  }
}