Skip to content

Create your first watch

A watch is four decisions. Everything else has a sensible default.

id: product-restock # a stable slug — this is its identity
name: Studio monitor restock # what you will see in the alert
sources: # 1. WHERE to look
- type: page
value: "https://example.com/products/monitor"
schedule: "0 */6 * * *" # 2. HOW OFTEN (cron, UTC)
condition: > # 3. WHAT COUNTS
The product page shows the monitor as in stock and orderable — not
"notify me when available", and not a pre-order.
channels: [telegram] # 4. WHERE to send it
threshold: 0.6
enabled: true

Three source types, each good at something different:

Type Use it for Notes
rss Blogs, release feeds, anything with a feed Cheapest and most reliable
page A specific URL you already know Extraction is best-effort on JS-heavy pages
search “Anything, anywhere, about X” Needs a Tavily key

Two sources are usually better than one: an official page for authority, plus a search for coverage. See Choosing sources.

Sources also accept an optional hints object that tunes collection — pinning a search to official domains, narrowing a time window, or enabling a heavier extraction tier for a JavaScript-rendered page:

sources:
- type: search
value: "2026 examination result declared"
hints:
officialDomains: ["example.gov"]
recencyDays: 30
topic: news

Every hint is documented in Sources and Watch schema.

schedule is a cron expression in UTC. "0 */6 * * *" is every six hours; "30 3 * * *" is 03:30 UTC daily. Your real check frequency is also limited by how often the instance itself runs — see Scheduling.

The condition is the heart of the watch. A language model reads it, so write it the way you would brief a careful assistant: say what should trigger, and say what should not.

condition: >
Alert when the official result for the 2026 examination is declared or the
scorecard is published. Do NOT alert on exam-date announcements, admit cards,
answer keys, coaching articles, or speculation about when the result might come.

Watcher will not fire unless it can quote a sentence from the collected page that proves the condition — so a condition it cannot verify from text will never trigger. Writing a condition collects the patterns that work and the ones that do not.

channels: [telegram] # the configured default chat
channels: [{ type: telegram, chatId: "-1001234567890" }] # a specific chat
channels: [telegram, push] # Telegram and the Android app
threshold: 0.6 # confirmed alert at or above this confidence
weakThreshold: 0.4 # 0.4–0.6 arrives labelled "unconfirmed / possible update"
minSources: 2 # optional: require two sources to agree for a confirmed alert

Setting weakThreshold gives you a low-priority tier instead of silence for borderline evidence. Omit it and anything below threshold is dropped. See Confidence and alert tiers.

  1. Put the file in watches/ and run pnpm watches:import, or create it in the admin panel, or POST /watches over the API.
  2. Test it immediately with pnpm run-due:force, or “Run now” in the panel.
  3. Read the outcome. If it did not fire and you expected it to, go straight to Why didn’t my watch fire?.