IronWatchDocs
Dashboard
synced 2026-05-29·/api/v1 routes@live
WatchTower · API reference

API reference

Everything you need to create uptime & SSL monitors, read their check history and incidents, verify domains, and schedule maintenance windows. New to WatchTower? Start with the quickstart.

Base URL & authentication

The base URL is https://watchtower.works and every route lives under /api/v1. Requests carry a Bearer token (wt_…) created in Settings → API keys. A missing, placeholder, invalid, or revoked key returns 401.

Monitors

A monitor is a recurring check against one URL. Full CRUD lives under /api/v1/monitors (GET, POST, GET /{id}, PATCH /{id}, DELETE /{id}). GET /api/v1/monitors returns { monitors: [...] }for the key's user.

POST/api/v1/monitors
Bearer60 creates/hr · key

Create a monitor. Runs a synchronous first check and returns the full monitor object, including regions and the initialCheck result.

Request body
name
string·REQUIRED
Human-readable label. Trimmed and truncated to 100 chars.
url
string·REQUIRED
Target endpoint. HTTP monitors may use http://; SSL monitors must be https://. Run through an SSRF guard.
checkType
string·OPTIONAL
http = uptime/keyword/status check; ssl = TLS handshake with cert-expiry alerting (Pro+).
default·"http"
"http""ssl"
method
string·OPTIONAL
HTTP verb used for each probe.
default·"GET"
expectedStatus
integer·OPTIONAL
Status code that counts as up.
default·200
keywordMatch
string·OPTIONAL
Substring that must appear in the response body for the check to pass. null disables.
headersJson
string·OPTIONAL
JSON-encoded string of custom request headers sent on each probe. null disables.
intervalSeconds
integer·OPTIONAL
Seconds between checks. Floored to your tier's minimum interval; unverified domains are capped at 3600.
default·300
flipsignComponentId
string·OPTIONAL
UUID of a FlipSign component to flip when this monitor changes state. null/"" clears it.
Responses
201Created
Monitor created and active. Body is the full monitor object, including its computed regions, cronpilotScheduleId, and an initialCheck (the synchronous first probe result, or null if it couldn't run).
400Bad Request
Invalid JSON, missing name/url, an unsupported checkType, a non-UUID flipsignComponentId, or a URL the SSRF guard rejected.
error.codename is requiredurl is requiredcheckType must be 'http' or 'ssl'flipsignComponentId must be a UUID
401Unauthorized
Missing, placeholder, invalid, or revoked API key.
403Forbidden
REST API requires Pro/Business; SSL monitoring requires Pro+; monitor cap reached; interval below tier minimum; or an unverified-domain cap hit.
error.codeSSL certificate monitoring is a Pro feature.Monitor limit reached (…). Upgrade to add more.Unverified domains support at most 1 monitor.Unverified domains must check at most once per hour.
429Too Many Requests
More than 60 monitors created via the API in the last hour for this key. A Retry-After header is included.
Request
POST /api/v1/monitors
curl -X POST "https://watchtower.works/api/v1/monitors" \
  -H "Authorization: Bearer <WATCHTOWER_API_KEY>" \
  -H "Content-Type: application/json" \
  -d {
       "name": "Checkout API",
       "url": "https://api.example.com/health",
       "expectedStatus": 200,
       "keywordMatch": "ok",
       "intervalSeconds": 60
     }'
Response201Created
application/json
{
"id": "d290f1ee-6c54-4b01-90e6-d701748f0851",
"userId": "8R2c1Pm9-…",
"name": "Checkout API",
"url": "https://api.example.com/health",
"checkType": "http",
"method": "GET",
"expectedStatus": 200,
"keywordMatch": "ok",
"headersJson": null,
"intervalSeconds": 60,
"isPaused": false,
"currentStatus": "unknown",
"regions": [
"iad1"
],
"cronpilotScheduleId": "sch_a1b2c3",
"initialCheck": {
"isUp": true,
"statusCode": 200,
"responseTimeMs": 142,
"error": null,
"transition": "stay-up"
},
"createdAt": "2026-05-29T15:32:00.000Z"
}
PATCH/api/v1/monitors/{id}
Bearertier · Pro+

Update a monitor. Only the fields you send are changed; url/interval changes are re-validated against the SSRF guard and unverified-domain caps. Mirrors the change onto the underlying schedule.

Path parameters
id
string·REQUIRED
Monitor UUID from the create response.
Request body
name
string·OPTIONAL
New label. Must be a non-empty string.
url
string·OPTIONAL
New target. Re-checked by the SSRF guard; SSL monitors can't be repointed to http://.
method
string·OPTIONAL
New HTTP verb.
expectedStatus
integer·OPTIONAL
New expected status code.
keywordMatch
string·OPTIONAL
New body keyword, or null to disable.
headersJson
string·OPTIONAL
New JSON header string, or null to disable.
intervalSeconds
integer·OPTIONAL
New cadence. Floored to your tier's minimum; revalidated against the unverified-domain interval cap.
isPaused
boolean·OPTIONAL
Pause or resume the monitor (and its underlying schedule).
flipsignComponentId
string·OPTIONAL
UUID of a FlipSign component, or null/"" to clear.
Responses
200OK
Updated monitor object. With an empty body (no recognised fields), the unchanged monitor is returned.
400Bad Request
A supplied field had the wrong type, or a new url failed the SSRF guard.
403Forbidden
API tier gate, interval below tier minimum, or an unverified-domain cap when the url/interval changes.
404Not Found
No monitor with that id belongs to you.
Request
PATCH /api/v1/monitors/{id}
curl -X PATCH "https://watchtower.works/api/v1/monitors/:id" \
  -H "Authorization: Bearer <WATCHTOWER_API_KEY>" \
  -H "Content-Type: application/json" \
  -d {
       "intervalSeconds": 60,
       "isPaused": false
     }'
Response200OK
application/json
{
"id": "d290f1ee-6c54-4b01-90e6-d701748f0851",
"userId": "8R2c1Pm9-…",
"name": "Checkout API",
"url": "https://api.example.com/health",
"checkType": "http",
"method": "GET",
"expectedStatus": 200,
"keywordMatch": "ok",
"headersJson": null,
"intervalSeconds": 60,
"isPaused": false,
"currentStatus": "unknown",
"regions": [
"iad1"
],
"cronpilotScheduleId": "sch_a1b2c3",
"initialCheck": {
"isUp": true,
"statusCode": 200,
"responseTimeMs": 142,
"error": null,
"transition": "stay-up"
},
"createdAt": "2026-05-29T15:32:00.000Z"
}

Checks

The time-series of probe results for a monitor — status code, latency, up/down, and any error. This is the rollup row written each round (multi-location detail is aggregated into it).

GET/api/v1/monitors/{id}/checks
Bearertier · Pro+

List recent check results for a monitor, newest first.

Path parameters
id
string·REQUIRED
Monitor UUID.
Query parameters
limit
integer·OPTIONAL
How many recent results to return, newest first. Clamped to 1–500.
default·50
Responses
200OK
{ checks: [...] } — recent check_results rows ordered by checkedAt descending.
403Forbidden
REST API requires Pro/Business.
404Not Found
No monitor with that id belongs to you.
Request
GET /api/v1/monitors/{id}/checks
curl -X GET "https://watchtower.works/api/v1/monitors/:id/checks" \
  -H "Authorization: Bearer <WATCHTOWER_API_KEY>" \
Response200OK
application/json
{
"checks": [
{7 fields}
]
}

Incidents

Down-periods for a monitor. An incident opens when a monitor crosses into a failing state and resolves when it recovers; resolvedAt: null means it's still open.

GET/api/v1/monitors/{id}/incidents
Bearertier · Pro+

List recent incidents for a monitor, newest first.

Path parameters
id
string·REQUIRED
Monitor UUID.
Query parameters
limit
integer·OPTIONAL
How many recent incidents to return, newest first. Clamped to 1–200.
default·50
Responses
200OK
{ incidents: [...] } — incidents ordered by startedAt descending. An open incident has resolvedAt: null.
403Forbidden
REST API requires Pro/Business.
404Not Found
No monitor with that id belongs to you.
Request
GET /api/v1/monitors/{id}/incidents
curl -X GET "https://watchtower.works/api/v1/monitors/:id/incidents" \
  -H "Authorization: Bearer <WATCHTOWER_API_KEY>" \
Response200OK
application/json
{
"incidents": [
{6 fields}
]
}

Domain verification

Unverified domains are capped at one monitor and an hourly minimum interval. Verifying the apex lifts both caps for every subdomain. Register a domain, prove ownership via a .well-known file or a DNS TXT record, then call /verify. List your domains with GET /api/v1/domains ({ domains: [...] }).

POST/api/v1/domains
Bearertier · Pro+

Register a domain. Returns a pending entry with a pathToken (.well-known file path) and a verificationToken (the file body / TXT value). Idempotent: re-posting an existing domain returns it with 200.

Request body
domain
string·REQUIRED
The domain to verify (e.g. example.com). A full URL is accepted and normalised to its lowercase hostname.
Responses
201Created
{ domain } — a new pending entry carrying pathToken and verificationToken. Satisfy either the .well-known file or the DNS TXT record, then call /verify.
200OK
{ domain } — the domain already exists for you; the existing entry (and its tokens/status) is returned. Idempotent re-post.
400Bad Request
Invalid JSON, missing domain, or a value that didn't parse to a hostname.
error.codedomain is requiredInvalid domain
403Forbidden
REST API requires Pro/Business.
Request
POST /api/v1/domains
curl -X POST "https://watchtower.works/api/v1/domains" \
  -H "Authorization: Bearer <WATCHTOWER_API_KEY>" \
  -H "Content-Type: application/json" \
  -d {
       "domain": "example.com"
     }'
Response201Created
application/json
{
"domain": {
"id": "f8a9b0c1-4567-4def-a012-3456789abcde",
"userId": "8R2c1Pm9-…",
"domain": "example.com",
"pathToken": "a1b2c3d4e5f60718",
"verificationToken": "9f8e7d6c5b4a3210",
"verificationMethod": "well_known",
"status": "pending",
"verifiedAt": null,
"createdAt": "2026-05-29T15:32:00.000Z"
}
}
POST/api/v1/domains/verify
Bearertier · Pro+

Re-check a pending domain. WatchTower probes both the .well-known file and the DNS TXT record; the first that matches flips the entry to verified.

Request body
domain
string·REQUIRED
The domain whose pending verification to re-check. Normalised to its hostname.
Responses
200OK
{ status: "verified", method, verified_at } when ownership is confirmed via .well-known or DNS TXT; otherwise { status: "pending", message, pathToken, verificationToken } so you can retry. An already-verified domain returns { status: "verified", verified_at }.
400Bad Request
Invalid JSON, missing domain, or an unparseable value.
403Forbidden
REST API requires Pro/Business.
404Not Found
No pending domain with that name belongs to you.
Request
POST /api/v1/domains/verify
curl -X POST "https://watchtower.works/api/v1/domains/verify" \
  -H "Authorization: Bearer <WATCHTOWER_API_KEY>" \
  -H "Content-Type: application/json" \
  -d {
       "domain": "example.com"
     }'
Response200OK
application/json
{
"status": "verified",
"method": "dns_txt",
"verified_at": "2026-05-29T15:40:00.000Z"
}

Maintenance windows

A window suppresses incident creation — and therefore alerts — for the covered monitors during [startsAt, endsAt]. Omit monitorId for a global window. Manage them under /api/v1/maintenance-windows (GET { windows: [...] }, POST, GET /{id}, DELETE /{id}).

POST/api/v1/maintenance-windows
Bearertier · Pro+

Create a maintenance window. Scope it to one monitor with monitorId, or leave it out for a global window.

Request body
title
string·REQUIRED
Window label. Trimmed and truncated to 200 chars.
startsAt
string·REQUIRED
ISO 8601 timestamp (with Z or ±HH:MM offset) when suppression begins.
endsAt
string·REQUIRED
ISO 8601 timestamp when suppression ends. Must be strictly after startsAt.
description
string·OPTIONAL
Optional note, or null.
monitorId
string·OPTIONAL
UUID of one monitor to scope the window to. Omit or null for a global window covering every monitor you own. Must be a monitor you own.
Responses
201Created
The maintenance window object. Incidents (and alerts) are suppressed for the covered monitors during [startsAt, endsAt].
400Bad Request
Non-object body, missing title, missing/invalid ISO timestamps, or endsAt not after startsAt.
error.codetitle is requiredstartsAt and endsAt are required (ISO 8601)startsAt and endsAt must be valid ISO 8601 timestampsendsAt must be after startsAt
403Forbidden
REST API requires Pro/Business.
404Not Found
monitorId referred to a monitor you don't own.
error.codeMonitor not found
Request
POST /api/v1/maintenance-windows
curl -X POST "https://watchtower.works/api/v1/maintenance-windows" \
  -H "Authorization: Bearer <WATCHTOWER_API_KEY>" \
  -H "Content-Type: application/json" \
  -d {
       "title": "Quarterly DB migration",
       "startsAt": "2026-06-01T02:00:00.000Z",
       "endsAt": "2026-06-01T04:00:00.000Z"
     }'
Response201Created
application/json
{
"id": "a0b1c2d3-5678-4ef0-b123-456789abcdef",
"userId": "8R2c1Pm9-…",
"monitorId": null,
"title": "Quarterly DB migration",
"description": "Read-only window during the migration.",
"startsAt": "2026-06-01T02:00:00.000Z",
"endsAt": "2026-06-01T04:00:00.000Z",
"createdAt": "2026-05-29T15:32:00.000Z"
}

Errors

Every error returns JSON with an error field. Unverified-domain rejections add a stable code (monitor_cap or interval_cap) so you can branch on them programmatically.

StatusMeaning
400Invalid JSON, missing/typed-wrong field, bad checkType, or a URL the SSRF guard rejected
401Missing, placeholder, invalid, or revoked API key
403API tier gate (Free/Starter), SSL on a non-Pro plan, monitor cap, interval below tier minimum, or an unverified-domain cap
404Monitor, domain, maintenance window, or referenced monitorId not found / not yours
429More than 60 monitor creates/hour for this key (Retry-After included)

Tiers & limits

TierMonitorsMin intervalAPISSLMulti-location
Free105 min
Starter501 min
ProUnlimited1 miniad1 + fra1
BusinessUnlimited1 miniad1 + fra1

note Alert channels also scale by tier — email (Free), +Slack/webhook (Starter), +Discord/PagerDuty (Pro+). See the live numbers on the pricing page.