Monitors are the core resource. Every monitor has a top-level
monitor_type —
http, ping,
port, heartbeat or
cron — which determines the shape of the request payload and which
sub-checks (SSL, DNS, domain, keyword, JSON) are allowed.
GET
/monitors
List monitors for your team. Paginated.
Query Parameters
| Parameter | Type | Description |
|---|---|---|
search |
string | Filter by URL (partial match) |
status |
string | online, offline, degraded, pending |
type |
string | http, ping, port, heartbeat, cron |
interval |
number | Filter by check interval in minutes (e.g. 0.5, 1, 5) |
sort |
string | url, status, check_interval, last_checked_at, created_at |
direction |
string | asc or desc |
per_page |
integer | Page size, max 100 (default 20) |
curl "https://sentinel.rootstuff.io/api/v1/monitors?type=heartbeat&status=online" \
-H "Authorization: Bearer YOUR_API_TOKEN"
GET
/monitors/{id}
Get a single monitor with its recent incidents and response times.
{
"id": 1,
"monitor_type": "http",
"url": "https://example.com",
"friendly_name": "My Website",
"status": "online",
"check_interval": 1,
"check_types": ["ssl", "dns"],
"monitored_regions": ["us-east", "us-west", "eu-central", "ap-southeast", "eu-west", "us-central", "ap-northeast", "ap-south", "au-east", "ca-central", "sa-east", "af-south", "il-central", "kr-central", "mx-central"],
"is_paused": false,
"last_checked_at": "2026-05-29T10:30:00Z",
"last_heartbeat_at": null,
"heartbeat_expected_at": null,
"ping_url": null,
"incidents": [...],
"response_times": [...]
}
For heartbeat and cron monitors,
ping_url is the unique URL your job should ping;
last_heartbeat_at and heartbeat_expected_at are
populated; and url mirrors the ping URL.
POST
/monitors
Create a monitor. The request body shape depends on monitor_type.
Common Fields
| Field | Type | Required | Description |
|---|---|---|---|
monitor_type |
string | No (defaults to http) |
http, ping, port, heartbeat, cron. Plan-gated. |
friendly_name |
string | Required for push | Display name. Required for heartbeat/cron; optional otherwise. |
check_interval |
number | Required for pull | Minutes between checks (0.25–60, 0.25 = 15 seconds; the plan floor applies). Not used for heartbeat/cron. |
confirmation_seconds |
integer | Optional | Seconds the outage must hold across regions before an incident opens and alerts go out (0–300, defaults to 30, or 0 on a single-region monitor; 0 alerts at once). Not used for heartbeat/cron. |
recovery_seconds |
integer | Optional | Seconds the target must stay up before the incident closes and the recovery alert goes out (0–1800, defaults to 60; 0 closes at once). Not used for heartbeat/cron. |
monitored_regions |
array | No | Any of us-east, us-west, eu-central, ap-southeast, eu-west, us-central, ap-northeast, ap-south, au-east, ca-central, sa-east, af-south, il-central, kr-central, mx-central. The older keys (ash, pdx, nbg, sin) are accepted and mean the same regions. Defaults to all. |
check_types |
array | No | Sub-checks. HTTP: ssl, dns, domain, keyword, json. Ping/port: dns, domain. Heartbeat/cron: none. |
Type-Specific Fields
| Field | Type | Used By | Description |
|---|---|---|---|
url |
string | http (URL), ping/port (host or URL) | Full URL for HTTP; hostname or URL for ping/port. Generated by the server for heartbeat/cron. |
port |
integer | port (required) | TCP port (1–65535). |
heartbeat_interval |
integer | heartbeat (required) | Expected interval in seconds (30–2592000). |
heartbeat_cron_expression |
string | cron (required) | 5-field cron expression (e.g. */5 * * * *). |
heartbeat_timezone |
string | heartbeat/cron | IANA timezone (e.g. America/New_York). Defaults to UTC. |
heartbeat_grace |
integer | heartbeat/cron | Grace period in seconds before a missed ping is treated as failure (0–86400, defaults to 60). |
Content, Auth & Organization Fields (HTTP)
| Field | Type | Description |
|---|---|---|
group_id |
integer | Assigns the monitor to a group (see the Groups resource). null to ungroup. |
keyword_settings |
object | Content assertions: keywords is up to 10 of {phrase, mode, case_sensitive} with mode must_contain or must_not_contain; search_target is html or text. Supplying keywords implies the keyword check type. Plan-gated. |
json_assertion_settings |
object | API response assertions: assertions is up to 10 of {path, operator, value} with operators equals, not_equals, contains, not_contains, exists, not_exists, gt, gte, lt, lte, regex, not_regex. Mutually exclusive with keyword matching on the same monitor. Plan-gated. |
payment_settings |
object | Agent-payment check (http only): with the payment check type, the monitor passes only when the URL answers HTTP 402 with a valid x402 (v1 or v2) or MPP payment envelope. Optional expected object asserts amount (decimal USD), pay_to (case-insensitive), and network against the envelope; a mismatch fails the check. Supplying expected values implies the payment check type. Replaces keyword and JSON body checks on the same monitor. Every plan. |
notification_settings |
object | Per-monitor alert routing, the same block the monitor's Notifications page saves. enabled (boolean) turns alerts on or off for this monitor; channels maps each of email, sms, slack, discord, teams, webhook, pagerduty, telegram, in_app to the severities it fires at (in_app is the dashboard bell; it is stored and returned under its legacy name database, which is also accepted) (critical, warning, info); quiet_hours is {enabled, start, end, timezone, bypass_critical} with times as HH:MM. If no channel lists a given severity, alerts at that severity fall back to each member's own preferences. On update, each block you send replaces the stored one and blocks you omit are untouched; omit the whole object to leave routing alone. Custom recipients can only be managed in the dashboard. Returned only to tokens with write access. |
auth_type |
string | basic (username and password) or bearer (a token, sent as Authorization: Bearer …). Omit for unauthenticated checks. A token belongs here rather than in an Authorization request header. |
auth_username |
string | Required for basic, ignored for bearer. Returned only to tokens with the update permission. |
auth_password |
string | Required with auth_type: the password for basic, the token for bearer. Write-only: never returned. Sending the masked placeholder •••••••• on update keeps the stored value. |
Example: HTTP monitor
curl -X POST "https://sentinel.rootstuff.io/api/v1/monitors" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"monitor_type": "http",
"url": "https://example.com",
"check_interval": 1,
"check_types": ["ssl", "dns"]
}'
Example: Ping monitor
curl -X POST "https://sentinel.rootstuff.io/api/v1/monitors" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"monitor_type": "ping",
"url": "db.example.com",
"check_interval": 1
}'
Example: Port monitor
curl -X POST "https://sentinel.rootstuff.io/api/v1/monitors" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"monitor_type": "port",
"url": "db.example.com",
"port": 5432,
"check_interval": 1
}'
Example: Heartbeat monitor
curl -X POST "https://sentinel.rootstuff.io/api/v1/monitors" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"monitor_type": "heartbeat",
"friendly_name": "Nightly Backup",
"heartbeat_interval": 86400,
"heartbeat_grace": 300
}'
The response includes ping_url — the unique URL your job should hit
after each successful run. Append ?status=fail to report a failed run.
Example: Cron monitor
curl -X POST "https://sentinel.rootstuff.io/api/v1/monitors" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"monitor_type": "cron",
"friendly_name": "Weekday Billing",
"heartbeat_cron_expression": "30 2 * * 1-5",
"heartbeat_timezone": "America/New_York",
"heartbeat_grace": 300
}'
PUT
/monitors/{id}
Update a monitor. monitor_type is locked — create a new monitor
instead of changing type. For heartbeat/cron, url and
check_interval are ignored (the ping URL is system-owned); changes to
heartbeat_interval or
heartbeat_cron_expression recompute the next-expected ping time.
curl -X PUT "https://sentinel.rootstuff.io/api/v1/monitors/1" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"check_interval": 0.5}'
DELETE
/monitors/{id}
Delete a monitor and all associated data.
curl -X DELETE "https://sentinel.rootstuff.io/api/v1/monitors/1" \
-H "Authorization: Bearer YOUR_API_TOKEN"
# Response: 204 No Content
POST
/monitors/{id}/check
Trigger an immediate check outside the normal schedule. The response carries an immediate result from one location, and every region the monitor uses runs a check too. Those regional results arrive within about a minute and count like scheduled checks, so after a fix a healthy result starts the recovery period of an open incident right away. The regional run happens at most once every 30 seconds per monitor. Inside that window the response still carries the immediate result, with an empty regions_queued and regions_retry_after giving the seconds to wait. HTTP, ping and port monitors only. Heartbeat and cron monitors are push-based and have no outbound check to trigger.
{
"uptime": {
"status": "online",
"response_time": 245,
"status_code": 200,
"error": null
},
"ssl": null,
"regions_queued": ["ash", "pdx", "nbg", "sin"]
}
POST
/monitors/{id}/pause
Pause monitoring. Useful during deployments. For heartbeat/cron, pausing also ignores inbound pings.
{
"success": true,
"message": "Monitor paused",
"monitor": { "id": 1, "is_paused": true }
}
POST
/monitors/{id}/resume
Resume monitoring after it was paused.
{
"success": true,
"message": "Monitor resumed",
"monitor": { "id": 1, "is_paused": false }
}
GET / POST
/ping/{token}
(public, no auth)
The public ingest endpoint for heartbeat/cron monitors. Your job hits this URL when it finishes
successfully — the token is the monitor's ping_url token. GET, POST and
HEAD are all accepted; throttled to 120 requests per minute. Append ?status=fail
to report a failed run and open an incident immediately.
# Success ping
curl -fsS "https://sentinel.rootstuff.io/ping/your-token"
# Failure ping — opens an incident
curl -fsS "https://sentinel.rootstuff.io/ping/your-token?status=fail"