{
  "title": "Substack Public Mention Monitor",
  "type": "object",
  "schemaVersion": 1,
  "properties": {
    "publicationUrls": {
      "title": "Public Substack publications",
      "type": "array",
      "editor": "stringList",
      "items": {
        "type": "string"
      },
      "default": [],
      "prefill": [
        "https://www.lennysnewsletter.com"
      ],
      "description": "Up to five public HTTPS publication homepages or exact /feed URLs. Blank uses Lenny’s Newsletter. Custom domains are supported when the feed identifies Substack. Private feeds, credentials, query strings and redirects are rejected."
    },
    "keywords": {
      "title": "Brands or phrases (optional)",
      "type": "array",
      "editor": "stringList",
      "items": {
        "type": "string"
      },
      "default": [],
      "description": "Case-insensitive literal whole-word or whole-phrase OR matching in titles and public feed text. Empty keywords, aliases and domains return recent articles without a mention filter."
    },
    "outboundDomains": {
      "title": "Linked domains (optional)",
      "type": "array",
      "editor": "stringList",
      "items": {
        "type": "string"
      },
      "default": [],
      "description": "Bare public domain names, for example openai.com. Matches that host and its subdomains in links published in the public feed. Matching a link does not establish a sponsorship."
    },
    "maxResults": {
      "title": "Maximum saved articles",
      "type": "integer",
      "default": 5,
      "minimum": 1,
      "maximum": 100,
      "description": "Global cap on confirmed saved article observations. Mention arrays are included in each article row. Does not expand the publisher’s recent feed window."
    },
    "aliases": {
      "title": "Brand aliases",
      "type": "object",
      "editor": "json",
      "default": {},
      "description": "Optional map from brand label to up to five literal aliases, for example {\"OpenAI\":[\"ChatGPT\"]}. Each brand label also matches itself. Up to ten labels; all configured filters combine with OR.",
      "sectionCaption": "Advanced monitoring settings",
      "sectionDescription": "Saved comparisons, aliases and time limits. Leave these unchanged for a first run."
    },
    "comparePrevious": {
      "title": "Compare with saved observations",
      "type": "boolean",
      "default": false,
      "description": "First comparison run establishes a baseline from confirmed saved rows. Reuse the same publications, filters and snapshot name. One writer per snapshot."
    },
    "onlyChanges": {
      "title": "Save only new or changed articles",
      "type": "boolean",
      "default": false,
      "description": "Enables comparison. Unchanged articles produce no paid data row. Missing or rolled-off articles are never labeled deleted."
    },
    "snapshotName": {
      "title": "Snapshot name",
      "type": "string",
      "editor": "textfield",
      "default": "default",
      "description": "1–80 letters, digits, underscores or hyphens. Named saved history is isolated by publication/filter scope. Reuse the same scope for comparisons."
    },
    "maxRunSeconds": {
      "title": "Run time budget (seconds)",
      "type": "integer",
      "default": 60,
      "minimum": 30,
      "maximum": 180,
      "description": "All requests share the remaining deadline. At most two requests per publication; no redirects, retries, browser or proxy."
    }
  }
}
