Watch the pricing page

An agent can GET a page once. What it can't do is keep watching one for months and wake up only when something changes. A watch is that missing half: you hand us a source and a rule for what counts as interesting, we poll it politely and diff each result, and we fire a webhook the moment your rule matches. This rung introduces the fourth primitive — reacting to the world changing, rather than to a clock or a person — plus match rules and the change history that lets you tune them.

The story

A competitive-intel agent needs to know when a rival's pricing page changes — but it can't sit and refresh the page every few minutes forever. So it registers a watch on the pricing URL, scoped to the price element and a rule that fires only when a currency figure appears, polling every fifteen minutes. When the price moves, a watch.changed webhook wakes the agent with the new excerpt.

Solve it

Go to Watches → New watch. Pick the url source type, paste the pricing page URL, add a CSS selector to scope the diff to the price element, choose a match rule, and set a poll interval. On save you land on the watch's detail page, where its change history will accumulate.

POST /v1/watches/ with the url, an optional css_selector, a match_rule, a webhook_url, and a poll_interval (an ISO-8601 duration, clamped up to your tier's floor).

curl https://aigears.example.com/v1/watches/ \
  -H "Authorization: Bearer aigears_live_..." \
  -H "Idempotency-Key: rival-pricing-watch-1" \
  -H "Content-Type: application/json" \
  -d '{
    "source_type": "url",
    "url": "https://rival.example.com/pricing",
    "css_selector": "#price",
    "match_rule": {"kind": "regex", "pattern": "\\$[0-9]+"},
    "webhook_url": "https://hooks.example.com/aigears",
    "poll_interval": "PT15M"
  }'

The runnable version — create, inspect, pause/resume, delete — is examples/python/watches_agent.py and examples/typescript/watchesAgent.ts:

watch = create_watch(
    client,
    url="https://rival.example.com/pricing",
    webhook_url=webhook_url,
    poll_interval="PT15M",
    css_selector="#price",
)

"Watch rival.example.com/pricing and tell me when the price changes — check every 15 minutes."

watch_create(
  source_type="url",
  url="https://rival.example.com/pricing",
  css_selector="#price",
  match_rule={"kind": "regex", "pattern": "\\$[0-9]+"},
  webhook_url="https://hooks.example.com/aigears",
  poll_interval="PT15M",
)
  → { "id": "01J9Z8XKQR...", "status": "active",
      "poll_interval_seconds": 900 }

Reading a change notification

When a poll finds a change your rule matched, we fire one watch.changed webhook. Its payload is new-only and bounded: we store only a hash of each source, so the old value literally doesn't exist for us to send, and the new value is a capped excerpt (the changed region plus a bounded prefix), never the whole page.

{
  "event_type": "watch.changed",
  "data": {
    "watch_id": "01J9Z8XKQR...",
    "source_type": "url",
    "content_hash": "9f2b...",
    "excerpt": "Pro plan — $79 / month",
    "match": {"kind": "regex", "pattern": "\\$[0-9]+"}
  }
}

The agent reads the excerpt and reacts. If it needs to diff against the previous price, it keeps that from the last event it received. For receiving and verifying the webhook, see Receiving webhooks.

Tuning the match rule

A watch records every change it detects — even ones your match_rule didn't match. Only matched changes fire a webhook, but the unmatched ones are still logged, so the history can tell you a source "changed 12 times, matched twice". That gap is your tuning signal: read the history with watch_history(watch_id=...) (the per-change log lives on MCP and the dashboard, not REST in v1), see the changed-but-not-matched rows, and widen a rule that's too narrow — or tighten one that fires on noise.

Variations

  • Other sources. The same primitive watches an rss feed (fire on each new item) or a json_endpoint (fire when a jsonpath value crosses a threshold), by switching source_type.
  • Private endpoints. A watch can carry auth headers, stored encrypted, for a source behind an API key.
  • Quiet a noisy period. watch_pause stops polling (and billing) without losing the config; watch_resume picks it back up. See the Watches guide for the full behaviour.

Next rung

The renewals agent — the capstone, where all four primitives compose into one workflow.