Let an AI agent set up uptime and change alerts with one API call
By PingWhen · September 30, 2026
AI agents are good at fetching a page once and reasoning about it. They are less good at the boring middle: a durable schedule, a paid subscription, and a signed delivery channel that keeps working after the chat ends. That is the gap we built PingWhen's agent API for.
If your agent can read a short doc, POST JSON, and hand a human a checkout link, it can set up page-change and uptime alerts without scraping, cron, or a free-tier dashboard.
What the agent needs to know first
Two public docs are enough:
- https://pingwhen.app/llms.txt — short product summary for LLM loaders
- https://pingwhen.app/for-agents — the create / poll / webhook / cancel flow
- OpenAPI at https://pingwhen.app/v1/openapi.json if you want a machine-readable schema
There is no free plan and no agent-only price. Agents pay the same Stripe subscription as humans: Starter $9/mo (10 watches, every 15 min) or Plus $19/mo (40 watches, every 5 min).
The flow in five steps
- Read
/llms.txtor/for-agents. POST /v1/watcheswith the URL to watch and where to send alerts.- Give the human the returned
checkout_url(or open it in an approved browser tool). - Poll
GET /v1/watches/:id?token=...untilstatusisactive. - Receive signed webhook POSTs when the page changes, goes down, or comes back.
Payment is required before polling starts. The create call inserts the watch as pending_payment, opens Stripe Checkout, and returns immediately.
Create a watch
curl -sS -X POST https://pingwhen.app/v1/watches \
-H 'Content-Type: application/json' \
-d '{
"url": "https://example.com/status",
"notify": "https://agent.example/hooks/pingwhen",
"events": ["change", "down", "up"],
"plan": "starter",
"success_url": "https://agent.example/paid",
"cancel_url": "https://agent.example/abandoned"
}'
notify may be an HTTPS webhook URL (preferred for agents), an email, or a US/CA phone number. events is any subset of change, down, and up. plan is starter or plus. success_url and cancel_url are optional Stripe return URLs.
A typical 201 response looks like:
{
"watch_id": "w_...",
"status": "pending_payment",
"manage_token": "mt_...",
"plan": "starter",
"checkout_url": "https://checkout.stripe.com/c/pay/cs_...",
"poll": "GET /v1/watches/w_...?token=mt_...",
"cancel": "DELETE /v1/watches/w_...?token=mt_...",
"instructions": "Open checkout_url in a browser. Then poll until status=active."
}
Store manage_token. You need it to poll, cancel, and verify webhook signatures. If notify is an email for an existing customer who still has spare quota, Checkout is skipped and status comes back active right away.
Poll until active
curl -sS "https://pingwhen.app/v1/watches/w_...?token=mt_..."
Status values: pending_payment | active | past_due | canceled | expired. Once active, the JSON includes a Stripe Customer Portal URL so the human can manage billing.
Verify webhook signatures
Alerts to an HTTPS notify URL are JSON POSTs:
{
"event": "change",
"watch_id": "w_...",
"url": "https://example.com/status",
"checked_at": "2026-09-20T20:04:00Z",
"http_status": 200,
"excerpt": "text now includes $19/mo"
}
event is one of change | down | up | active. Headers:
User-Agent: PingWhenBot/1.0X-PingWhen-Signature: sha256=<hex>X-PingWhen-Event: change(same as the bodyevent)
The signature is HMAC-SHA256 of the raw request body, using manage_token as the secret. Header format is exactly sha256= plus the hex digest.
Node:
const crypto = require("crypto");
function verifyPingWhen(rawBody, signatureHeader, manageToken) {
const expected =
"sha256=" +
crypto.createHmac("sha256", manageToken).update(rawBody).digest("hex");
const a = Buffer.from(expected);
const b = Buffer.from(signatureHeader || "");
return a.length === b.length && crypto.timingSafeEqual(a, b);
}
Python:
import hashlib, hmac
def verify_pingwhen(raw_body: bytes, signature_header: str, manage_token: str) -> bool:
digest = hmac.new(manage_token.encode(), raw_body, hashlib.sha256).hexdigest()
expected = "sha256=" + digest
return hmac.compare_digest(expected, signature_header or "")
Use the raw bytes you received, not a re-serialized JSON object. Respond with 410 Gone (or {"ok":false,"unsubscribe":true}) to delete the watch. We retry failed deliveries a couple of times before giving up.
Honest limits (read these before you wire it in)
- We watch the HTML we receive: visible text after scripts/styles are stripped. We do not run a browser or execute JavaScript. If the words you care about are missing from view-source, use a JS-capable monitor or watch the JSON API the page loads instead.
- No free plan. Starter is $9/mo for 10 watches every 15 minutes; Plus is $19/mo for 40 watches every 5 minutes.
- Watch URLs go through SSRF protections (public HTTP(S) only). Private/local targets are refused.
- The first check is a silent baseline. You get alerted on later changes, not on the initial snapshot.
- Webhook destinations must be HTTPS. Email and US/CA SMS also work if that fits the human better than a hook.
Not sure whether a URL is a good candidate? The free one-shot page check fetches it the same way our watcher would and shows status, size, and a text preview.
Where this fits in an agent loop
A practical pattern: the human asks the agent to "watch this status page and ping my webhook if it dies or the copy changes." The agent reads /for-agents, creates the watch, returns the checkout link, polls to confirm payment, and then treats signed webhooks as durable events. Cancel a single watch with DELETE /v1/watches/:id?token=...; cancel the subscription through the portal URL once the watch is active.
We built PingWhen for the simple case — public, server-rendered pages and JSON URLs — not as a general browser automation platform. If that matches what your agent needs, start at /for-agents or the homepage at pingwhen.app.