{"openapi":"3.1.0","info":{"title":"Ned Watch","version":"1.7"},"paths":{"/health":{"get":{"summary":"Health","operationId":"health_health_get","parameters":[{"name":"shallow","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Shallow"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/stats":{"get":{"summary":"Public Stats","description":"Public, aggregate, no per-agent data (site v1.4): checks run in the last 24 h across both stations (summed: each\nstation checks independently) and active watches (replicated, so the local count). Cached 60 s; the peer is asked\nwith shallow=1 so the two stations never ask each other in a loop.","operationId":"public_stats_v1_stats_get","parameters":[{"name":"shallow","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Shallow"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/pricing":{"get":{"summary":"Get Pricing","operationId":"get_pricing_v1_pricing_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/.well-known/agent-card.json":{"get":{"summary":"Agent Card","operationId":"agent_card__well_known_agent_card_json_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/v1/watches":{"post":{"summary":"Create Watch","description":"Register a watch. No key yet? Call without one: the 201 carries your agent_key (shown once). A signed test callback\nreaches callback_url within a minute. Your callback should outlive your agent. notify: up to 3 Discord/Slack/Teams\nwebhook URLs told on fire, clear and change (v1.13).","operationId":"create_watch_v1_watches_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WatchIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"summary":"List Watches","operationId":"list_watches_v1_watches_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/":{"get":{"summary":"Root","description":"What this is and how to start (for an agent that knocked on the base URL).","operationId":"root__get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/v1/watches/{wid}":{"get":{"summary":"Get Watch","operationId":"get_watch_v1_watches__wid__get","parameters":[{"name":"wid","in":"path","required":true,"schema":{"type":"string","title":"Wid"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"summary":"Cancel Watch","operationId":"cancel_watch_v1_watches__wid__delete","parameters":[{"name":"wid","in":"path","required":true,"schema":{"type":"string","title":"Wid"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/checkin/{wid}":{"post":{"summary":"Checkin","description":"Deadman check-in. Proves possession of the watch's signing secret (audit E53):\neither  Authorization: Bearer <signing_secret>  or  X-Ned-Checkin: hex(HMAC-SHA256(signing_secret, watch_id)).","operationId":"checkin_v1_checkin__wid__post","parameters":[{"name":"wid","in":"path","required":true,"schema":{"type":"string","title":"Wid"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-ned-checkin","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Ned-Checkin"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/watches/{wid}/start":{"post":{"summary":"Run Start","description":"Overrun: a run begins. If it isn't finished within max_runtime_s, Ned fires once. A start while a run is open closes\nthat run as superseded (logged, no callback).","operationId":"run_start_v1_watches__wid__start_post","parameters":[{"name":"wid","in":"path","required":true,"schema":{"type":"string","title":"Wid"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-ned-timestamp","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Ned-Timestamp"}},{"name":"x-ned-signature","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Ned-Signature"}}],"requestBody":{"content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/RunIn"},{"type":"null"}],"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/watches/{wid}/finish":{"post":{"summary":"Run Finish","description":"Overrun: the run ended. If Ned already fired for it, your callback gets a clear with late:true and the runtime.","operationId":"run_finish_v1_watches__wid__finish_post","parameters":[{"name":"wid","in":"path","required":true,"schema":{"type":"string","title":"Wid"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"x-ned-timestamp","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Ned-Timestamp"}},{"name":"x-ned-signature","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Ned-Signature"}}],"requestBody":{"content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/RunIn"},{"type":"null"}],"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/balance":{"get":{"summary":"Balance","operationId":"balance_v1_balance_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/topup":{"post":{"summary":"Topup Pointer","operationId":"topup_pointer_v1_topup_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/v1/catalog":{"get":{"summary":"Catalog Public","description":"What Ned can recognise from outside. Public entries only; no examples, conditions or signatures.","operationId":"catalog_public_v1_catalog_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/v1/topup/{usd}":{"post":{"summary":"Topup Paid","description":"Reached only after the x402 middleware verified a payment (or with no keys configured -> 503).\nThe credit itself happens in the after-settle hook; this body states the balance that settlement will produce.","operationId":"topup_paid_v1_topup__usd__post","parameters":[{"name":"usd","in":"path","required":true,"schema":{"type":"integer","title":"Usd"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/status":{"get":{"summary":"Public Status","description":"Ned Watch's own uptime, as this node sees it: every minute each station checks both stations' public /health (as\ncustomer zero, through the public internet). 90 days by day, 24 hours by hour, latency, and every fire and clear.\nRead both nodes for the full picture; ned.watch/status does.","operationId":"public_status_v1_status_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/v1/public":{"get":{"summary":"Public Index","description":"Every watch whose owner made it public: the state word, since when, how long observed. One node's view; ned.watch/w/ merges both.","operationId":"public_index_v1_public_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/v1/up/{host}":{"get":{"summary":"Up Lookup","description":"v1.15 'ask before you call': the state of everything Ned watches publicly on a host (and its subdomains), no key.\nstate is the worst of them: down, degraded, reachable, up, or unknown when nothing on The Watch matches (then\nwatch_this says how to add it). One station's view, cached 20 s; ask the other station too if it matters.","operationId":"up_lookup_v1_up__host__get","parameters":[{"name":"host","in":"path","required":true,"schema":{"type":"string","title":"Host"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/diagnose":{"post":{"summary":"Diagnose Now","description":"v1.19 why is this host failing right now: one paid call (x402, no account). The same evidence pass and catalog that\nevery confirmed fire gets, run on demand from this station. Anything that stops it answers 4xx/5xx, and x402 never\nsettles an error, so a diagnosis that didn't happen is never charged.\nv1.19.1 (security review #1): runs on its own small pool, never the API's shared threads; when the pool is full it\nanswers 429 before doing any work; one payment authorization buys one diagnosis, not one per parallel replay.","operationId":"diagnose_now_v1_diagnose_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiagnoseIn"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/public/changes":{"get":{"summary":"Public Changes","description":"v1.14: every change Ned saw on a public MCP watch: tool lists, server versions and names, protocol, auth posture.\nNewest first, up to 200, this station's view; ned.watch/w/changes/ merges both. Facts only: names, never descriptions.","operationId":"public_changes_v1_public_changes_get","parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":30,"title":"Days"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/public/{slug}":{"get":{"summary":"Public Watch","description":"One public watch: identity, three-valued verdicts, 90 days by day, 24 hours by hour, latency, every fire, clear and change.\nNever the callback, the key, the agent, or (for content) anything the page said.","operationId":"public_watch_v1_public__slug__get","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}},"components":{"schemas":{"DiagnoseIn":{"properties":{"target":{"type":"string","maxLength":300,"minLength":3,"title":"Target","description":"a host (api.example.com) or an https URL"},"type":{"type":"string","pattern":"^(http|mcp)$","title":"Type","default":"http"}},"type":"object","required":["target"],"title":"DiagnoseIn"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"RunIn":{"properties":{"run_id":{"anyOf":[{"type":"string","pattern":"^[A-Za-z0-9._:-]{1,64}$"},{"type":"null"}],"title":"Run Id","description":"optional: your own id for the run (1-64 of A-Z a-z 0-9 . _ : -); Ned makes one if you leave it out"},"status":{"anyOf":[{"type":"string","pattern":"^(ok|failed)$"},{"type":"null"}],"title":"Status","description":"finish only (v1.9): 'failed' says the run crashed; Ned fires at once and clears on the next run that finishes ok"},"error":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Error","description":"finish only, with status failed: a short reason, sent in the fire callback"}},"type":"object","title":"RunIn"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"},"input":{"title":"Input"},"ctx":{"type":"object","title":"Context"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"},"WatchIn":{"properties":{"type":{"type":"string","pattern":"^(http|tls|deadman|overrun|content|mcp)$","title":"Type","description":"http | tls | deadman | overrun (a run still going past max_runtime_s) | content (200, but the wrong content) | mcp (a remote MCP server: initialize + tools/list; fires when the handshake fails, reports when the tool list changes)"},"target":{"anyOf":[{"type":"string","maxLength":2083,"minLength":1,"format":"uri"},{"type":"string"},{"type":"null"}],"title":"Target","description":"URL for http/content, host or URL for tls; omit for deadman and overrun"},"interval_s":{"type":"integer","maximum":86400.0,"minimum":30.0,"title":"Interval S","description":"Seconds between checks (deadman: the check-in deadline). Not used by overrun.","default":300},"callback_url":{"type":"string","maxLength":2083,"minLength":1,"format":"uri","title":"Callback Url"},"condition":{"additionalProperties":true,"type":"object","title":"Condition","description":"http: max_ms, expect_status, timeout_s. tls: warn_days. deadman: grace_s. content: timeout_s.","default":{}},"expect":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Expect","description":"content only, 1 to 5 conditions: status (int), contains / not_contains (string or list), json_path (dotted, e.g. data.items.0.state) with equals (a JSON scalar) or exists (bool). No regex."},"max_runtime_s":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Max Runtime S","description":"overrun only: 60 to 86400. A run started and not finished within this fires once. Give each job its own watch with condition.label (the same registration twice returns the same watch)."},"meta":{"additionalProperties":true,"type":"object","title":"Meta","default":{}},"public":{"type":"boolean","title":"Public","description":"v1.11: give this watch a public page at ned.watch/w/<slug> (state, history, badge, feed). Only the target, the type, timing and pass/fail history are shown; never your callback or key.","default":false},"slug":{"anyOf":[{"type":"string","pattern":"^[a-z0-9][a-z0-9-]{2,58}[a-z0-9]$"},{"type":"null"}],"title":"Slug","description":"v1.11, with public: the page's slug (lowercase, digits, dashes); Ned makes one from the host if you leave it out"},"notify":{"anyOf":[{"items":{"type":"string","maxLength":2083,"minLength":1,"format":"uri"},"type":"array","maxItems":3},{"type":"null"}],"title":"Notify","description":"v1.13: up to 3 human-facing incoming-webhook URLs (Discord, Slack, Teams) told in one plain line on fire, clear, changed and paused, after the signed callback. Your callback should outlive your agent: if the agent that registered this watch dies, these still reach a person. Shown back as kinds only, never URLs."}},"type":"object","required":["type","callback_url"],"title":"WatchIn"}}}}