{"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"https://uppage.dev/schemas/uppage.v1.json","title":"uppage.yml","description":"UpPage monitoring as code. The repository is the source of truth: uppage apply makes the status page match this file.","type":"object","additionalProperties":false,"required":["version","page"],"properties":{"version":{"const":1},"page":{"type":"object","additionalProperties":false,"required":["slug"],"properties":{"slug":{"type":"string","pattern":"^[a-z0-9][a-z0-9-]{1,61}[a-z0-9]$","description":"The page this file manages."},"name":{"type":"string","maxLength":100},"description":{"type":"string","maxLength":1000},"pending_visibility":{"enum":["hidden","shown"],"description":"Monitors waiting for their first signal: hidden (default) or shown as Not yet monitored."}}},"settings":{"type":"object","additionalProperties":false,"properties":{"drift":{"enum":["reject","record"],"description":"reject (default): dashboard and MCP edits to managed monitors are refused. record: allowed, reported by the next plan, reverted by the next apply."}}},"groups":{"type":"array","maxItems":50,"description":"Public groups that roll up their monitors: the worst child status, internal children included, never named.","items":{"type":"object","additionalProperties":false,"required":["key","name"],"properties":{"key":{"type":"string","pattern":"^[a-z0-9][a-z0-9-]{0,62}$","description":"Stable ID; monitors refer to it with group."},"name":{"type":"string","minLength":1,"maxLength":100},"description":{"type":"string","maxLength":1000},"hide_children":{"type":"boolean","description":"List only the group publicly, not its monitors."}}}},"monitors":{"type":"array","maxItems":200,"items":{"type":"object","required":["key","name","kind"],"additionalProperties":false,"properties":{"key":{"type":"string","pattern":"^[a-z0-9][a-z0-9-]{0,62}$","description":"Stable ID for this monitor. Never change it; renaming is done with name."},"name":{"type":"string","minLength":1,"maxLength":100},"description":{"type":"string","maxLength":1000},"kind":{"enum":["http","mcp","heartbeat","webhook","manual"]},"visibility":{"enum":["public","internal"],"description":"internal: never on the public page, JSON, badge, history or incidents; still alerts you. Pro and White Label."},"group":{"type":"string","pattern":"^[a-z0-9][a-z0-9-]{0,62}$","description":"Key of a group under groups."},"start_active":{"type":"boolean","description":"Default false: the monitor stays pending (no alerts, not public) until its first good signal."},"url":{"type":"string","format":"uri","pattern":"^https?://","description":"http and mcp: the public URL to check."},"method":{"enum":["HEAD","GET","POST"],"description":"http only."},"interval_seconds":{"type":"integer","minimum":60,"maximum":604800,"description":"http/mcp: one of 60, 300, 1800, 3600, 43200, 86400. heartbeat: expected time between runs."},"schedule":{"type":"object","additionalProperties":false,"required":["cron"],"description":"heartbeat: expected runs, instead of interval_seconds. Copy the cron lines from the workflow; an alert fires when a run has not pinged within grace_seconds of its scheduled time.","properties":{"cron":{"type":"array","minItems":1,"maxItems":10,"items":{"type":"string","description":"Five-field cron, e.g. \"0 6 * * 1\"."}},"timezone":{"type":"string","description":"IANA zone for the cron lines (default UTC; GitHub Actions cron is UTC)."}}},"assertions":{"type":"array","maxItems":20,"description":"http: response assertions that must all pass (json_path, body_contains, body_regex, max_latency_ms, header, status). Headers with secrets are set with MCP or the API, never in this file.","items":{"type":"object","required":["type"],"properties":{"type":{"enum":["json_path","body_contains","body_regex","max_latency_ms","header","status"]}}}},"cache_bust":{"type":"boolean","description":"http: add a random query parameter to each check so a CDN cache cannot hide an outage."},"max_duration_seconds":{"type":"integer","minimum":60,"maximum":86400,"description":"heartbeat: fail a run that pinged start but not success/fail within this time."},"payload_rules":{"type":"array","maxItems":10,"description":"heartbeat: \"ran but did nothing\" rules over the run payload.","items":{"type":"object","additionalProperties":false,"required":["path","op","value"],"properties":{"path":{"type":"string","maxLength":200},"op":{"enum":["==","!=","<","<=",">",">="]},"value":{"type":["string","number","boolean","null"]},"consecutive":{"type":"integer","minimum":1,"maximum":10},"status":{"enum":["degraded","partial_outage","major_outage"],"description":"Status shown while this monitor is failing."}}}},"grace_seconds":{"type":"integer","minimum":60,"maximum":86400,"description":"heartbeat: lateness allowed before alerting (GitHub OIDC default 1800)."},"failure_status":{"enum":["degraded","partial_outage","major_outage"],"description":"Status shown while this monitor is failing."},"incident_mode":{"enum":["none","draft","auto"],"description":"heartbeat: open no incident, a draft for approval, or a public incident."},"auth":{"enum":["secret","github_oidc"],"description":"heartbeat: github_oidc lets a GitHub Actions job ping with its identity; no secret exists."},"github":{"type":"object","additionalProperties":false,"required":["repository_id","repository_owner_id","workflow_ref"],"properties":{"repository_id":{"type":"integer","minimum":1,"description":"gh api repos/OWNER/REPO --jq .id"},"repository_owner_id":{"type":"integer","minimum":1,"description":"gh api repos/OWNER/REPO --jq .owner.id"},"repository":{"type":"string","pattern":"^[A-Za-z0-9-]{1,39}/[A-Za-z0-9._-]{1,100}$"},"workflow_ref":{"type":"string","pattern":"^\\.github/workflows/[A-Za-z0-9._*/-]+\\.ya?ml@refs/(heads|tags)/[A-Za-z0-9._*/-]+$"},"events":{"type":"array","minItems":1,"uniqueItems":true,"items":{"enum":["schedule","workflow_dispatch","push","workflow_run","repository_dispatch","release","deployment","merge_group"]},"description":"Default schedule and workflow_dispatch. Pull-request events are never accepted."}}}},"allOf":[{"if":{"properties":{"kind":{"enum":["http","mcp"]}}},"then":{"required":["url"]}},{"if":{"properties":{"auth":{"const":"github_oidc"}},"required":["auth"]},"then":{"required":["github"]}},{"if":{"required":["schedule"]},"then":{"not":{"required":["interval_seconds"]}}}]}}}}