A monitor re-captures a URL on a schedule and diffs the normalized content (line-trimmed markdown, SHA-256). On change it emits a webhook event and keeps a snapshot for later diffing.
Endpoints
POST /v1/monitor create
GET /v1/monitor list (keyset)
GET /v1/monitor/{id} detail
PATCH /v1/monitor/{id} { "status": "paused" | "active" } | interval | ignore_patterns | wait_for
DELETE /v1/monitor/{id} delete (cascades checks + snapshots)
GET /v1/monitor/{id}/checks check history (keyset); changed/removed rows carry
an inline diff_excerpt (bounded change summary)
GET /v1/monitor/{id}/checks/{checkId}/diff adjacent-snapshot line diff (free)
GET /v1/monitor/{id}/snapshots/{snapshotId} snapshot source: normalized markdown
(text/markdown, not the JSON envelope; free)
POST /v1/monitor/{id}/run trigger a manual check now → 202Create body: {url, name?, interval_minutes?, ignore_patterns?, webhook_id?, wait_for?}.
Semantics
- Interval floor is per-plan — e.g. Free gets a coarser minimum than paid
plans; too-small values are rejected with
INVALID_REQUEST. wait_for(ms, 0–60000, optional) — extra wait after page load before capture, same field as/v1/markdown. Pages that hydrate their content with JS (app-like directories, dashboards, filtered lists) need it: without it the snapshot may only contain page meta/boilerplate and changes to the real content are never seen. If the first check's snapshot looks like front-matter only, PATCH{"wait_for": 5000}— the next check re-captures aschanged(a legit baseline re-cut, not an error). Content below the rendered viewport (infinite scroll) is still out of scope.- Each check costs 1 credit; errored checks cost 0.
ignore_patterns: regex lines stripped before hashing — use for timestamps, view counters, ad text that changes every render.- Check statuses:
new(first baseline) ·same·changed·removed·error. - Circuit breaker: after consecutive errors the monitor auto-pauses and
notifies you — fix the target and resume with
PATCH. - Snapshots are stored on
new/changedonly (bounded retention per monitor). - Diff endpoint:
GET .../checks/{checkId}/diffreturns a bounded hunk-style line diff (contextparam,elisionsegments,stats,truncated).available:false+reasonis a normal state, not an error —not_diffable(same/error),snapshot_expired(snapshot pruned by retention: snapshots outlive ~10 changes, checks 200),too_large. - Snapshot source endpoint:
GET .../snapshots/{snapshotId}returns the stored normalized markdown (line-trimmed,ignore_patternslines stripped — not the raw page) astext/markdown, withETag=content_hash andX-Snapshot-Char-Count/X-Snapshot-Created-Atheaders. Check rows exposesnapshot_id(content written by that check) andprev_snapshot_id(the baseline — forremoved/samerows this is the content that was seen). Only the latest ~10 snapshots per monitor are kept: a404usually means the snapshot was pruned by retention, so fetch soon after the event.
Notifications
Monitors notify via your team's notification channels — subscribe to
monitor.* events in dashboard → Notifications, or pin a specific channel
with webhook_id. Use POST /v1/monitor/{id}/run to verify the wiring
without waiting for the schedule.