Watchtower

Change detection · for people and agents

Tell me when this page changes.

Give it a URL. Get back what the page says now, whether it differs from your baseline, and exactly what moved. Point a monitor at it and the same check runs on a schedule, pushing a signed webhook only when there is something to say.

Most of the time the answer is no change, and that is the answer you are paying for.

No subscription — pay in Ounie credits · 3 credits a check

$19.00PASS 1No change.Nothing sent. Nothing to do.WEBHOOKTimestamps, tokens and cache-bustersare ignored before anything is compared.

Two ways to watch

One check, or a standing watch.

A check is one look at one page, compared against a baseline you supply. It works on your Ounie credits and on keyless x402, because a single call has a single payer.

A monitor repeats that check on a schedule and pushes the alert to you. It needs an Ounie account, and that is not an upsell: a recurring watch has to bill somebody on every renewal and has to have somewhere to deliver. A keyless caller has neither — so rather than sell a subscription we cannot renew, we say so here instead of after you have paid.

ONE CHECKPOST urlanswerCompared against the baselineyou sent. Then it ends.credits · x402A MONITORcheckwalletRepeats. Pays per pass.Pushes when it changed.credits only

On x402

Pass the previous response's snapshot_id as your baseline on the next call. You keep the schedule; we keep the comparison honest.

On credits

Up to 50 active monitors, hourly to weekly, each paying 3 credits per pass. Out of credits? The pass is skipped, not failed, and it resumes the moment you top up.

The honest part

What counts as a change.

A page whose only difference is a rotating CSRF token, a “last updated” stamp or a cache-busting asset URL has not changed. If we called that a change we would bill you and fire your webhook every hour, forever, and within a day changed: true would mean nothing to you. So before anything is hashed, the extraction is normalised.

THE PAGENORMALISEthen sha256DISCARDED2026-08-25T07:56:18Zcsrf=8f3ab91c…?v=a91f4c23 minutes agoKEPT$29.00In stock: 4v2.7.1© 2026

Ignored, by default

  • timestampsDates and clock times, in ISO, numeric and written forms.
  • relative_times"3 minutes ago", "just now", "yesterday".
  • tokensUUIDs, CSRF and nonce values, and long opaque hashes such as asset fingerprints.
  • cache_bustersVersioning query parameters on URLs (?v=, ?t=, ?cb=, ?hash=).

Plus whitespace, line wrapping and invisible spaces — always. In the default scope the renderer has already dropped navigation, footers, asides and advertising. Turn any of it off and compare raw.

Never ignored

  • Prices, stock levels, counts and any other number (unless you turn on `numbers`).
  • Version strings, release names and copyright years.
  • The order of a list — a reordered page has changed.
  • Capitalisation, punctuation and wording.

Extraction is deterministic — selectors and a field map, no model. A stochastic extractor would report changes that never happened, and each one would cost you 3 credits and an alert.

Use cases

Pages worth a standing watch.

Pricing pages

A competitor moves a number. You know the same hour, with the old value and the new one side by side.

Terms, policies, docs

The paragraph that changed, quoted. Not a notification that says something, somewhere, is different.

Status and changelog feeds

Watch the page rather than the RSS nobody maintains. Narrow it to one selector and ignore the rest.

Stock, availability, waitlists

A field map turns a page into named values, so the alert reads price: 19 → 29 rather than a wall of markdown.

For developers & AI agents

Three ways in. One wallet behind them.

Every rail draws the same Ounie credits and refuses before it overdraws, so an agent can never spend money you have not put there. Reading a result you already paid for is always free.

REST

Bearer a wtc_live_ key — or your Ounie master key — on the same endpoints the dashboard uses. A bad key returns 401, never a redirect.

curl -X POST https://watch.ounie.com/api/checks \
  -H "Authorization: Bearer wtc_live_…" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/pricing",
    "selector": ".plan-pro .price",
    "baseline": "<a previous check id>"
  }'

MCP

Eleven tools for Claude, Cursor, ChatGPT and the Ounie AI Team. The key can ride the URL for hosts that cannot set headers.

{
  "mcpServers": {
    "watchtower": {
      "url": "https://watch.ounie.com/api/mcp?api_key=wtc_live_…"
    }
  }
}

get_change_policy and get_pricing need no key — read them before you interpret a result.

x402

No account, no key. Pay per call in USDC on Base. The endpoint returns 402 with terms; sign and retry with an X-Payment header.

