CAPABILITY · MONITORS
Watch a page on a schedule, get a signed webhook on change
A monitor reads a value on a cadence, hashes it, compares it to last time, and posts an HMAC-signed webhook only when it actually moved.
POST /api/v1/monitorsCapability
Inside change monitoring
A monitor is a URL, a cadence, and a definition of what counts as "the value". Each scheduled check reads that value, hashes it, and compares the hash to the previous one. An unchanged check does nothing except appear in the history. A change posts a monitor.changed webhook to your callback, signed when you set a secret — which is what makes a tight cadence survivable on your side.
Two check types, two price points
check_type "observe" watches a CSS selector's text (or the page map with no selector) deterministically and cheaply. check_type "extract" watches LLM-extracted fields or a natural-language description — the only option when the value is not addressable by a selector.
Hash-compared, not diffed
The value is normalized and SHA-256 hashed. The webhook carries both hashes and an excerpt of the current value, so your handler sees what changed without you storing the previous state.
Signed like the job callbacks
With callback_secret set, the delivery carries x-twin-signature: sha256=<hex HMAC of the raw body> and x-twin-event: monitor.changed.
History you can audit
GET /monitors/{id}/history returns recent checks with changed / unchanged / error plus value excerpts — so a monitor that has been quietly failing is visible rather than mistaken for a page that never changes.
How it works
The mechanism, in execution order
4 stages, in the order the runtime performs them — not a summary of them.
- 1Define the valuePick check_type, a URL, and either a selector (observe) or watch_fields / nl_watch (extract).
- 2Set the cadenceinterval_seconds, minimum 60. Multiply it by the per-check price before you choose it.
- 3Check and hashEach scheduled run reads the value, normalizes it and hashes it against the last hash.
- 4Fire only on changeA differing hash posts monitor.changed to callback_url with the previous hash, the new hash and an excerpt of the value.
In practice
A call you can paste and run
The webhook carries both hashes and an excerpt of the new value — enough to act on without storing previous state yourself.
curl -X POST https://twin-browser.com/api/v1/monitors \
-H "Authorization: Bearer $TWIN_API_KEY" \
-H "content-type: application/json" \
-d '{"name":"competitor pricing",
"check_type":"observe",
"url":"https://competitor.example.com/pricing",
"selector":".price-table",
"interval_seconds":3600,
"callback_url":"https://your.app/hooks/price",
"callback_secret":"whsec_REPLACE_ME"}'
# → 201 (the monitor, secrets excluded)
# The change webhook:
# x-twin-event: monitor.changed
# x-twin-signature: sha256=<hex>
# {
# "event": "monitor.changed", "monitorId": "…", "name": "competitor pricing",
# "checkType": "observe", "url": "https://…", "changedAt": "…",
# "previous": { "hash": "…" },
# "current": { "hash": "…", "value": "…excerpt…" }
# }- Define the valuedone
- Set the cadencerunning
- Check and hashqueued
- Fire only on changequeued
What it costs, how to switch it on
Priced from the same rate card the API serves
Every credit figure on this page is read from the rate card behind GET /api/v1/pricing — it is not typed into the copy, so it cannot drift from what you are billed.
Free to create · 1 or 5 credits per check
Creating, listing and deleting a monitor cost nothing. The recurring cost is the check: an observe check bills 1 credit, the same as POST /api/v1/observe; an extract check bills the 5-credit extract floor, settled higher-of against the model cost. Multiply by your cadence before you set interval_seconds to 60 — a one-minute observe monitor is 1,440 checks a day.
Turning it on
- 1Prefer observeIf the value has a stable CSS selector, check_type "observe" is deterministic, cheap, and cannot hallucinate a change.
- 2Set a secretcallback_secret is what makes the delivery verifiable. Without it you cannot tell a real change webhook from anyone else's POST.
- 3Choose the cadence with the price in front of youinterval_seconds has a 60-second floor, and every check is billed whether or not anything changed.
- 4Watch the historyGET /monitors/{id}/history shows changed / unchanged / error per check. A monitor erroring silently looks exactly like a page that never changes.
At a glance
The contract, in the fewest rows that say it
| Property | Twin Browser |
|---|---|
| Create | POST /api/v1/monitors → 201 |
| Check types | "observe" (selector) · "extract" (fields) |
| Cadence | `interval_seconds`, minimum 60 |
| Event | monitor.changed |
| Signature | x-twin-signature: sha256=<hex> |
| History | GET /api/v1/monitors/{id}/history |
Limits
Where it stops, and what it deliberately does not do
A capabilities page with no limits section is a brochure. These are the ceilings, the defaults that will surprise you, and the things this capability is not.
Limits and defaults
- Minimum interval is 60 seconds.
- Every check is billed, including the ones that find nothing changed.
- A monitor whose tenant runs out of credits is paused rather than silently dropped — top up and re-enable it.
- The webhook carries an EXCERPT of the current value, not the whole page.
What it does not do
- It does not push on every check. Only a changed hash fires a webhook.
- It does not diff for you. It reports the previous hash, the new hash and an excerpt; interpreting the change is your handler's job.
- It does not act on the change. A monitor observes; kicking off a run in response is something your webhook handler does.
- It does not retain unlimited history — GET /monitors/{id}/history is paginated with a limit of 200 per page.
FAQ
Questions about change monitoring
Which check type should I use?
Do I get a webhook on every check?
How do I verify a monitor webhook?
What happens if my balance runs out?
Keep reading
The rest of the platform
The capabilities this one works with, the tools that expose it, the guides that use it end to end, and the vocabulary behind it.
Async jobs & webhooks
Submit a run as a background job and get an id immediately — then poll it, stream its status, or receive an HMAC-signed webhook when it completes.
Token-efficient DOM state
The live page is serialized into a numerically-indexed list of its interactive elements under a token budget, so the planner reasons over signal instead of markup.
Structured extraction
Ask for fields or a JSON schema and get typed data back — or match a per-host template, expand a row selector, or capture the page's own XHR, and pay no model cost at all.
Tools that expose it
Guides
Vocabulary
Related reading
Put your agent to work. Keep the decision.
Start free. Hand your agent a goal on a site you authorize, set the guardrails, and let the first successful run compile the skill every run after it replays.