Monitors
Create, list, update, pause, resume and delete monitors via the API.
The monitor object:
{
"id": "0b0f6a41-…",
"object": "monitor",
"name": "Marketing site",
"type": "http",
"target": "https://example.com",
"description": null,
"interval_seconds": 60,
"status": "up",
"active": true,
"scheduling_strategy": "roundrobin",
"locations": ["…"],
"tags": ["prod", "web"],
"config": { "method": "GET" },
"created_at": "2026-08-01T09:12:00.000Z",
"updated_at": "2026-08-20T14:03:11.000Z"
}status is one of up, down, paused, unknown — see
how monitor status works. Secret values inside
config (passwords, private keys, auth headers) are returned as
"[redacted]" — the API never exposes stored credentials.
List monitors
GET /v1/monitors — newest first, cursor-paginated.
Query parameters: limit (1–100, default 50), cursor, type, status,
tag, q (case-insensitive substring search over name and target).
List items are slim — id, name, type, target, status,
interval_seconds, tags. The full object (config, locations,
timestamps, available_metrics) comes from
GET /v1/monitors/{id}.
curl "https://watchfor.io/api/v1/monitors?status=down&limit=20" \
-H "Authorization: Bearer wf_live_YOUR_KEY"{
"object": "list",
"data": [ { "id": "…", "object": "monitor", "…": "…" } ],
"has_more": true,
"next_cursor": "MGIwZjZh…"
}Pass next_cursor back as cursor to fetch the next page.
Create a monitor
POST /v1/monitors — requires write scope. Runs the exact same path as
the dashboard: your plan's limits apply, checks start immediately, and the
default alert rules for the monitor type are scaffolded automatically.
Body fields: name (required, ≤30 chars), type (required — http,
api, dns, tcp, ssl, icmp, heartbeat, … any type from the
dashboard), target (required, format depends on type), interval
(required, seconds), locations (array of location ids from
GET /v1/locations — required for every type except
heartbeat), schedulingStrategy (roundrobin default | random |
parallel, plan-gated), tags, description, config (type-specific,
same keys the dashboard uses), isPaused (create without starting checks).
curl -X POST https://watchfor.io/api/v1/monitors \
-H "Authorization: Bearer wf_live_YOUR_KEY" \
-H "Idempotency-Key: create-marketing-site" \
-H "Content-Type: application/json" \
-d '{
"name": "Marketing site",
"type": "http",
"target": "https://example.com",
"interval": 60,
"locations": ["9f31c2ab-…"],
"tags": ["prod", "web"]
}'Returns 201 with the created monitor. Send an
Idempotency-Key so a
retried request can't create two monitors. For heartbeat monitors the
target is server-assigned (the ping URL path) and locations are not
used — pass config.scheduleMode (period or cron) instead.
Update a monitor
PATCH /v1/monitors/{id} — requires write scope. Send only the fields
you want to change: name, target, description, interval,
schedulingStrategy, locations, tags, config. The monitor type is
fixed at creation. Use pause /
resume to start or stop checks — status is not
writable here.
Alerting-related config keys (failIfBodyMatchesRegexp,
failIfBodyNotMatchesRegexp, notificationGroupIds, …) are preserved
when your config payload omits them — send the key with a null value
to explicitly remove it.
curl -X PATCH https://watchfor.io/api/v1/monitors/0b0f6a41-… \
-H "Authorization: Bearer wf_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{ "interval": 300, "tags": ["prod", "web", "critical"] }'Config keys you omit that are owned by other surfaces (alerting settings, notification groups) are preserved, not wiped — the same protection the dashboard's Settings form gets.
Get a monitor
GET /v1/monitors/{id}
curl https://watchfor.io/api/v1/monitors/0b0f6a41-… \
-H "Authorization: Bearer wf_live_YOUR_KEY"Pause a monitor
POST /v1/monitors/{id}/pause — requires write scope. Stops checks and
closes the monitor's open incidents (so resuming starts clean).
curl -X POST https://watchfor.io/api/v1/monitors/0b0f6a41-…/pause \
-H "Authorization: Bearer wf_live_YOUR_KEY"Returns the updated monitor with "status": "paused".
Resume a monitor
POST /v1/monitors/{id}/resume — requires write scope.
Pause and resume are idempotent: pausing an already-paused (or resuming an
already-active) monitor is a no-op returning 200 with the current object.
curl -X POST https://watchfor.io/api/v1/monitors/0b0f6a41-…/resume \
-H "Authorization: Bearer wf_live_YOUR_KEY"Delete a monitor
DELETE /v1/monitors/{id} — requires write scope. Permanently removes the
monitor and everything it owns: check history references, alert rules,
incidents and silences. This cannot be undone.
curl -X DELETE https://watchfor.io/api/v1/monitors/0b0f6a41-… \
-H "Authorization: Bearer wf_live_YOUR_KEY"Returns 204 No Content.
Uptime & response times
GET /v1/monitors/{id}/uptime?period=24h — sample-based uptime and
response-time stats. Periods: 1h, 24h (default), 7d, 30d, 90d.
Time inside activated maintenance windows marked "exclude from SLA" is
removed from both the numerator and denominator. uptime_percent is
null when the period has no checks — no data is not 100%.
curl "https://watchfor.io/api/v1/monitors/0b0f6a41-…/uptime?period=7d" \
-H "Authorization: Bearer wf_live_YOUR_KEY"{
"object": "uptime",
"monitor_id": "0b0f6a41-…",
"period": "7d",
"uptime_percent": 99.983,
"total_checks": 10080,
"successful_checks": 10078,
"failed_checks": 2,
"avg_response_time_ms": 100,
"min_response_time_ms": 67,
"max_response_time_ms": 1216,
"last_failure_at": "2026-08-22T04:12:00Z",
"uptime_24h": 100,
"uptime_7d": 99.983,
"uptime_30d": 99.995
}Check history
GET /v1/monitors/{id}/checks?hours=24&limit=50 — individual probe
results (newest first) plus a per-location performance breakdown for the
same window. hours 1–720, limit 1–100.
{
"object": "list",
"data": [
{
"object": "check",
"timestamp": "2026-08-24 18:48:25Z",
"success": true,
"duration_ms": 74,
"location": "eu-frankfurt-1",
"error": null,
"status_code": 200
}
],
"locations": [
{
"location": "eu-frankfurt-1",
"checks": 1440,
"uptime_percent": 100,
"avg_response_time_ms": 100,
"min_response_time_ms": 67,
"max_response_time_ms": 1216
}
],
"has_more": true
}Run a check now
POST /v1/monitors/{id}/check-now — requires write scope. Dispatches
one immediate check without touching the schedule; returns 202 Accepted
and the result appears in check history within the probe timeout. The
budget is your plan's Run now limit (per monitor per hour, shared with
the dashboard's "Run check now" menu action) — exceeding it returns 429
with Retry-After. Not available for heartbeat monitors.
curl -X POST https://watchfor.io/api/v1/monitors/0b0f6a41-…/check-now \
-H "Authorization: Bearer wf_live_YOUR_KEY"Bulk operations
POST /v1/monitors/bulk — requires write scope. Pause, resume or
delete up to 100 monitors in one call. Unknown ids (or ids from another
organization) are reported in skipped, never an error — retrying a
partially-applied batch is safe. Supports Idempotency-Key.
curl -X POST https://watchfor.io/api/v1/monitors/bulk \
-H "Authorization: Bearer wf_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{ "action": "pause", "ids": ["0b0f6a41-…", "9c2d1e57-…"] }'{ "object": "bulk_result", "action": "pause", "succeeded": ["0b0f6a41-…"], "skipped": ["9c2d1e57-…"] }Alert rules
Each monitor has alert rules — the conditions that open an incident
(e.g. success == false, response_time > 2000). New monitors get the
default rules for their type automatically; these endpoints let you list
and fine-tune them.
GET /v1/monitors/{id}/alert-rules — list a monitor's rules.
{
"object": "list",
"data": [
{
"id": "a16c3a38-…",
"object": "alert_rule",
"monitor_id": "0b0f6a41-…",
"name": "Availability Check",
"metric": "success",
"operator": "==",
"value": "false",
"severity": "critical",
"failure_threshold": 3,
"min_failed_probes": 1,
"duration_window_seconds": 0,
"enabled": true,
"is_system": true,
"contact_group": null,
"created_at": "2026-08-01T09:12:00.000Z",
"updated_at": "2026-08-01T09:12:00.000Z"
}
],
"has_more": false
}POST /v1/monitors/{id}/alert-rules — requires write scope. Body:
name, metric, operator, value (required), severity
(critical|warning|info, default critical), failureThreshold
(1–100), minFailedProbes (1–100), durationWindow (seconds),
contactGroupId. The rule is pushed to the alerting engine immediately.
Validation: metric must be one of the monitor type's
alert metrics (plus the universal success),
operator and value must fit the metric's value type (numbers get
comparison operators and numeric values; status_code takes
in_groups/not_in_groups with a JSON array like ["2xx","3xx"] or
[200,301]). At most one rule per metric per severity exists on a
monitor — a duplicate returns 409 with the existing rule's id, so
PATCH that rule instead.
curl -X POST https://watchfor.io/api/v1/monitors/0b0f6a41-…/alert-rules \
-H "Authorization: Bearer wf_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Slow response",
"metric": "response_time",
"operator": ">",
"value": "2000",
"severity": "warning"
}'GET / PATCH / DELETE /v1/monitors/{id}/alert-rules/{ruleId} — get,
partial-update, or delete one rule (write scope for PATCH/DELETE). PATCH
also carries enabled: true|false to enable/disable the rule (disabling
resolves any incident it left firing). System rules (the auto-created
defaults, is_system: true) can be edited/toggled but not deleted —
DELETE returns 403.
curl -X PATCH https://watchfor.io/api/v1/monitors/0b0f6a41-…/alert-rules/a16c3a38-… \
-H "Authorization: Bearer wf_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{ "value": "5000", "enabled": false }'Monitor Settings (name, target, interval, locations, tags, config)
are managed with PATCH /v1/monitors/{id}.
MCP Server
Connect AI agents (Claude, Cursor, VS Code) to WatchFor via the Model Context Protocol — 26 tools over your monitors, incidents and alerting.
Monitor types reference
Every WatchFor monitor type — its target format, default alert rules, all alert metrics you can build rules from, and every config field. The complete per-type reference for building monitors and alert rules via the API.