# 402 first, then sign and retry
curl -X POST https://watch.ounie.com/api/x402/check \
  -H "Content-Type: application/json" \
  -d '{"url":"https://example.com/pricing","baseline":"<snapshot_id>"}'

Sells one check. Not monitors — there is no wallet to bill on renewal and nowhere to deliver an alert, and we would rather say that here than take your money for it.

Full API docs

3 credits a check · refused, never overdrawn

Credit maths

3 credits a check. The same for a monitor pass.

A monitor pass does exactly the work a check does, so it costs exactly the same. There is no recurring discount to reason about and no plan to pick. A page that yields nothing at all is refunded in full; a page that simply did not change is a real answer and is billed.

3 crone check=3 crone monitor pass·freereading a result·freea skipped passONE PRICE, BOTH WAYS

Questions

The awkward ones first.

What stops it crying wolf every hour?

Normalisation, before anything is compared. By default we mask timestamps, relative_times, tokens, cache_busters: timestamps in every form, "3 minutes ago", CSRF and nonce values, long opaque hashes, and cache-busting query parameters. Whitespace, line wrapping and invisible spaces are always normalised. In the default scope the renderer has already dropped navigation, footers, asides and advertising. Everything else — prices, counts, versions, copyright years, and the ORDER of a list — is compared exactly as it is.

So what would it miss?

A change that consists only of something we mask. If the page you care about genuinely publishes a meaningful date — an event that moved, a deadline that shifted — pass `ignore_volatile: []` and we compare raw, or narrow to a selector so the date is the only thing in scope. The defaults are defaults, not a ceiling, and every class can be turned off individually.

Why can’t an agent buy a recurring monitor with x402?

Because we could not deliver it. A recurring watch has to bill somebody on every renewal and has to have somewhere to push the alert. A keyless caller has no wallet we can draw again and no account to hold a webhook against. Selling a subscription we cannot renew would be selling something that does not exist — so x402 buys one check, and you keep the schedule by passing the previous snapshot_id as your next baseline.

Does a screenshot decide whether something changed?

No. Two renders of an unchanged page produce different pixels constantly — a lazy image lands a frame later, a font swaps, an animation is mid-frame. Comparing screenshots would report a change on nearly every pass. The picture is evidence a human looks at; the text extraction is the verdict.

Is the extraction done by a model?

No, deliberately. Selectors and a field map, evaluated the same way every time. A model's output is not reproducible across runs or model versions, so two identical pages would routinely extract differently and be reported as changed — and every false report would cost you credits and fire your webhook. That is why `schema` here is a map of name → CSS selector rather than a JSON Schema document, and why we refuse one rather than quietly doing something else.

What does a check cost?

3 Ounie credits — $0.03 in plain money, from the same wallet as every other Ounie app. A monitor pass costs exactly the same, because it does exactly the same work. Reading a result, listing your history and reading a monitor's delivery log are free forever.

Do I get charged when nothing changed?

Yes, and that is the answer you are paying for. "No change" is a fact somebody had to go and establish. What is never charged is a check where the page yielded nothing at all and there was no baseline to compare against — that taught you nothing, so it refunds in full.

What happens when I run out of credits mid-schedule?

The pass is skipped, not failed. Nothing is reserved, the monitor stays due, and the very next tick after you top up runs it. Your monitor does not need restarting and nothing needs clearing — the dashboard just says why the last pass did not happen.

Will it fetch anything I point it at?

No. Non-public addresses — localhost, link-local, private ranges, cloud metadata, .internal and .local names — are refused for free, before any price is quoted, on both rails. The same refusal applies to a webhook URL, so a monitor cannot be turned into the thing a check refuses to be. DNS can still rebind a public name after the check, so this is an honest refusal rather than a full egress firewall, and it says so in the docs.

How do I know a webhook really came from you?

Every delivery carries `X-Watchtower-Signature: sha256=<hmac>` over the exact bytes we sent, keyed with the secret shown once when you create the monitor. Verify it and nobody who merely learns your URL can feed you a forged alert. Failed deliveries are logged and visible — a monitor that has been 500ing for a week should never be a mystery.

How many monitors can I run?

Up to 50 active at once, hourly, daily or weekly. Pausing one costs nothing and keeps its history. Deleting one keeps its past checks readable.

Point it at a page.

Your first check records a baseline. Everything after that is the answer to one question: did anything actually move?