{
  "title": "Judge.me reviews - choose products and review limits",
  "type": "object",
  "schemaVersion": 1,
  "properties": {
    "productUrls": {
      "title": "Product page URLs",
      "description": "Up to 30 public product pages exposing Judge.me SHOP_DOMAIN, PUBLIC_TOKEN and a review widget product ID. Empty input uses Owala FreeSip. Browser-only widgets are unsupported.",
      "type": "array",
      "editor": "stringList",
      "items": {
        "type": "string"
      },
      "maxItems": 30,
      "sectionCaption": "1 · Public product pages",
      "sectionDescription": "Supply pages with publicly available Judge.me review widgets."
    },
    "maxReviewsPerProduct": {
      "title": "Maximum reviews per product",
      "description": "Maximum distinct reviews inspected per product, including unchanged reviews. This is a visible widget window, not a guarantee of full historical coverage.",
      "type": "integer",
      "minimum": 1,
      "maximum": 1000,
      "default": 10,
      "sectionCaption": "2 · Review window and flags",
      "sectionDescription": "Bound collection and choose the low-rating flag threshold; the flag is not a sentiment judgment."
    },
    "maxPagesPerProduct": {
      "title": "Review pages per product",
      "description": "Maximum documented public widget pages requested. Reviews outside the selected window cannot have edits or replies detected.",
      "type": "integer",
      "minimum": 1,
      "maximum": 100,
      "default": 10
    },
    "lowRatingThreshold": {
      "title": "Flag ratings at or below",
      "description": "Ratings at or below this value are flagged isLowRating. needsMerchantReply additionally requires no visible merchant reply. This flags rows; it does not filter them.",
      "type": "integer",
      "minimum": 1,
      "maximum": 5,
      "default": 3
    },
    "maxResults": {
      "title": "Maximum saved rows",
      "description": "Stop after this many saved rows across all sources. This is a selected sample limit, not proof of complete source coverage.",
      "type": "integer",
      "minimum": 1,
      "maximum": 10000,
      "default": 100
    },
    "comparePrevious": {
      "title": "Compare with saved observations",
      "description": "Compare against this snapshot name. First observation emits baseline rows; only successfully saved rows advance history. Run the same snapshot name sequentially.",
      "type": "boolean",
      "default": false,
      "sectionCaption": "3 · Saved comparisons",
      "sectionDescription": "The first comparison establishes history. Reuse the same scope for later observations and avoid concurrent writers."
    },
    "onlyChanges": {
      "title": "Save only new or changed observations",
      "description": "Also enables comparison. First run emits baseline rows. Later unchanged observations produce no rows; this can legitimately return an empty dataset.",
      "type": "boolean",
      "default": false
    },
    "snapshotName": {
      "title": "Saved comparison name",
      "description": "Named persistent history, isolated by source. Use 1–40 letters, digits, hyphens or underscores. Do not run the same name concurrently.",
      "type": "string",
      "editor": "textfield",
      "default": "default",
      "pattern": "^[a-zA-Z0-9][a-zA-Z0-9_-]{0,39}$"
    },
    "maxRunSeconds": {
      "title": "Run time budget (seconds)",
      "description": "Own deadline, also shortened to 25 seconds before the platform timeout. Requests and retry delays stay within the remaining time.",
      "type": "integer",
      "minimum": 30,
      "maximum": 3600,
      "default": 240,
      "sectionCaption": "4 · Advanced run settings",
      "sectionDescription": "Adjust the time budget only when a larger collection needs it."
    }
  },
  "description": "Supply public product pages using Judge.me, choose a review window and optionally compare saved reviews. Low-rating flags describe published ratings; the Actor does not infer sentiment or reviewer identity."
}
