WatchFor
API

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 slimid, 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 deletedDELETE 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}.

On this page