Uptime monitoring for developers — monitors, incidents, heartbeats, maintenance, status pages
Maintain this server? Claim it
Call this server's tools from your browser. Nothing to install.
Fettle API key as a bearer token: "Bearer fettle_…". The same key the npm package takes in FETTLE_API_KEY.
Requests go through mcptry because browsers can't call most MCP servers directly. Header values are sent to the server for that request only and are never stored or logged.
Tool list from our last check. Connect live to refresh it.
Mark an open incident as being handled so its repeat alerts stop; the recovery alert still goes out when it resolves. Use it when someone is working on the outage. It does not close the incident (that happens when the monitor recovers) and it is not for status page incidents (post_status_page_update). Requires a key with the write scope. Acknowledging twice keeps the first acknowledgement; on a resolved incident it succeeds without effect on alerts. Returns the incident in the get_incident shape.
The incident's uuid from list_incidents. An id that is not an incident of this organization is a 404.
This tool isn't marked read-only, so it may change something on the server or in an account you connected.
Mark an open incident as being handled so its repeat alerts stop; the recovery alert still goes out when it resolves. Use it when someone is working on the outage. It does not close the incident (that happens when the monitor recovers) and it is not for status page incidents (post_status_page_update). Requires a key with the write scope. Acknowledging twice keeps the first acknowledgement; on a resolved incident it succeeds without effect on alerts. Returns the incident in the get_incident shape.
| Argument | Type | Description |
|---|---|---|
| incident_id* | string | The incident's uuid from list_incidents. An id that is not an incident of this organization is a 404. |
{
"type": "object",
"required": [
"incident_id"
],
"properties": {
"incident_id": {
"type": "string",
"format": "uuid",
"description": "The incident's uuid from list_incidents. An id that is not an incident of this organization is a 404."
}
},
"additionalProperties": false
}Create 1 to 100 monitors in one call, all or nothing; for a single monitor use create_monitor. Requires a key with the write scope. An invalid item, or a batch that would exceed the plan, rejects the whole call and creates nothing: call get_plan_usage first to check the batch fits. Errors name the failing item. A field that fails validation is named by its path, such as monitors.3.target (index 3 is the fourth item; all such errors are reported at once). Any other item error keeps the status create_monitor would give (403 when its interval or regions break the plan or name an unknown region, 404 for an unknown channel id, 422 for its config or its type's region rules) and its reason starts with monitors[3] ("<item name>"): — only the first such item is reported. Read the index only from that leading monitors[N] (the pattern ^monitors\[(\d+)\] after any lead-in such as "Forbidden: " or "Invalid request: "), never from the name, which may itself contain quotes or brackets. A plan-limit 403 without that prefix is about the batch as a whole: together the items would exceed the plan's monitor count or its quota of monitors below 60 seconds. Fix the item or shrink the batch and send the whole call again. Returns monitors: one list_monitors row per created monitor, in request order. The example is a port-list TCP monitor, which takes exactly one region and an interval of at least 300 seconds. Example: {"monitors": [{"name": "db.example.com exposed ports", "type": "TCP", "target": "db.example.com", "interval_seconds": 3600, "timeout_seconds": 10, "regions": ["eu-central"], "config": {"ports": [5432, 6379, 27017], "expected_state": "closed"}}]}
| Argument | Type | Description |
|---|---|---|
| monitors* | array | 1 to 100 monitors, each taking the same fields and rules as create_monitor's arguments. Items with is_active false are created paused and still count toward the plan. Unless the user names regions, give each item every code in limits.allowed_regions, up to the plan's max_regions; tell the user the intervals you chose. |
{
"type": "object",
"required": [
"monitors"
],
"properties": {
"monitors": {
"type": "array",
"items": {
"type": "object",
"required": [
"name",
"type",
"target",
"interval_seconds",
"timeout_seconds",
"regions"
],
"properties": {
"name": {
"type": "string",
"maxLength": 255,
"minLength": 1
},
"tags": {
"type": "array",
"items": {
"type": "string"
}
},
"type": {
"enum": [
"HTTP",
"PING",
"SSL",
"TCP",
"DNS"
],
"type": "string"
},
"config": {
"type": "object",
"additionalProperties": true
},
"target": {
"type": "string",
"maxLength": 2048,
"minLength": 1
},
"regions": {
"type": "array",
"items": {
"type": "string"
},
"minItems": 1
},
"is_active": {
"type": "boolean"
},
"channel_ids": {
"type": "array",
"items": {
"type": "string",
"format": "uuid"
}
},
"timeout_seconds": {
"type": "integer",
"maximum": 120,
"minimum": 1
},
"interval_seconds": {
"type": "integer",
"maximum": 86400,
"minimum": 30
},
"confirmation_threshold": {
"type": "integer",
"maximum": 10,
"minimum": 1
},
"notification_cooldown_seconds": {
"type": "integer",
"maximum": 86400,
"minimum": 60
}
},
"additionalProperties": false
},
"maxItems": 100,
"minItems": 1,
"description": "1 to 100 monitors, each taking the same fields and rules as create_monitor's arguments. Items with is_active false are created paused and still count toward the plan. Unless the user names regions, give each item every code in limits.allowed_regions, up to the plan's max_regions; tell the user the intervals you chose."
}
},
"additionalProperties": false
}Pause up to 100 monitors in one call, all or nothing: no checks run and each open incident closes without an alert. Use it to stop monitoring a whole project or group found with list_monitors; for one monitor use pause_monitor, and to silence alerts around a deploy while checks keep running use create_maintenance_window instead. Requires a key with the write scope. Pausing a paused monitor is not an error. Returns monitors: the list_monitors rows after the change, in request order. Example: {"monitor_ids": ["<id from list_monitors>", "<id from list_monitors>"]}
| Argument | Type | Description |
|---|---|---|
| monitor_ids* | array | 1 to 100 distinct monitor uuids from list_monitors. A repeated id is a 422; an id that is not a monitor of this organization is a 404 naming it, and then nothing changes. |
{
"type": "object",
"required": [
"monitor_ids"
],
"properties": {
"monitor_ids": {
"type": "array",
"items": {
"type": "string",
"format": "uuid"
},
"maxItems": 100,
"minItems": 1,
"description": "1 to 100 distinct monitor uuids from list_monitors. A repeated id is a 422; an id that is not a monitor of this organization is a 404 naming it, and then nothing changes."
}
},
"additionalProperties": false
}Resume up to 100 paused monitors in one call, all or nothing; each goes back to PENDING and is checked on the next tick. For one monitor use resume_monitor; to run a check right away on a monitor that is already running, use check_now. Requires a key with the write scope. Resuming more monitors than the plan's active limit allows is a 403 and nothing changes (see get_plan_usage). A monitor already running is left as it is. Returns monitors: the list_monitors rows after the change, in request order. Example: {"monitor_ids": ["<id from list_monitors>", "<id from list_monitors>"]}
| Argument | Type | Description |
|---|---|---|
| monitor_ids* | array | 1 to 100 distinct monitor uuids from list_monitors. A repeated id is a 422; an id that is not a monitor of this organization is a 404 naming it, and then nothing changes. |
{
"type": "object",
"required": [
"monitor_ids"
],
"properties": {
"monitor_ids": {
"type": "array",
"items": {
"type": "string",
"format": "uuid"
},
"maxItems": 100,
"minItems": 1,
"description": "1 to 100 distinct monitor uuids from list_monitors. A repeated id is a 422; an id that is not a monitor of this organization is a 404 naming it, and then nothing changes."
}
},
"additionalProperties": false
}Add and/or remove tags across up to 300 monitors in one call, all or nothing: each monitor ends with (its tags | add) - remove. Use it to retag a fleet; to replace one monitor's whole tag list use update_monitor, to rename or drop a tag everywhere (heartbeats too) use rename_tag or delete_tag. Requires a key with the write scope. At least one of add or remove is required, and they must not share a tag (422). A monitor that would end with more than 10 tags rejects the whole call (422). Each tag is trimmed, lowercased and deduplicated on write, at most 50 characters of a-z, 0-9 and : _ . / -, starting with a letter or digit. Reuse names from list_tags rather than inventing near-duplicates. Returns monitors_updated. Example: {"monitor_ids": ["<id from list_monitors>", "<id from list_monitors>"], "add": ["env:prod"], "remove": ["env:staging"]}
| Argument | Type | Description |
|---|---|---|
| add | array | Tags to add to every listed monitor, up to 10. |
| remove | array | Tags to remove from every listed monitor, up to 10. |
| monitor_ids* | array | 1 to 300 distinct monitor uuids from list_monitors. A repeated id is a 422; an unknown id rejects the whole call with a 404 naming every id not found. |
{
"type": "object",
"required": [
"monitor_ids"
],
"properties": {
"add": {
"type": "array",
"items": {
"type": "string"
},
"description": "Tags to add to every listed monitor, up to 10."
},
"remove": {
"type": "array",
"items": {
"type": "string"
},
"description": "Tags to remove from every listed monitor, up to 10."
},
"monitor_ids": {
"type": "array",
"items": {
"type": "string",
"format": "uuid"
},
"maxItems": 300,
"minItems": 1,
"description": "1 to 300 distinct monitor uuids from list_monitors. A repeated id is a 422; an unknown id rejects the whole call with a 404 naming every id not found."
}
},
"additionalProperties": false
}Set the same interval, timeout, regions, channels, threshold or cooldown on up to 100 monitors in one call, all or nothing. For example, move twenty PING monitors to other regions. Not for config or name (use update_monitor, one monitor at a time) or tags (use bulk_update_monitor_tags). Requires a key with the write scope. The plan's rules apply to the result: an interval below the plan minimum, too many fast monitors, too many regions or an unknown region is a 403. A value one monitor cannot take (several regions on an SSL, DNS or port-list TCP monitor, or an interval under 300 on a port-list one) is a 422 that starts with monitors[<i>] ("<name>"). Returns monitors: list_monitors rows, which show the interval but not the other settings; read those with get_monitor. Example: {"monitor_ids": ["<id from list_monitors>", "<id from list_monitors>"], "changes": {"regions": ["eu-central", "us-east"], "interval_seconds": 300}}
| Argument | Type | Description |
|---|---|---|
| changes* | object | The values to set, at least one of: interval_seconds (30 to 86400), timeout_seconds (1 to 120), regions (region codes from limits.allowed_regions in get_plan_usage), channel_ids (channel uuids from list_channels; replaces each binding, [] clears it, an unknown channel is a 404), confirmation_threshold (1 to 10) and notification_cooldown_seconds (60 to 86400). Omit a field to keep it; never send null. |
| monitor_ids* | array | 1 to 100 distinct monitor uuids from list_monitors. A repeated id is a 422; an id that is not a monitor of this organization is a 404 naming it, and then nothing changes. |
{
"type": "object",
"required": [
"monitor_ids",
"changes"
],
"properties": {
"changes": {
"type": "object",
"properties": {
"regions": {
"type": "array",
"items": {
"type": "string"
},
"minItems": 1
},
"channel_ids": {
"type": "array",
"items": {
"type": "string",
"format": "uuid"
}
},
"timeout_seconds": {
"type": "integer",
"maximum": 120,
"minimum": 1
},
"interval_seconds": {
"type": "integer",
"maximum": 86400,
"minimum": 30
},
"confirmation_threshold": {
"type": "integer",
"maximum": 10,
"minimum": 1
},
"notification_cooldown_seconds": {
"type": "integer",
"maximum": 86400,
"minimum": 60
}
},
"description": "The values to set, at least one of: interval_seconds (30 to 86400), timeout_seconds (1 to 120), regions (region codes from limits.allowed_regions in get_plan_usage), channel_ids (channel uuids from list_channels; replaces each binding, [] clears it, an unknown channel is a 404), confirmation_threshold (1 to 10) and notification_cooldown_seconds (60 to 86400). Omit a field to keep it; never send null.",
"minProperties": 1,
"additionalProperties": false
},
"monitor_ids": {
"type": "array",
"items": {
"type": "string",
"format": "uuid"
},
"maxItems": 100,
"minItems": 1,
"description": "1 to 100 distinct monitor uuids from list_monitors. A repeated id is a 422; an id that is not a monitor of this organization is a 404 naming it, and then nothing changes."
}
},
"additionalProperties": false
}Queue one existing monitor to run on the scheduler's next tick instead of waiting out its interval, for example to confirm a fix. It returns the monitor record, not a check result: call get_monitor or list_check_results a moment later for the outcome. To probe a target that has no monitor yet use check_target; to restart a paused monitor use resume_monitor. Requires a key with the write scope. A repeat call for the same monitor within 30 seconds, or a call on a paused monitor, is a silent no-op that returns it unchanged. Calls are limited to 10 per minute per calling address (not per key or organization); past that it is a 429 with the seconds to wait, so pace a run over many monitors.
| Argument | Type | Description |
|---|---|---|
| monitor_id* | string | The monitor's uuid from list_monitors. An id that is not a monitor of this organization, or a deleted one, is a 404. |
{
"type": "object",
"required": [
"monitor_id"
],
"properties": {
"monitor_id": {
"type": "string",
"format": "uuid",
"description": "The monitor's uuid from list_monitors. An id that is not a monitor of this organization, or a deleted one, is a 404."
}
},
"additionalProperties": false
}Run one real check of a target from one region chosen by Fettle, without creating anything. It reports status (up, down, error or unknown), region, response_time_ms, status_code, error and failure_kind. unknown means no definite answer came back, not a failure. Use it before create_monitor to catch a typo or an unreachable host; a down result does not stop you creating the monitor. For a monitor that already exists use check_now. Requires a key with the write scope. It is rate limited per calling address: a 429 whose retry_after says how many seconds to wait. Example: {"type": "HTTP", "target": "https://api.example.com/health"}
| Argument | Type | Description |
|---|---|---|
| type* | string | HTTP (a URL answers with an expected status), PING (a host answers ICMP), SSL (a certificate is valid and not about to expire), TCP (a port is open, or a list of ports is closed) or DNS (a record resolves, optionally to an expected value). |
| config | object | As in create_monitor; TCP and DNS need it. The region, interval and update rules there do not apply to a one-off probe. An object whose fields depend on type, all optional unless stated. HTTP: method (GET, HEAD, POST, PUT, PATCH or DELETE, any case; default GET); expected_status_codes (non-empty list of codes 100 to 599, default [200]); keyword (case-sensitive substring the body must contain, at most 1024 characters, not with HEAD); follow_redirects (default true); verify_ssl (default true; false skips TLS verification, so the monitor can never report TLS_INVALID); request_headers (object of name to value: at most 20, a name at most 128 characters of letters, digits and ! # $ % & ' * + - . ^ _ ` | ~, a value at most 1024 bytes of printable ASCII, space or tab, the whole object at most 8192 bytes as JSON; names differing only in case, and Host, Content-Length, Transfer-Encoding, Connection, Upgrade, TE, Trailer, Keep-Alive, CF-Connecting-IP or any Proxy-* name, are a 422); request_body (string, at most 8192 bytes as UTF-8, needs POST, PUT, PATCH or DELETE). Reads return every request_headers value as the literal __FETTLE_UNCHANGED__; writing that literal back in an update keeps the stored value (on create it is a 422). PING: packet_count (1 to 20, default 4). SSL: warn_before_days (1 to 365, default 14) and port (1 to 65535, default 443). TCP: either port (1 to 65535) with expected_state "open" (default) or "closed", or ports (1 to 20 distinct ports that must all be closed, which needs expected_state "closed", exactly one region and interval_seconds of at least 300; a check fails when any of them accepts a connection, and the monitor goes DOWN once confirmation_threshold failures confirm it). Both port and ports, or neither, is a 422; expected_state values are lowercase. DNS: record_type (A, AAAA, CNAME, MX, TXT or NS; required) and expected_value (at most 2048 characters). With expected_value one answer must equal it (case-insensitive, ignoring surrounding spaces and a trailing dot; exact for TXT); without it any answer passes. MX answers are the mail host names without priority (mail.example.com, not 10 mail.example.com). A and AAAA look up every address of the host, IPv4 and IPv6 alike, and any one may match; write an IPv6 address in its shortest form. |
Create a heartbeat: Fettle alerts when a scheduled job stops calling its ping_url. Use it for cron jobs, backups and workers that should report in; to probe a URL, host or port from outside use create_monitor. The job calls ping_url (GET or POST) on success, ping_url + "/start" when it begins (restarts the clock) and ping_url + "/fail" to go DOWN at once. It is never late before its first success ping (a /fail call still marks it DOWN); after that an alert fires period_seconds + grace_seconds after the last ping. Requires a key with the write scope. The plan caps how many heartbeats exist (403 past it; see get_plan_usage). An invalid argument is a 422 naming it. Returns the heartbeat, with ping_url. Example: {"name": "nightly backup", "period_seconds": 86400, "grace_seconds": 3600}
| Argument | Type | Description |
|---|---|---|
| name* | string | Display name, 1 to 255 characters. |
| tags | array | Flat labels for grouping and filtering, for example env:prod or service:api, at most 10. Each tag is trimmed, lowercased and deduplicated on write, at most 50 characters of a-z, 0-9 and : _ . / -, starting with a letter or digit. Reuse names from list_tags rather than inventing near-duplicates. |
| channel_ids | array | Notification channel uuids from list_channels. Omit it or pass [] to alert every verified, active channel of the organization. An id that is not an active channel of this organization is a 404. |
| grace_seconds* | integer | Extra seconds of silence tolerated after the period before the heartbeat is reported late, 0 to 2592000; it may exceed the period. |
| period_seconds |
Create a one-time or recurring maintenance window that silences alerts for some or all monitors. While a window is in effect its monitors keep being checked, but no incident opens and no alert goes out. Use it around a deploy or reboot instead of pause_monitor; it does not cover heartbeats (use pause_heartbeat). For a recurring schedule run preview_maintenance_window first. Requires a key with the write scope. The plan caps how many windows exist (403 past it or when the plan has none; see max_maintenance_windows in get_plan_usage). An invalid argument or schedule is a 422 naming it. Returns the window. Example: {"name": "Deploy", "scope": "MONITOR", "monitor_ids": ["<id from list_monitors>"], "dtstart": "2026-10-14T22:00:00", "duration_minutes": 30}
| Argument | Type | Description |
|---|---|---|
| name* | string | Display name, 1 to 255 characters. |
| rrule | string | Repeats the window: one iCal RRULE without the RRULE: prefix, at most 1024 characters, for example FREQ=WEEKLY;BYDAY=SU;BYHOUR=3. Hourly, minutely and secondly frequencies are refused. Omit it for a one-time window. |
| scope* | string | ORG covers every monitor of the organization and ignores monitor_ids; MONITOR covers only monitor_ids. |
| dtstart* | string | The first start, an ISO 8601 date-time in the wall-clock time of timezone, for example 2026-10-14T22:00:00. A UTC offset or Z is a 422. |
| enabled | boolean | Default true; false stores the window without effect. |
Create one monitor that checks a URL, host or port on a schedule from one or more regions, and return it in the get_monitor shape. Use check_target first to catch a typo or an unreachable host; use bulk_create_monitors for several monitors, and create_heartbeat for a job that should report in rather than be probed. Requires a key with the write scope. Plan limits apply (monitor count, minimum interval, monitors faster than 60 seconds, regions per monitor): breaking one is a 403 naming it, so call get_plan_usage first. A paused monitor still counts toward them. An invalid argument is a 422 naming it. The example shows the shape for a plan with one region per monitor; take interval_seconds and regions from the user and get_plan_usage, not from it. Example: {"name": "API", "type": "HTTP", "target": "https://api.example.com/health", "interval_seconds": 600, "timeout_seconds": 10, "regions": ["us-east"], "config": {"expected_status_codes": [200]}, "tags": ["env:prod"]}
| Argument | Type | Description |
|---|---|---|
| name* | string | Display name, 1 to 255 characters. |
| tags | array | Flat labels for grouping and filtering, for example env:prod or service:api, at most 10. Each tag is trimmed, lowercased and deduplicated on write, at most 50 characters of a-z, 0-9 and : _ . / -, starting with a letter or digit. Reuse names from list_tags rather than inventing near-duplicates. |
| type* | string | HTTP (a URL answers with an expected status), PING (a host answers ICMP), SSL (a certificate is valid and not about to expire), TCP (a port is open, or a list of ports is closed) or DNS (a record resolves, optionally to an expected value). Cannot be changed after create. |
| config | object | An object whose fields depend on type, all optional unless stated. HTTP: method (GET, HEAD, POST, PUT, PATCH or DELETE, any case; default GET); expected_status_codes (non-empty list of codes 100 to 599, default [200]); keyword (case-sensitive substring the body must contain, at most 1024 characters, not with HEAD); follow_redirects (default true); verify_ssl (default true; false skips TLS verification, so the monitor can never report TLS_INVALID); request_headers (object of name to value: at most 20, a name at most 128 characters of letters, digits and ! # $ % & ' * + - . ^ _ ` | ~, a value at most 1024 bytes of printable ASCII, space or tab, the whole object at most 8192 bytes as JSON; names differing only in case, and Host, Content-Length, Transfer-Encoding, Connection, Upgrade, TE, Trailer, Keep-Alive, CF-Connecting-IP or any Proxy-* name, are a 422); request_body (string, at most 8192 bytes as UTF-8, needs POST, PUT, PATCH or DELETE). Reads return every request_headers value as the literal __FETTLE_UNCHANGED__; writing that literal back in an update keeps the stored value (on create it is a 422). PING: packet_count (1 to 20, default 4). SSL: warn_before_days (1 to 365, default 14) and port (1 to 65535, default 443). TCP: either port (1 to 65535) with expected_state "open" (default) or "closed", or ports (1 to 20 distinct ports that must all be closed, which needs expected_state "closed", exactly one region and interval_seconds of at least 300; a check fails when any of them accepts a connection, and the monitor goes DOWN once confirmation_threshold failures confirm it). Both port and ports, or neither, is a 422; expected_state values are lowercase. DNS: record_type (A, AAAA, CNAME, MX, TXT or NS; required) and expected_value (at most 2048 characters). With expected_value one answer must equal it (case-insensitive, ignoring surrounding spaces and a trailing dot; exact for TXT); without it any answer passes. MX answers are the mail host names without priority (mail.example.com, not 10 mail.example.com). A and AAAA look up every address of the host, IPv4 and IPv6 alike, and any one may match; write an IPv6 address in its shortest form. Omit it for HTTP, PING or SSL to take the defaults; a TCP or DNS monitor without it is a 422. |
Open a new incident on a status page with its first message, to tell the page's visitors about an outage. It is public and emails the page's confirmed subscribers: confirm the wording with the user before calling. Any status but RESOLVED emails every confirmed subscriber at once; RESOLVED records an already closed incident (a postmortem) and emails nobody. To add to an existing incident use post_status_page_update. Requires a key with the write scope. Returns the incident. Example: {"page_id": "<id from list_status_pages>", "title": "API errors", "body": "We are investigating elevated error rates."}
| Argument | Type | Description |
|---|---|---|
| body* | string | The first message. The message subscribers read, 1 to 5000 characters. |
| title* | string | The incident's headline, 1 to 255 characters. |
| status | string | INVESTIGATING, IDENTIFIED, MONITORING or RESOLVED; RESOLVED closes the incident on the page. Default INVESTIGATING. |
| page_id* | string | The status page's uuid from list_status_pages. An id that is not a status page of this organization is a 404. |
| component_ids | array | The affected components: uuids from get_status_page. An id that is not a component of this page is a 404. |
{
"type": "object",
"required": [
"page_id",
"title",
"body"
],
"properties": {
"body": {
"type": "string",
"maxLength": 5000,
"minLength": 1,
"description": "The first message. The message subscribers read, 1 to 5000 characters."
},
"title": {
"type": "string",
"maxLength": 255,
"minLength": 1,
"description": "The incident's headline, 1 to 255 characters."
},
"status": {
"enum": [
"INVESTIGATING",
"IDENTIFIED",
"MONITORING",
"RESOLVED"
],
"type": "string",
"description": "INVESTIGATING, IDENTIFIED, MONITORING or RESOLVED; RESOLVED closes the incident on the page. Default INVESTIGATING."
},
"page_id": {
"type": "string",
"format": "uuid",
"description": "The status page's uuid from list_status_pages. An id that is not a status page of this organization is a 404."
},
"component_ids": {
"type": "array",
"items": {
"type": "string",
"format": "uuid"
},
"description": "The affected components: uuids from get_status_page. An id that is not a component of this page is a 404."
}
},
"additionalProperties": false
}Delete one heartbeat for good: its ping_url stops working (404) and no tool restores it. Use it only when the user asked for deletion; to stop alerts for a while use pause_heartbeat. Requires a key with the write scope. Returns {"success": true}.
| Argument | Type | Description |
|---|---|---|
| heartbeat_id* | string | The heartbeat's uuid from list_heartbeats. An id that is not a heartbeat of this organization, or a deleted one, is a 404. |
{
"type": "object",
"required": [
"heartbeat_id"
],
"properties": {
"heartbeat_id": {
"type": "string",
"format": "uuid",
"description": "The heartbeat's uuid from list_heartbeats. An id that is not a heartbeat of this organization, or a deleted one, is a 404."
}
},
"additionalProperties": false
}Delete one maintenance window; alerts for its monitors resume at once and no tool restores it. To suspend a window but keep it, use update_maintenance_window with enabled false. Requires a key with the write scope. Returns {"success": true}.
| Argument | Type | Description |
|---|---|---|
| window_id* | string | The window's uuid from list_maintenance_windows. An id that is not a window of this organization is a 404. |
{
"type": "object",
"required": [
"window_id"
],
"properties": {
"window_id": {
"type": "string",
"format": "uuid",
"description": "The window's uuid from list_maintenance_windows. An id that is not a window of this organization is a 404."
}
},
"additionalProperties": false
}Delete one monitor: it stops being checked and disappears from the organization's monitors, and no tool restores it. Use it only when the user explicitly asked for deletion; otherwise prefer pause_monitor, which keeps the monitor and its history. There is no bulk delete. Requires a key with the write scope. Returns {"success": true}.
| Argument | Type | Description |
|---|---|---|
| monitor_id* | string | The monitor's uuid from list_monitors. An id that is not a monitor of this organization, or a deleted one, is a 404. |
{
"type": "object",
"required": [
"monitor_id"
],
"properties": {
"monitor_id": {
"type": "string",
"format": "uuid",
"description": "The monitor's uuid from list_monitors. An id that is not a monitor of this organization, or a deleted one, is a 404."
}
},
"additionalProperties": false
}Remove an incident and its timeline from a status page; it stops showing at once, nothing is sent to subscribers, and no tool restores it. Use it for an incident posted by mistake; to close a real one use post_status_page_update with RESOLVED. Requires a key with the write scope. Returns {"success": true}.
| Argument | Type | Description |
|---|---|---|
| page_id* | string | The status page's uuid from list_status_pages. An id that is not a status page of this organization is a 404. |
| incident_id* | string | The status page incident's uuid from list_status_page_incidents (not from list_incidents). An unknown id is a 404. |
{
"type": "object",
"required": [
"page_id",
"incident_id"
],
"properties": {
"page_id": {
"type": "string",
"format": "uuid",
"description": "The status page's uuid from list_status_pages. An id that is not a status page of this organization is a 404."
},
"incident_id": {
"type": "string",
"format": "uuid",
"description": "The status page incident's uuid from list_status_page_incidents (not from list_incidents). An unknown id is a 404."
}
},
"additionalProperties": false
}Remove a tag from every monitor and heartbeat that carries it; the items themselves stay, and no tool restores the tag. Use it to retire a tag across the organization; to take it off chosen monitors only use bulk_update_monitor_tags with remove, and to rename it use rename_tag. Requires a key with the write scope. A tag nothing carries is not an error (both counts are 0). Returns from (the tag), monitors_updated and heartbeats_updated.
| Argument | Type | Description |
|---|---|---|
| tag* | string | The tag to remove, exactly as list_tags shows it. |
{
"type": "object",
"required": [
"tag"
],
"properties": {
"tag": {
"type": "string",
"minLength": 1,
"description": "The tag to remove, exactly as list_tags shows it."
}
},
"additionalProperties": false
}List the ports that should never be reachable from the internet (databases, queues, admin panels). Returns ports, each a port number and the service that usually listens on it. Use it to pick ports when asked to watch a server for exposed services; it creates nothing. Then create one TCP monitor on the server's public hostname or IP with config {"ports": [...], "expected_state": "closed"} (1 to 20 distinct ports, one region, interval_seconds 300 or more; 3600 is plenty) through create_monitor: it counts as one monitor and fails naming every port that answers.
No arguments.
{
"type": "object",
"required": [],
"properties": {},
"additionalProperties": false
}Read one heartbeat in full, including how long its last run took. It has name, state, period_seconds, grace_seconds, ping_url, last_ping_at, last_success_at, last_start_at, last_duration_ms (how long the last run took, from its /start to its success ping), overdue_at (when it will be reported late), notification_cooldown_seconds, is_active, channel_ids and tags. Use it to answer whether a job ran, how long it took and when it is next due. To find a heartbeat use list_heartbeats; to change it use update_heartbeat; probed monitors are get_monitor.
| Argument | Type | Description |
|---|---|---|
| heartbeat_id* | string | The heartbeat's uuid from list_heartbeats. An id that is not a heartbeat of this organization, or a deleted one, is a 404. |
{
"type": "object",
"required": [
"heartbeat_id"
],
"properties": {
"heartbeat_id": {
"type": "string",
"format": "uuid",
"description": "The heartbeat's uuid from list_heartbeats. An id that is not a heartbeat of this organization, or a deleted one, is a 404."
}
},
"additionalProperties": false
}Read one incident in full, with every alert sent for it. It has id, monitor_id or heartbeat_id (exactly one is set), started_at, resolved_at, cause, cause_detail, last_notified_at, acknowledged_at, acknowledged_by, renotify_count and notification_logs (each alert attempt: id, channel_type, destination, event TRIGGERED, RESOLVED or SSL_EXPIRY, sent_at, status PENDING, SENT or FAILED, and error). Use it to tell who acknowledged an incident and whether its alerts were delivered. destination is an email address, a Telegram chat id, or only scheme://host/… of a webhook. To find incidents use list_incidents; to stop its reminders use acknowledge_incident.
| Argument | Type | Description |
|---|---|---|
| incident_id* | string | The incident's uuid from list_incidents. An id that is not an incident of this organization is a 404. |
{
"type": "object",
"required": [
"incident_id"
],
"properties": {
"incident_id": {
"type": "string",
"format": "uuid",
"description": "The incident's uuid from list_incidents. An id that is not an incident of this organization is a 404."
}
},
"additionalProperties": false
}Read one maintenance window with its schedule and its next three occurrences. It has the list_maintenance_windows fields plus next_occurrences, each with start and end. Use it to confirm when a recurring window fires next. To check a schedule before saving it use preview_maintenance_window; to find a window use list_maintenance_windows.
| Argument | Type | Description |
|---|---|---|
| window_id* | string | The window's uuid from list_maintenance_windows. An id that is not a window of this organization is a 404. |
{
"type": "object",
"required": [
"window_id"
],
"properties": {
"window_id": {
"type": "string",
"format": "uuid",
"description": "The window's uuid from list_maintenance_windows. An id that is not a window of this organization is a 404."
}
},
"additionalProperties": false
}Read one monitor's full configuration and current state. It has type, target, interval_seconds, timeout_seconds, regions, config, status, is_active, confirmation_threshold, notification_cooldown_seconds, channel_ids (empty means every verified, active channel is alerted), tags, badge_enabled, last_checked_at, next_run_at, and for SSL monitors ssl_expires_at and ssl_cert_info. Use it before update_monitor, since a config you send replaces the stored one whole. Not for finding a monitor (list_monitors), its uptime (get_uptime_summary) or why it fails (list_check_results).
| Argument | Type | Description |
|---|---|---|
| monitor_id* | string | The monitor's uuid from list_monitors. An id that is not a monitor of this organization, or a deleted one, is a 404. |
{
"type": "object",
"required": [
"monitor_id"
],
"properties": {
"monitor_id": {
"type": "string",
"format": "uuid",
"description": "The monitor's uuid from list_monitors. An id that is not a monitor of this organization, or a deleted one, is a 404."
}
},
"additionalProperties": false
}Show the organization's plan, its limits and current usage, to check that new monitors, heartbeats or regions fit before creating them or to explain a 403 naming a limit. Region codes and their cities come from list_regions; billing changes happen only in the dashboard. Values are current as of the call. Returns plan (one of FREE, PLUS, PRO, MAX, ENTERPRISE), limits and usage. limits: max_monitors, min_interval_seconds, max_fast_monitors (monitors below fast_interval_threshold_seconds, 60), max_regions per monitor, allowed_regions, max_heartbeats, max_maintenance_windows, history_retention_days, api_rate_limit_per_minute (shared by the whole organization), renotify_interval_seconds and renotify_max_count (how often and how many times an open, unacknowledged incident is alerted again; 0 means no reminders); null means no limit. usage: monitors (paused included), fast_monitors and heartbeats.
No arguments.
{
"type": "object",
"required": [],
"properties": {},
"additionalProperties": false
}Chart one monitor's average response time over time, one series per region, to see where it is slow and since when. Returns monitor_id, window, bucket_seconds and regions, each with region, display_name and points (oldest first, the same buckets in every series: bucket_start, avg_response_time_ms, null when the bucket had no successful check, and sample_count). Every configured region has a series, even an empty one, followed by removed regions that still had checks; points start at the window's start or at the monitor's creation. For uptime use get_uptime_summary; for single checks use list_check_results.
| Argument | Type | Description |
|---|---|---|
| window | string | 24h (the default; up to 24 hourly buckets), 7d (up to 28 six-hourly buckets) or 30d (up to 30 daily buckets). |
| monitor_id* | string | The monitor's uuid from list_monitors. An id that is not a monitor of this organization, or a deleted one, is a 404. |
{
"type": "object",
"required": [
"monitor_id"
],
"properties": {
"window": {
"enum": [
"24h",
"7d",
"30d"
],
"type": "string",
"description": "24h (the default; up to 24 hourly buckets), 7d (up to 28 six-hourly buckets) or 30d (up to 30 daily buckets)."
},
"monitor_id": {
"type": "string",
"format": "uuid",
"description": "The monitor's uuid from list_monitors. An id that is not a monitor of this organization, or a deleted one, is a 404."
}
},
"additionalProperties": false
}Read one status page with its components. It has the list_status_pages fields, where each component has id, monitor_id, display_name and position. Use it to get the component ids that create_status_page_incident takes as component_ids. To find a page use list_status_pages; for the incidents posted on it use list_status_page_incidents.
| Argument | Type | Description |
|---|---|---|
| page_id* | string | The status page's uuid from list_status_pages. An id that is not a status page of this organization is a 404. |
{
"type": "object",
"required": [
"page_id"
],
"properties": {
"page_id": {
"type": "string",
"format": "uuid",
"description": "The status page's uuid from list_status_pages. An id that is not a status page of this organization is a 404."
}
},
"additionalProperties": false
}Report one monitor's uptime for each UTC day, oldest first and ending today, to find the day an outage or degradation began. Each day has date, uptime_percentage, downtime_seconds, observed_seconds, degraded_seconds, total_checks, up_checks, down_checks, degraded_checks and maintenance_checks. degraded_seconds is the day's time in confirmed DEGRADED status (an ongoing degradation counts up to now; 0 for days before this monitor's status history began, when the status ledger shipped); it does not lower uptime_percentage. A day with degraded_seconds above 0 and no confirmed outage is a degraded (yellow) day in the dashboard; degraded_checks alone does not make one. uptime_percentage is the share of the day's observed time outside confirmed outages (an outage counts from the first failing probe to the first probe that is not DOWN; a failure only one region saw does not count; maintenance counts as up; hours with no checks are left out); a day with no checks and no outage has a null uptime_percentage, not 100. The check counts are informational: a failed check outside a confirmed outage does not lower uptime_percentage. For 24h, 7d and 30d totals use get_uptime_summary. Returns monitor_id and days.
| Argument | Type | Description |
|---|---|---|
| days | integer | How many days to return, 1 to 365; default 30. Capped at the plan's limits.history_retention_days (get_plan_usage), so fewer days can come back. |
| monitor_id* | string | The monitor's uuid from list_monitors. An id that is not a monitor of this organization, or a deleted one, is a 404. |
{
"type": "object",
"required": [
"monitor_id"
],
"properties": {
"days": {
"type": "integer",
"maximum": 365,
"minimum": 1,
"description": "How many days to return, 1 to 365; default 30. Capped at the plan's limits.history_retention_days (get_plan_usage), so fewer days can come back."
},
"monitor_id": {
"type": "string",
"format": "uuid",
"description": "The monitor's uuid from list_monitors. An id that is not a monitor of this organization, or a deleted one, is a 404."
}
},
"additionalProperties": false
}Report one monitor's uptime percentage, average response time and incident count over the last 24 hours, 7 days and 30 days at once, to answer how reliable it has been. For a day-by-day series use get_uptime_daily; for response time per region use get_response_time_by_region; for the individual failures use list_check_results. Returns monitor_id and windows keyed 24h, 7d and 30d, each with uptime_percentage, downtime_seconds, observed_seconds, window_start (later than the window's start when the monitor is younger), total_checks, up_checks, down_checks, degraded_checks, incident_count and avg_response_time_ms. uptime_percentage is the share of observed time outside confirmed outages (an outage counts from the first failing probe to the first probe that is not DOWN; a failure only one region saw does not count; maintenance counts as up; hours with no checks are left out; null when nothing was observed). The check counts are informational: a failed check outside a confirmed outage does not lower uptime_percentage.
| Argument | Type | Description |
|---|---|---|
| monitor_id* | string | The monitor's uuid from list_monitors. An id that is not a monitor of this organization, or a deleted one, is a 404. |
{
"type": "object",
"required": [
"monitor_id"
],
"properties": {
"monitor_id": {
"type": "string",
"format": "uuid",
"description": "The monitor's uuid from list_monitors. An id that is not a monitor of this organization, or a deleted one, is a 404."
}
},
"additionalProperties": false
}List the enabled maintenance windows in effect at this moment, to tell whether a monitor's alerts are silenced now. An ORG-scope window covers every monitor, a MONITOR-scope one its monitor_ids. For every window, including future and disabled ones, use list_maintenance_windows. Returns a plain array (not a page) of list_maintenance_windows items; empty when nothing is silenced.
No arguments.
{
"type": "object",
"required": [],
"properties": {},
"additionalProperties": false
}List the organization's notification channels (up to 100), to get the ids that channel_ids of monitors and heartbeats take. Each has id, type (TELEGRAM, WEBHOOK, SLACK or DISCORD), label, is_verified, is_active and destination_hint (a chat id, a masked address, or a webhook's scheme and host). Only a verified, active channel is alerted; an unverified one can be bound but receives nothing yet, and an inactive or unknown id cannot be bound (404). Email is not a channel: every alert also goes to the account email of whoever created the monitor or heartbeat. Channels are added only in the dashboard.
No arguments.
{
"type": "object",
"required": [],
"properties": {},
"additionalProperties": false
}List one monitor's individual check results, newest first, to find out why it is DOWN: which regions fail and with what status code or error. Each has id, region, checked_at, status, response_time_ms, status_code, error_message, details and maintenance_window_id (set when the check ran inside a window). For totals use get_uptime_summary or get_uptime_daily; for the incident they opened use list_incidents. Returns a page: items plus total (of matching results), page, size and pages.
| Argument | Type | Description |
|---|---|---|
| page | integer | Page number, 1 or more; default 1. |
| size | integer | Rows per page, 1 to 100; default 25. Keep paging until you have seen total rows. |
| region | string | Keep one region's results: its exact code, up to 64 characters. |
| status | string | Keep results with this status only. |
| monitor_id* | string | The monitor's uuid from list_monitors. An id that is not a monitor of this organization, or a deleted one, is a 404. |
| period_days | integer | Keep results of the last N days, 1 to 365; without it every stored result is eligible. |
{
"type": "object",
"required": [
"monitor_id"
],
"properties": {
"page": {
"type": "integer",
"minimum": 1,
"description": "Page number, 1 or more; default 1."
},
"size": {
"type": "integer",
"maximum": 100,
"minimum": 1,
"description": "Rows per page, 1 to 100; default 25. Keep paging until you have seen total rows."
},
"region": {
"type": "string",
"maxLength": 64,
"description": "Keep one region's results: its exact code, up to 64 characters."
},
"status": {
"enum": [
"UP",
"DEGRADED",
"DOWN",
"ERROR"
],
"type": "string",
"description": "Keep results with this status only."
},
"monitor_id": {
"type": "string",
"format": "uuid",
"description": "The monitor's uuid from list_monitors. An id that is not a monitor of this organization, or a deleted one, is a 404."
},
"period_days": {
"type": "integer",
"maximum": 365,
"minimum": 1,
"description": "Keep results of the last N days, 1 to 365; without it every stored result is eligible."
}
},
"additionalProperties": false
}Find heartbeat (cron / dead-man's-switch) monitors. Each has id, name, state (PENDING until a success ping makes it UP or a /fail ping makes it DOWN; later UP, DOWN or PAUSED), period_seconds, grace_seconds, ping_url, last_ping_at, last_success_at, overdue_at, tags and channel_ids. Use it to get a heartbeat's id or ping_url, or to see which jobs are late (sort status puts DOWN first). Probed URLs and hosts are monitors: use list_monitors for those. For one heartbeat's full record, with its last run's duration, use get_heartbeat. Returns a page: items plus total, page, size and pages.
| Argument | Type | Description |
|---|---|---|
| q | string | Case-insensitive substring of the name, up to 255 characters. |
| tag | array | Keep items carrying these tags: a JSON array of up to 10 tags, each up to 50 characters, lowercased before matching. tag_match decides whether all or any of them must match. |
| page | integer | Page number, 1 or more; default 1. |
| size | integer | Rows per page, 1 to 100; default 25. Keep paging until you have seen total rows. |
| sort | string | status (the default: DOWN, PENDING, UP, PAUSED, newest first within each), name, or created_at (newest first). |
| tag_match | string | all (the default): an item must carry every tag in tag. any: at least one of them. |
List the organization's monitor and heartbeat incidents, newest first, to see what is or was down and which alerts went out. Each item has id, monitor_id or heartbeat_id (exactly one is set), started_at, resolved_at (null while open), cause, cause_detail (the raw check error), acknowledged_at and notification_logs: the alerts sent, oldest first, each with id, channel_type (EMAIL, TELEGRAM, WEBHOOK, SLACK or DISCORD), destination (may be masked), event (TRIGGERED for the first alert and every reminder, RESOLVED for the recovery), sent_at, status (PENDING while a webhook, Slack or Discord delivery is still being tried, then SENT or FAILED) and error (the last delivery error). For one incident's full record use get_incident; for the checks behind it use list_check_results; incidents posted on a status page are list_status_page_incidents. An incident resolves by itself when its monitor or heartbeat recovers. Returns a page: items plus total, page, size and pages. An invalid value is a 422 naming the argument. Example: {"status": "open", "size": 50}
| Argument | Type | Description |
|---|---|---|
| page | integer | Page number, 1 or more; default 1. |
| size | integer | Rows per page, 1 to 100; default 25. Keep paging until you have seen total rows. |
| cause | string | Keep one failure cause, such as TIMEOUT, HTTP_5XX or HEARTBEAT_MISSED. |
| status | string | open or resolved (lowercase); omit it for both. |
| monitor_id | string | Keep one monitor's incidents: its uuid from list_monitors. |
List every maintenance window of the organization, enabled or not, in effect or not. Each has id, name, enabled, scope (ORG or MONITOR), dtstart, duration_minutes, rrule, timezone, monitor_ids and created_at. Use it to find a window's id. To learn whether alerts are silenced right now use list_active_maintenance; for a window's upcoming occurrences use get_maintenance_window. Returns a page: items plus total, page, size and pages.
| Argument | Type | Description |
|---|---|---|
| q | string | Case-insensitive substring of the name, up to 255 characters. |
| page | integer | Page number, 1 or more; default 1. |
| size | integer | Rows per page, 1 to 100; default 25. Keep paging until you have seen total rows. |
| sort | string | created_at (the default, newest first) or name. |
{
"type": "object",
"required": [],
"properties": {
"q": {
"type": "string",
"maxLength": 255,
"description": "Case-insensitive substring of the name, up to 255 characters."
},
"page": {
"type": "integer",
"minimum": 1,
"description": "Page number, 1 or more; default 1."
},
"size": {
"type": "integer",
"maximum": 100,
"minimum": 1,
"description": "Rows per page, 1 to 100; default 25. Keep paging until you have seen total rows."
},
"sort": {
"enum": [
"name",
"created_at"
],
"type": "string",
"description": "created_at (the default, newest first) or name."
}
},
"additionalProperties": false
}Find monitors in the organization by name, target, type, status or tag. Each row is short: id, name, type, target, status, is_active, tags, interval_seconds, ssl_expires_at and last_checked_at. Use it first to get a monitor's id, or to answer what is broken now (status ["DOWN", "DEGRADED"]) or which certificates expire first (type ["SSL"], sort ssl_expiry). For one monitor's full settings use get_monitor; for heartbeats use list_heartbeats. Returns a page: items plus total, page, size and pages. An invalid value is a 422 naming the argument. Example: {"status": ["DOWN", "DEGRADED"], "size": 100}
| Argument | Type | Description |
|---|---|---|
| q | string | Case-insensitive substring of the name or the target, up to 255 characters. |
| tag | array | Keep items carrying these tags: a JSON array of up to 10 tags, each up to 50 characters, lowercased before matching. tag_match decides whether all or any of them must match. |
| page | integer | Page number, 1 or more; default 1. |
| size | integer | Rows per page, 1 to 100; default 25. Keep paging until you have seen total rows. |
| sort | string | status (the default: PENDING, DOWN, DEGRADED, UP, PAUSED, then by name), up_first (healthy first), name, newest, oldest, or ssl_expiry (soonest-expiring certificates first; monitors with no known expiry, non-SSL types or not checked yet, last). |
| type | array |
List every check region of the platform, to show the user where checks can run. Returns regions, each with code (what a regions argument takes), display_name, city, country_code, group and group_display_name. The organization may use only some of them: to choose regions for a monitor read limits.allowed_regions and max_regions from get_plan_usage instead. For one monitor's per-region timings use get_response_time_by_region.
No arguments.
{
"type": "object",
"required": [],
"properties": {},
"additionalProperties": false
}List the incidents shown on one status page, newest first, both posted by a person and opened by Fettle. Each has id, title, status, is_auto (opened by Fettle from a monitor), started_at, resolved_at, notified_at (when subscribers were first emailed; null if never), component_ids and updates (the timeline, oldest first, each with id, status, body, created_by and created_at). Use it to find the incident_id for post_status_page_update. Monitor outages behind the scenes are list_incidents. Returns a page: items plus total, page, size and pages.
| Argument | Type | Description |
|---|---|---|
| page | integer | Page number, 1 or more; default 1. |
| size | integer | Rows per page, 1 to 100; default 25. Keep paging until you have seen total rows. |
| page_id* | string | The status page's uuid from list_status_pages. An id that is not a status page of this organization is a 404. |
{
"type": "object",
"required": [
"page_id"
],
"properties": {
"page": {
"type": "integer",
"minimum": 1,
"description": "Page number, 1 or more; default 1."
},
"size": {
"type": "integer",
"maximum": 100,
"minimum": 1,
"description": "Rows per page, 1 to 100; default 25. Keep paging until you have seen total rows."
},
"page_id": {
"type": "string",
"format": "uuid",
"description": "The status page's uuid from list_status_pages. An id that is not a status page of this organization is a 404."
}
},
"additionalProperties": false
}List the organization's public status pages. Each has id, slug, name, description, is_published (publicly visible now), history_days, created_at, groups (each with name, position and its components) and ungrouped_components. Use it to find a page's id before posting an incident on it. For one page use get_status_page; pages are created and edited only in the dashboard. Returns a page of results: items plus total, page, size and pages.
| Argument | Type | Description |
|---|---|---|
| page | integer | Page number, 1 or more; default 1. |
| size | integer | Rows per page, 1 to 100; default 25. Keep paging until you have seen total rows. |
| sort | string | created_at (the default, newest first) or name. |
{
"type": "object",
"required": [],
"properties": {
"page": {
"type": "integer",
"minimum": 1,
"description": "Page number, 1 or more; default 1."
},
"size": {
"type": "integer",
"maximum": 100,
"minimum": 1,
"description": "Rows per page, 1 to 100; default 25. Keep paging until you have seen total rows."
},
"sort": {
"enum": [
"name",
"created_at"
],
"type": "string",
"description": "created_at (the default, newest first) or name."
}
},
"additionalProperties": false
}List the tags used in the organization, most-used first, each with monitor_count and heartbeat_count, as items. Call it before tagging anything so you reuse an existing name instead of inventing a near-duplicate. Not a filter: to find the monitors or heartbeats carrying a tag, pass tag to list_monitors or list_heartbeats. Returns every tag at once, not a page.
| Argument | Type | Description |
|---|---|---|
| kind | string | monitor or heartbeat lists only the tags that kind of item uses; all (the default) lists both. Both counts are reported either way. |
{
"type": "object",
"required": [],
"properties": {
"kind": {
"enum": [
"monitor",
"heartbeat",
"all"
],
"type": "string",
"description": "monitor or heartbeat lists only the tags that kind of item uses; all (the default) lists both. Both counts are reported either way."
}
},
"additionalProperties": false
}Pause one heartbeat: it is no longer reported late, pings to its ping_url are accepted but ignored, and its open incident closes without an alert. Use it when the job is stopped on purpose, such as a runner taken out of service; resume_heartbeat undoes it, while delete_heartbeat removes the heartbeat for good. Maintenance windows do not cover heartbeats. Requires a key with the write scope. Pausing a paused heartbeat changes nothing. Returns the heartbeat.
| Argument | Type | Description |
|---|---|---|
| heartbeat_id* | string | The heartbeat's uuid from list_heartbeats. An id that is not a heartbeat of this organization, or a deleted one, is a 404. |
{
"type": "object",
"required": [
"heartbeat_id"
],
"properties": {
"heartbeat_id": {
"type": "string",
"format": "uuid",
"description": "The heartbeat's uuid from list_heartbeats. An id that is not a heartbeat of this organization, or a deleted one, is a 404."
}
},
"additionalProperties": false
}Stop checking one monitor without deleting it: its status becomes PAUSED and an open incident on it closes without a recovery alert. Use it when the user wants a monitor off for an open-ended time. For a planned deploy or reboot prefer create_maintenance_window, which keeps checking but silences alerts; for many monitors use bulk_pause_monitors; to restart checks use resume_monitor. Requires a key with the write scope. Pausing a paused monitor changes nothing. Returns the monitor in the get_monitor shape.
| Argument | Type | Description |
|---|---|---|
| monitor_id* | string | The monitor's uuid from list_monitors. An id that is not a monitor of this organization, or a deleted one, is a 404. |
{
"type": "object",
"required": [
"monitor_id"
],
"properties": {
"monitor_id": {
"type": "string",
"format": "uuid",
"description": "The monitor's uuid from list_monitors. An id that is not a monitor of this organization, or a deleted one, is a 404."
}
},
"additionalProperties": false
}Add a message to an existing status page incident and set its status; RESOLVED closes it. It is public and emails the page's confirmed subscribers: confirm the wording with the user before calling. Every confirmed subscriber gets the update, except that resolving an incident they were never told about stays silent. To open a new incident use create_status_page_incident; to fix only the title, silently, use update_status_page_incident. Requires a key with the write scope. A resolved incident accepts only further RESOLVED updates; any other status is a 409. Returns the incident. Example: {"page_id": "<id from list_status_pages>", "incident_id": "<id from list_status_page_incidents>", "status": "RESOLVED", "body": "Fixed; error rates are back to normal."}
| Argument | Type | Description |
|---|---|---|
| body* | string | The message subscribers read, 1 to 5000 characters. |
| status* | string | INVESTIGATING, IDENTIFIED, MONITORING or RESOLVED; RESOLVED closes the incident on the page. |
| page_id* | string | The status page's uuid from list_status_pages. An id that is not a status page of this organization is a 404. |
| incident_id* | string | The status page incident's uuid from list_status_page_incidents (not from list_incidents). An unknown id is a 404. |
{
"type": "object",
"required": [
"page_id",
"incident_id",
"status",
"body"
],
"properties": {
"body": {
"type": "string",
"maxLength": 5000,
"minLength": 1,
"description": "The message subscribers read, 1 to 5000 characters."
},
"status": {
"enum": [
"INVESTIGATING",
"IDENTIFIED",
"MONITORING",
"RESOLVED"
],
"type": "string",
"description": "INVESTIGATING, IDENTIFIED, MONITORING or RESOLVED; RESOLVED closes the incident on the page."
},
"page_id": {
"type": "string",
"format": "uuid",
"description": "The status page's uuid from list_status_pages. An id that is not a status page of this organization is a 404."
},
"incident_id": {
"type": "string",
"format": "uuid",
"description": "The status page incident's uuid from list_status_page_incidents (not from list_incidents). An unknown id is a 404."
}
},
"additionalProperties": false
}Compute the next three occurrences, each with start and end, that a schedule would produce, without saving anything. Use it to check a recurring schedule before create_maintenance_window or update_maintenance_window; for a saved window's occurrences use get_maintenance_window. Requires a key with the write scope. An invalid schedule is a 422 with the reason. Returns an array. Example: {"dtstart": "2026-10-18T03:00:00", "duration_minutes": 60, "rrule": "FREQ=WEEKLY;BYDAY=SU", "timezone": "Europe/Berlin"}
| Argument | Type | Description |
|---|---|---|
| rrule | string | Repeats the window: one iCal RRULE without the RRULE: prefix, at most 1024 characters, for example FREQ=WEEKLY;BYDAY=SU;BYHOUR=3. Hourly, minutely and secondly frequencies are refused. Omit it for a one-time window. |
| dtstart* | string | The first start, an ISO 8601 date-time in the wall-clock time of timezone, for example 2026-10-14T22:00:00. A UTC offset or Z is a 422. |
| timezone | string | IANA zone name such as Europe/Berlin, at most 64 characters. The schedule repeats in it, so a weekly 03:00 stays 03:00 across daylight-saving changes. Defaults to the organization's timezone. |
| duration_minutes* | integer | How long each occurrence lasts, 1 to 40320 minutes. |
{
"type": "object",
"required": [
"dtstart",
"duration_minutes"
],
"properties": {
"rrule": {
"type": "string",
"maxLength": 1024,
"description": "Repeats the window: one iCal RRULE without the RRULE: prefix, at most 1024 characters, for example FREQ=WEEKLY;BYDAY=SU;BYHOUR=3. Hourly, minutely and secondly frequencies are refused. Omit it for a one-time window."
},
"dtstart": {
"type": "string",
"format": "date-time",
"description": "The first start, an ISO 8601 date-time in the wall-clock time of timezone, for example 2026-10-14T22:00:00. A UTC offset or Z is a 422."
},
"timezone": {
"type": "string",
"maxLength": 64,
"description": "IANA zone name such as Europe/Berlin, at most 64 characters. The schedule repeats in it, so a weekly 03:00 stays 03:00 across daylight-saving changes. Defaults to the organization's timezone."
},
"duration_minutes": {
"type": "integer",
"maximum": 40320,
"description": "How long each occurrence lasts, 1 to 40320 minutes.",
"exclusiveMinimum": 0
}
},
"additionalProperties": false
}Rename a tag on every monitor and heartbeat that carries it; an item that already has the new name keeps one copy. Use it to fix a naming scheme across the organization; to change the tags of chosen monitors only use bulk_update_monitor_tags, and to drop a tag use delete_tag. Requires a key with the write scope. A tag nothing carries is not an error (both counts are 0). Returns from, to, monitors_updated and heartbeats_updated. Example: {"from": "prod", "to": "env:prod"}
| Argument | Type | Description |
|---|---|---|
| to* | string | The new name. Each tag is trimmed, lowercased and deduplicated on write, at most 50 characters of a-z, 0-9 and : _ . / -, starting with a letter or digit. Reuse names from list_tags rather than inventing near-duplicates. |
| from* | string | The existing tag, as list_tags shows it. |
{
"type": "object",
"required": [
"from",
"to"
],
"properties": {
"to": {
"type": "string",
"minLength": 1,
"description": "The new name. Each tag is trimmed, lowercased and deduplicated on write, at most 50 characters of a-z, 0-9 and : _ . / -, starting with a letter or digit. Reuse names from list_tags rather than inventing near-duplicates."
},
"from": {
"type": "string",
"minLength": 1,
"description": "The existing tag, as list_tags shows it."
}
},
"additionalProperties": false
}Resume one paused heartbeat: it goes back to PENDING with no deadline, so it is not reported late until its next success ping restarts the clock. Use it once the job runs again; to change its timing use update_heartbeat. There is no bulk resume for heartbeats. Requires a key with the write scope. Resuming one that is not paused resets it to PENDING the same way. Returns the heartbeat.
| Argument | Type | Description |
|---|---|---|
| heartbeat_id* | string | The heartbeat's uuid from list_heartbeats. An id that is not a heartbeat of this organization, or a deleted one, is a 404. |
{
"type": "object",
"required": [
"heartbeat_id"
],
"properties": {
"heartbeat_id": {
"type": "string",
"format": "uuid",
"description": "The heartbeat's uuid from list_heartbeats. An id that is not a heartbeat of this organization, or a deleted one, is a 404."
}
},
"additionalProperties": false
}Restart checks on one paused monitor: its status goes back to PENDING and it is checked on the scheduler's next tick. For many monitors use bulk_resume_monitors; to check a running monitor right away use check_now. Requires a key with the write scope. Resuming an active monitor is a no-op success. A 403 means the organization already has as many active monitors as its plan allows: pause or delete another one first. Returns the monitor in the get_monitor shape.
| Argument | Type | Description |
|---|---|---|
| monitor_id* | string | The monitor's uuid from list_monitors. An id that is not a monitor of this organization, or a deleted one, is a 404. |
{
"type": "object",
"required": [
"monitor_id"
],
"properties": {
"monitor_id": {
"type": "string",
"format": "uuid",
"description": "The monitor's uuid from list_monitors. An id that is not a monitor of this organization, or a deleted one, is a 404."
}
},
"additionalProperties": false
}Change one heartbeat's name, timing, channels or tags; only the fields you pass change. A new period or grace applies from the next ping: the current deadline (overdue_at) is not recomputed. To stop or restart late alerts use pause_heartbeat or resume_heartbeat instead; ping_url never changes. Requires a key with the write scope. An invalid argument is a 422 naming it; never send null. Returns the heartbeat. Example: {"heartbeat_id": "<id from list_heartbeats>", "grace_seconds": 7200}
| Argument | Type | Description |
|---|---|---|
| name | string | Display name, 1 to 255 characters. |
| tags | array | Replaces the whole tag list when passed, at most 10; [] clears it; omit it to keep the tags. Each tag is trimmed, lowercased and deduplicated on write, at most 50 characters of a-z, 0-9 and : _ . / -, starting with a letter or digit. Reuse names from list_tags rather than inventing near-duplicates. |
| channel_ids | array | Notification channel uuids from list_channels. When passed it replaces the whole binding; [] clears it, after which every verified, active channel is alerted; omit it to keep the binding. An id that is not an active channel of this organization is a 404. |
| heartbeat_id* | string | The heartbeat's uuid from list_heartbeats. An id that is not a heartbeat of this organization, or a deleted one, is a 404. |
| grace_seconds | integer | Extra seconds of silence tolerated after the period before the heartbeat is reported late, 0 to 2592000; it may exceed the period. |
Change one maintenance window's name, schedule, scope or monitors, or switch it off with enabled false; only the fields you pass change. While a window is in effect its monitors keep being checked, but no incident opens and no alert goes out. A recurring window cannot become one-time: delete it with delete_maintenance_window and create a new one. Check a new schedule with preview_maintenance_window first. Requires a key with the write scope. An invalid argument is a 422 and a resulting schedule that is invalid (for example a bad rrule) a 406, each with the reason. Returns the window. Example: {"window_id": "<id from list_maintenance_windows>", "enabled": false}
| Argument | Type | Description |
|---|---|---|
| name | string | Display name, 1 to 255 characters. |
| rrule | string | Repeats the window: one iCal RRULE without the RRULE: prefix, at most 1024 characters, for example FREQ=WEEKLY;BYDAY=SU;BYHOUR=3. Hourly, minutely and secondly frequencies are refused. |
| scope | string | ORG covers every monitor of the organization and ignores monitor_ids; MONITOR covers only monitor_ids. |
| dtstart | string | The first start, an ISO 8601 date-time in the wall-clock time of timezone, for example 2026-10-14T22:00:00. A UTC offset or Z is a 422. |
| enabled | boolean | false keeps the window but stops it silencing alerts. |
| timezone | string | IANA zone name such as Europe/Berlin, at most 64 characters. The schedule repeats in it, so a weekly 03:00 stays 03:00 across daylight-saving changes. |
Change one monitor's settings and return it in the get_monitor shape. Only the fields you pass change, and at least one besides monitor_id is required (422 otherwise); type and target never change (create a new monitor instead). Not for pausing or resuming (pause_monitor, resume_monitor), for many monitors at once (bulk_update_monitors) or for tags alone across monitors (bulk_update_monitor_tags). Requires a key with the write scope. Plan limits are checked against the result, as on create_monitor (only moving a monitor below 60 seconds takes a fast slot): breaking one is a 403 naming it. Port-list rules are checked against the result too, so changing only the interval or regions of a port-list TCP monitor can be a 422. Omit a field to keep it; never send null. Example: {"monitor_id": "<id from list_monitors>", "interval_seconds": 600}
| Argument | Type | Description |
|---|---|---|
| name | string | Display name, 1 to 255 characters. |
| tags | array | Replaces the whole tag list when passed, at most 10; [] clears it; omit it to keep the tags. Each tag is trimmed, lowercased and deduplicated on write, at most 50 characters of a-z, 0-9 and : _ . / -, starting with a letter or digit. Reuse names from list_tags rather than inventing near-duplicates. |
| config | object | Replaces the monitor's whole config, not just the keys you send: a field of the type's schema you leave out resets to its default (read the current one with get_monitor first). Sending only port on a TCP monitor whose expected_state is closed resets it to open. A port-list TCP monitor cannot switch to a single port (422). An object whose fields depend on type, all optional unless stated. HTTP: method (GET, HEAD, POST, PUT, PATCH or DELETE, any case; default GET); expected_status_codes (non-empty list of codes 100 to 599, default [200]); keyword (case-sensitive substring the body must contain, at most 1024 characters, not with HEAD); follow_redirects (default true); verify_ssl (default true; false skips TLS verification, so the monitor can never report TLS_INVALID); request_headers (object of name to value: at most 20, a name at most 128 characters of letters, digits and ! # $ % & ' * + - . ^ _ ` | ~, a value at most 1024 bytes of printable ASCII, space or tab, the whole object at most 8192 bytes as JSON; names differing only in case, and Host, Content-Length, Transfer-Encoding, Connection, Upgrade, TE, Trailer, Keep-Alive, CF-Connecting-IP or any Proxy-* name, are a 422); request_body (string, at most 8192 bytes as UTF-8, needs POST, PUT, PATCH or DELETE). Reads return every request_headers value as the literal __FETTLE_UNCHANGED__; writing that literal back in an update keeps the stored value (on create it is a 422). PING: packet_count (1 to 20, default 4). SSL: warn_before_days (1 to 365, default 14) and port (1 to 65535, default 443). TCP: either port (1 to 65535) with expected_state "open" (default) or "closed", or ports (1 to 20 distinct ports that must all be closed, which needs expected_state "closed", exactly one region and interval_seconds of at least 300; a check fails when any of them accepts a connection, and the monitor goes DOWN once confirmation_threshold failures confirm it). Both port and ports, or neither, is a 422; expected_state values are lowercase. DNS: record_type (A, AAAA, CNAME, MX, TXT or NS; required) and expected_value (at most 2048 characters). With expected_value one answer must equal it (case-insensitive, ignoring surrounding spaces and a trailing dot; exact for TXT); without it any answer passes. MX answers are the mail host names without priority (mail.example.com, not 10 mail.example.com). A and AAAA look up every address of the host, IPv4 and IPv6 alike, and any one may match; write an IPv6 address in its shortest form. |
Change a status page incident's title without telling anyone: nothing is sent to subscribers. To change its status or add a message use post_status_page_update. Requires a key with the write scope. Returns the incident. Example: {"page_id": "<id from list_status_pages>", "incident_id": "<id from list_status_page_incidents>", "title": "API errors in eu-central"}
| Argument | Type | Description |
|---|---|---|
| title* | string | The new headline, 1 to 255 characters. |
| page_id* | string | The status page's uuid from list_status_pages. An id that is not a status page of this organization is a 404. |
| incident_id* | string | The status page incident's uuid from list_status_page_incidents (not from list_incidents). An unknown id is a 404. |
{
"type": "object",
"required": [
"page_id",
"incident_id",
"title"
],
"properties": {
"title": {
"type": "string",
"maxLength": 255,
"minLength": 1,
"description": "The new headline, 1 to 255 characters."
},
"page_id": {
"type": "string",
"format": "uuid",
"description": "The status page's uuid from list_status_pages. An id that is not a status page of this organization is a 404."
},
"incident_id": {
"type": "string",
"format": "uuid",
"description": "The status page incident's uuid from list_status_page_incidents (not from list_incidents). An unknown id is a 404."
}
},
"additionalProperties": false
}| target* | string | What to check, up to 2048 characters. HTTP: an http:// or https:// URL (https:// is added when the scheme is missing; port, path and query are kept). PING, SSL, TCP and DNS: a bare public hostname or IP address with no :port and no path (the port goes in config): host:port is a 422, and a full URL is cut down to its host, losing its port. Hostnames are ASCII (punycode for internationalized domains). localhost, *.localhost and addresses that are not public (private, loopback, link-local, CGNAT 100.64.0.0/10, reserved, unspecified) are a 422. |
{
"type": "object",
"required": [
"type",
"target"
],
"properties": {
"type": {
"enum": [
"HTTP",
"PING",
"SSL",
"TCP",
"DNS"
],
"type": "string",
"description": "HTTP (a URL answers with an expected status), PING (a host answers ICMP), SSL (a certificate is valid and not about to expire), TCP (a port is open, or a list of ports is closed) or DNS (a record resolves, optionally to an expected value)."
},
"config": {
"type": "object",
"description": "As in create_monitor; TCP and DNS need it. The region, interval and update rules there do not apply to a one-off probe. An object whose fields depend on type, all optional unless stated. HTTP: method (GET, HEAD, POST, PUT, PATCH or DELETE, any case; default GET); expected_status_codes (non-empty list of codes 100 to 599, default [200]); keyword (case-sensitive substring the body must contain, at most 1024 characters, not with HEAD); follow_redirects (default true); verify_ssl (default true; false skips TLS verification, so the monitor can never report TLS_INVALID); request_headers (object of name to value: at most 20, a name at most 128 characters of letters, digits and ! # $ % & ' * + - . ^ _ ` | ~, a value at most 1024 bytes of printable ASCII, space or tab, the whole object at most 8192 bytes as JSON; names differing only in case, and Host, Content-Length, Transfer-Encoding, Connection, Upgrade, TE, Trailer, Keep-Alive, CF-Connecting-IP or any Proxy-* name, are a 422); request_body (string, at most 8192 bytes as UTF-8, needs POST, PUT, PATCH or DELETE). Reads return every request_headers value as the literal __FETTLE_UNCHANGED__; writing that literal back in an update keeps the stored value (on create it is a 422). PING: packet_count (1 to 20, default 4). SSL: warn_before_days (1 to 365, default 14) and port (1 to 65535, default 443). TCP: either port (1 to 65535) with expected_state \"open\" (default) or \"closed\", or ports (1 to 20 distinct ports that must all be closed, which needs expected_state \"closed\", exactly one region and interval_seconds of at least 300; a check fails when any of them accepts a connection, and the monitor goes DOWN once confirmation_threshold failures confirm it). Both port and ports, or neither, is a 422; expected_state values are lowercase. DNS: record_type (A, AAAA, CNAME, MX, TXT or NS; required) and expected_value (at most 2048 characters). With expected_value one answer must equal it (case-insensitive, ignoring surrounding spaces and a trailing dot; exact for TXT); without it any answer passes. MX answers are the mail host names without priority (mail.example.com, not 10 mail.example.com). A and AAAA look up every address of the host, IPv4 and IPv6 alike, and any one may match; write an IPv6 address in its shortest form.",
"additionalProperties": true
},
"target": {
"type": "string",
"maxLength": 2048,
"minLength": 1,
"description": "What to check, up to 2048 characters. HTTP: an http:// or https:// URL (https:// is added when the scheme is missing; port, path and query are kept). PING, SSL, TCP and DNS: a bare public hostname or IP address with no :port and no path (the port goes in config): host:port is a 422, and a full URL is cut down to its host, losing its port. Hostnames are ASCII (punycode for internationalized domains). localhost, *.localhost and addresses that are not public (private, loopback, link-local, CGNAT 100.64.0.0/10, reserved, unspecified) are a 422."
}
},
"additionalProperties": false
}| integer |
| How often the job runs, 60 to 2592000 seconds. To ping every N seconds and be alerted after T seconds of silence, set period_seconds = N and grace_seconds = T - N (T at least N). |
| notification_cooldown_seconds | integer | 60 to 86400 seconds; 1800 when not set on create. Only holds back a duplicate first alert for the same incident within that window. Repeat reminders while an incident stays open and unacknowledged follow the plan's renotify_interval_seconds and renotify_max_count (get_plan_usage; 0 means no reminders) instead, and the recovery alert is never held back. |
{
"type": "object",
"required": [
"name",
"period_seconds",
"grace_seconds"
],
"properties": {
"name": {
"type": "string",
"maxLength": 255,
"minLength": 1,
"description": "Display name, 1 to 255 characters."
},
"tags": {
"type": "array",
"items": {
"type": "string"
},
"description": "Flat labels for grouping and filtering, for example env:prod or service:api, at most 10. Each tag is trimmed, lowercased and deduplicated on write, at most 50 characters of a-z, 0-9 and : _ . / -, starting with a letter or digit. Reuse names from list_tags rather than inventing near-duplicates."
},
"channel_ids": {
"type": "array",
"items": {
"type": "string",
"format": "uuid"
},
"description": "Notification channel uuids from list_channels. Omit it or pass [] to alert every verified, active channel of the organization. An id that is not an active channel of this organization is a 404."
},
"grace_seconds": {
"type": "integer",
"maximum": 2592000,
"minimum": 0,
"description": "Extra seconds of silence tolerated after the period before the heartbeat is reported late, 0 to 2592000; it may exceed the period."
},
"period_seconds": {
"type": "integer",
"maximum": 2592000,
"minimum": 60,
"description": "How often the job runs, 60 to 2592000 seconds. To ping every N seconds and be alerted after T seconds of silence, set period_seconds = N and grace_seconds = T - N (T at least N)."
},
"notification_cooldown_seconds": {
"type": "integer",
"maximum": 86400,
"minimum": 60,
"description": "60 to 86400 seconds; 1800 when not set on create. Only holds back a duplicate first alert for the same incident within that window. Repeat reminders while an incident stays open and unacknowledged follow the plan's renotify_interval_seconds and renotify_max_count (get_plan_usage; 0 means no reminders) instead, and the recovery alert is never held back."
}
},
"additionalProperties": false
}| timezone |
| string |
| IANA zone name such as Europe/Berlin, at most 64 characters. The schedule repeats in it, so a weekly 03:00 stays 03:00 across daylight-saving changes. Defaults to the organization's timezone. |
| monitor_ids | array | Monitor uuids from list_monitors, at least one when scope is MONITOR. An id that is not a monitor of this organization is a 404. |
| duration_minutes* | integer | How long each occurrence lasts, 1 to 40320 minutes. |
{
"type": "object",
"required": [
"name",
"scope",
"dtstart",
"duration_minutes"
],
"properties": {
"name": {
"type": "string",
"maxLength": 255,
"minLength": 1,
"description": "Display name, 1 to 255 characters."
},
"rrule": {
"type": "string",
"maxLength": 1024,
"description": "Repeats the window: one iCal RRULE without the RRULE: prefix, at most 1024 characters, for example FREQ=WEEKLY;BYDAY=SU;BYHOUR=3. Hourly, minutely and secondly frequencies are refused. Omit it for a one-time window."
},
"scope": {
"enum": [
"MONITOR",
"ORG"
],
"type": "string",
"description": "ORG covers every monitor of the organization and ignores monitor_ids; MONITOR covers only monitor_ids."
},
"dtstart": {
"type": "string",
"format": "date-time",
"description": "The first start, an ISO 8601 date-time in the wall-clock time of timezone, for example 2026-10-14T22:00:00. A UTC offset or Z is a 422."
},
"enabled": {
"type": "boolean",
"description": "Default true; false stores the window without effect."
},
"timezone": {
"type": "string",
"maxLength": 64,
"description": "IANA zone name such as Europe/Berlin, at most 64 characters. The schedule repeats in it, so a weekly 03:00 stays 03:00 across daylight-saving changes. Defaults to the organization's timezone."
},
"monitor_ids": {
"type": "array",
"items": {
"type": "string",
"format": "uuid"
},
"description": "Monitor uuids from list_monitors, at least one when scope is MONITOR. An id that is not a monitor of this organization is a 404."
},
"duration_minutes": {
"type": "integer",
"maximum": 40320,
"description": "How long each occurrence lasts, 1 to 40320 minutes.",
"exclusiveMinimum": 0
}
},
"additionalProperties": false
}| target* | string | What to check, up to 2048 characters. HTTP: an http:// or https:// URL (https:// is added when the scheme is missing; port, path and query are kept). PING, SSL, TCP and DNS: a bare public hostname or IP address with no :port and no path (the port goes in config): host:port is a 422, and a full URL is cut down to its host, losing its port. Hostnames are ASCII (punycode for internationalized domains). localhost, *.localhost and addresses that are not public (private, loopback, link-local, CGNAT 100.64.0.0/10, reserved, unspecified) are a 422. Cannot be changed after create. |
| regions* | array | One or more distinct region codes: the plan's choices are limits.allowed_regions in get_plan_usage. An unknown code, or more regions than max_regions, is a 403. SSL, DNS and TCP monitors with a ports list take exactly one region (422 otherwise). Unless the user names regions, pass every code in limits.allowed_regions, up to the plan's max_regions. |
| is_active | boolean | Default true. false creates it paused: no check runs until resume_monitor. |
| channel_ids | array | Notification channel uuids from list_channels. Omit it or pass [] to alert every verified, active channel of the organization. An id that is not an active channel of this organization is a 404. |
| timeout_seconds* | integer | Seconds one check may take before it fails, 1 to 120. |
| interval_seconds* | integer | Seconds between checks, 30 to 86400. The plan sets a minimum (limits.min_interval_seconds) and caps how many monitors may check more often than every 60 seconds (max_fast_monitors); breaking either is a 403 naming the limit. Tell the user the interval you chose. |
| confirmation_threshold | integer | How many checks in a row must fail in every region before the monitor goes DOWN and an incident opens, 1 to 10; 3 when not set on create. |
| notification_cooldown_seconds | integer | 60 to 86400 seconds; 1800 when not set on create. Only holds back a duplicate first alert for the same incident within that window. Repeat reminders while an incident stays open and unacknowledged follow the plan's renotify_interval_seconds and renotify_max_count (get_plan_usage; 0 means no reminders) instead, and the recovery alert is never held back. |
{
"type": "object",
"required": [
"name",
"type",
"target",
"interval_seconds",
"timeout_seconds",
"regions"
],
"properties": {
"name": {
"type": "string",
"maxLength": 255,
"minLength": 1,
"description": "Display name, 1 to 255 characters."
},
"tags": {
"type": "array",
"items": {
"type": "string"
},
"description": "Flat labels for grouping and filtering, for example env:prod or service:api, at most 10. Each tag is trimmed, lowercased and deduplicated on write, at most 50 characters of a-z, 0-9 and : _ . / -, starting with a letter or digit. Reuse names from list_tags rather than inventing near-duplicates."
},
"type": {
"enum": [
"HTTP",
"PING",
"SSL",
"TCP",
"DNS"
],
"type": "string",
"description": "HTTP (a URL answers with an expected status), PING (a host answers ICMP), SSL (a certificate is valid and not about to expire), TCP (a port is open, or a list of ports is closed) or DNS (a record resolves, optionally to an expected value). Cannot be changed after create."
},
"config": {
"type": "object",
"description": "An object whose fields depend on type, all optional unless stated. HTTP: method (GET, HEAD, POST, PUT, PATCH or DELETE, any case; default GET); expected_status_codes (non-empty list of codes 100 to 599, default [200]); keyword (case-sensitive substring the body must contain, at most 1024 characters, not with HEAD); follow_redirects (default true); verify_ssl (default true; false skips TLS verification, so the monitor can never report TLS_INVALID); request_headers (object of name to value: at most 20, a name at most 128 characters of letters, digits and ! # $ % & ' * + - . ^ _ ` | ~, a value at most 1024 bytes of printable ASCII, space or tab, the whole object at most 8192 bytes as JSON; names differing only in case, and Host, Content-Length, Transfer-Encoding, Connection, Upgrade, TE, Trailer, Keep-Alive, CF-Connecting-IP or any Proxy-* name, are a 422); request_body (string, at most 8192 bytes as UTF-8, needs POST, PUT, PATCH or DELETE). Reads return every request_headers value as the literal __FETTLE_UNCHANGED__; writing that literal back in an update keeps the stored value (on create it is a 422). PING: packet_count (1 to 20, default 4). SSL: warn_before_days (1 to 365, default 14) and port (1 to 65535, default 443). TCP: either port (1 to 65535) with expected_state \"open\" (default) or \"closed\", or ports (1 to 20 distinct ports that must all be closed, which needs expected_state \"closed\", exactly one region and interval_seconds of at least 300; a check fails when any of them accepts a connection, and the monitor goes DOWN once confirmation_threshold failures confirm it). Both port and ports, or neither, is a 422; expected_state values are lowercase. DNS: record_type (A, AAAA, CNAME, MX, TXT or NS; required) and expected_value (at most 2048 characters). With expected_value one answer must equal it (case-insensitive, ignoring surrounding spaces and a trailing dot; exact for TXT); without it any answer passes. MX answers are the mail host names without priority (mail.example.com, not 10 mail.example.com). A and AAAA look up every address of the host, IPv4 and IPv6 alike, and any one may match; write an IPv6 address in its shortest form. Omit it for HTTP, PING or SSL to take the defaults; a TCP or DNS monitor without it is a 422.",
"additionalProperties": true
},
"target": {
"type": "string",
"maxLength": 2048,
"minLength": 1,
"description": "What to check, up to 2048 characters. HTTP: an http:// or https:// URL (https:// is added when the scheme is missing; port, path and query are kept). PING, SSL, TCP and DNS: a bare public hostname or IP address with no :port and no path (the port goes in config): host:port is a 422, and a full URL is cut down to its host, losing its port. Hostnames are ASCII (punycode for internationalized domains). localhost, *.localhost and addresses that are not public (private, loopback, link-local, CGNAT 100.64.0.0/10, reserved, unspecified) are a 422. Cannot be changed after create."
},
"regions": {
"type": "array",
"items": {
"type": "string"
},
"minItems": 1,
"description": "One or more distinct region codes: the plan's choices are limits.allowed_regions in get_plan_usage. An unknown code, or more regions than max_regions, is a 403. SSL, DNS and TCP monitors with a ports list take exactly one region (422 otherwise). Unless the user names regions, pass every code in limits.allowed_regions, up to the plan's max_regions."
},
"is_active": {
"type": "boolean",
"description": "Default true. false creates it paused: no check runs until resume_monitor."
},
"channel_ids": {
"type": "array",
"items": {
"type": "string",
"format": "uuid"
},
"description": "Notification channel uuids from list_channels. Omit it or pass [] to alert every verified, active channel of the organization. An id that is not an active channel of this organization is a 404."
},
"timeout_seconds": {
"type": "integer",
"maximum": 120,
"minimum": 1,
"description": "Seconds one check may take before it fails, 1 to 120."
},
"interval_seconds": {
"type": "integer",
"maximum": 86400,
"minimum": 30,
"description": "Seconds between checks, 30 to 86400. The plan sets a minimum (limits.min_interval_seconds) and caps how many monitors may check more often than every 60 seconds (max_fast_monitors); breaking either is a 403 naming the limit. Tell the user the interval you chose."
},
"confirmation_threshold": {
"type": "integer",
"maximum": 10,
"minimum": 1,
"description": "How many checks in a row must fail in every region before the monitor goes DOWN and an incident opens, 1 to 10; 3 when not set on create."
},
"notification_cooldown_seconds": {
"type": "integer",
"maximum": 86400,
"minimum": 60,
"description": "60 to 86400 seconds; 1800 when not set on create. Only holds back a duplicate first alert for the same incident within that window. Repeat reminders while an incident stays open and unacknowledged follow the plan's renotify_interval_seconds and renotify_max_count (get_plan_usage; 0 means no reminders) instead, and the recovery alert is never held back."
}
},
"additionalProperties": false
}{
"type": "object",
"required": [],
"properties": {
"q": {
"type": "string",
"maxLength": 255,
"description": "Case-insensitive substring of the name, up to 255 characters."
},
"tag": {
"type": "array",
"items": {
"type": "string",
"maxLength": 50
},
"maxItems": 10,
"description": "Keep items carrying these tags: a JSON array of up to 10 tags, each up to 50 characters, lowercased before matching. tag_match decides whether all or any of them must match."
},
"page": {
"type": "integer",
"minimum": 1,
"description": "Page number, 1 or more; default 1."
},
"size": {
"type": "integer",
"maximum": 100,
"minimum": 1,
"description": "Rows per page, 1 to 100; default 25. Keep paging until you have seen total rows."
},
"sort": {
"enum": [
"status",
"name",
"created_at"
],
"type": "string",
"description": "status (the default: DOWN, PENDING, UP, PAUSED, newest first within each), name, or created_at (newest first)."
},
"tag_match": {
"enum": [
"all",
"any"
],
"type": "string",
"description": "all (the default): an item must carry every tag in tag. any: at least one of them."
}
},
"additionalProperties": false
}| period_days | integer | Keep incidents active within the last N days, 1 to 365. An open incident always qualifies, so with status open it changes nothing: use status resolved, or omit status, to see the window take effect. |
{
"type": "object",
"required": [],
"properties": {
"page": {
"type": "integer",
"minimum": 1,
"description": "Page number, 1 or more; default 1."
},
"size": {
"type": "integer",
"maximum": 100,
"minimum": 1,
"description": "Rows per page, 1 to 100; default 25. Keep paging until you have seen total rows."
},
"cause": {
"enum": [
"TIMEOUT",
"DNS_FAILURE",
"CONNECTION_REFUSED",
"TLS_INVALID",
"HTTP_4XX",
"HTTP_5XX",
"HTTP_UNEXPECTED_STATUS",
"KEYWORD_MISSING",
"TOO_MANY_REDIRECTS",
"TARGET_BLOCKED",
"PORT_STATE_MISMATCH",
"INVALID_TARGET",
"HEARTBEAT_MISSED",
"HEARTBEAT_FAILED",
"UNKNOWN"
],
"type": "string",
"description": "Keep one failure cause, such as TIMEOUT, HTTP_5XX or HEARTBEAT_MISSED."
},
"status": {
"enum": [
"open",
"resolved"
],
"type": "string",
"description": "open or resolved (lowercase); omit it for both."
},
"monitor_id": {
"type": "string",
"format": "uuid",
"description": "Keep one monitor's incidents: its uuid from list_monitors."
},
"period_days": {
"type": "integer",
"maximum": 365,
"minimum": 1,
"description": "Keep incidents active within the last N days, 1 to 365. An open incident always qualifies, so with status open it changes nothing: use status resolved, or omit status, to see the window take effect."
}
},
"additionalProperties": false
}| Keep monitors of any of these types: a JSON array. |
| status | array | Keep monitors in any of these states: a JSON array. PENDING means no verdict yet. |
| tag_match | string | all (the default): an item must carry every tag in tag. any: at least one of them. |
{
"type": "object",
"required": [],
"properties": {
"q": {
"type": "string",
"maxLength": 255,
"description": "Case-insensitive substring of the name or the target, up to 255 characters."
},
"tag": {
"type": "array",
"items": {
"type": "string",
"maxLength": 50
},
"maxItems": 10,
"description": "Keep items carrying these tags: a JSON array of up to 10 tags, each up to 50 characters, lowercased before matching. tag_match decides whether all or any of them must match."
},
"page": {
"type": "integer",
"minimum": 1,
"description": "Page number, 1 or more; default 1."
},
"size": {
"type": "integer",
"maximum": 100,
"minimum": 1,
"description": "Rows per page, 1 to 100; default 25. Keep paging until you have seen total rows."
},
"sort": {
"enum": [
"status",
"up_first",
"name",
"newest",
"oldest",
"ssl_expiry"
],
"type": "string",
"description": "status (the default: PENDING, DOWN, DEGRADED, UP, PAUSED, then by name), up_first (healthy first), name, newest, oldest, or ssl_expiry (soonest-expiring certificates first; monitors with no known expiry, non-SSL types or not checked yet, last)."
},
"type": {
"type": "array",
"items": {
"enum": [
"HTTP",
"PING",
"SSL",
"TCP",
"DNS"
],
"type": "string"
},
"description": "Keep monitors of any of these types: a JSON array."
},
"status": {
"type": "array",
"items": {
"enum": [
"PENDING",
"UP",
"DEGRADED",
"DOWN",
"PAUSED"
],
"type": "string"
},
"description": "Keep monitors in any of these states: a JSON array. PENDING means no verdict yet."
},
"tag_match": {
"enum": [
"all",
"any"
],
"type": "string",
"description": "all (the default): an item must carry every tag in tag. any: at least one of them."
}
},
"additionalProperties": false
}| period_seconds |
| integer |
| How often the job runs, 60 to 2592000 seconds. To ping every N seconds and be alerted after T seconds of silence, set period_seconds = N and grace_seconds = T - N (T at least N). |
| notification_cooldown_seconds | integer | 60 to 86400 seconds; 1800 when not set on create. Only holds back a duplicate first alert for the same incident within that window. Repeat reminders while an incident stays open and unacknowledged follow the plan's renotify_interval_seconds and renotify_max_count (get_plan_usage; 0 means no reminders) instead, and the recovery alert is never held back. |
{
"type": "object",
"required": [
"heartbeat_id"
],
"properties": {
"name": {
"type": "string",
"maxLength": 255,
"minLength": 1,
"description": "Display name, 1 to 255 characters."
},
"tags": {
"type": "array",
"items": {
"type": "string"
},
"description": "Replaces the whole tag list when passed, at most 10; [] clears it; omit it to keep the tags. Each tag is trimmed, lowercased and deduplicated on write, at most 50 characters of a-z, 0-9 and : _ . / -, starting with a letter or digit. Reuse names from list_tags rather than inventing near-duplicates."
},
"channel_ids": {
"type": "array",
"items": {
"type": "string",
"format": "uuid"
},
"description": "Notification channel uuids from list_channels. When passed it replaces the whole binding; [] clears it, after which every verified, active channel is alerted; omit it to keep the binding. An id that is not an active channel of this organization is a 404."
},
"heartbeat_id": {
"type": "string",
"format": "uuid",
"description": "The heartbeat's uuid from list_heartbeats. An id that is not a heartbeat of this organization, or a deleted one, is a 404."
},
"grace_seconds": {
"type": "integer",
"maximum": 2592000,
"minimum": 0,
"description": "Extra seconds of silence tolerated after the period before the heartbeat is reported late, 0 to 2592000; it may exceed the period."
},
"period_seconds": {
"type": "integer",
"maximum": 2592000,
"minimum": 60,
"description": "How often the job runs, 60 to 2592000 seconds. To ping every N seconds and be alerted after T seconds of silence, set period_seconds = N and grace_seconds = T - N (T at least N)."
},
"notification_cooldown_seconds": {
"type": "integer",
"maximum": 86400,
"minimum": 60,
"description": "60 to 86400 seconds; 1800 when not set on create. Only holds back a duplicate first alert for the same incident within that window. Repeat reminders while an incident stays open and unacknowledged follow the plan's renotify_interval_seconds and renotify_max_count (get_plan_usage; 0 means no reminders) instead, and the recovery alert is never held back."
}
},
"additionalProperties": false
}| window_id* | string | The window's uuid from list_maintenance_windows. An id that is not a window of this organization is a 404. |
| monitor_ids | array | Replaces the list when passed. Monitor uuids from list_monitors, at least one when scope is MONITOR. An id that is not a monitor of this organization is a 404. |
| duration_minutes | integer | How long each occurrence lasts, 1 to 40320 minutes. |
{
"type": "object",
"required": [
"window_id"
],
"properties": {
"name": {
"type": "string",
"maxLength": 255,
"minLength": 1,
"description": "Display name, 1 to 255 characters."
},
"rrule": {
"type": "string",
"maxLength": 1024,
"description": "Repeats the window: one iCal RRULE without the RRULE: prefix, at most 1024 characters, for example FREQ=WEEKLY;BYDAY=SU;BYHOUR=3. Hourly, minutely and secondly frequencies are refused."
},
"scope": {
"enum": [
"MONITOR",
"ORG"
],
"type": "string",
"description": "ORG covers every monitor of the organization and ignores monitor_ids; MONITOR covers only monitor_ids."
},
"dtstart": {
"type": "string",
"format": "date-time",
"description": "The first start, an ISO 8601 date-time in the wall-clock time of timezone, for example 2026-10-14T22:00:00. A UTC offset or Z is a 422."
},
"enabled": {
"type": "boolean",
"description": "false keeps the window but stops it silencing alerts."
},
"timezone": {
"type": "string",
"maxLength": 64,
"description": "IANA zone name such as Europe/Berlin, at most 64 characters. The schedule repeats in it, so a weekly 03:00 stays 03:00 across daylight-saving changes."
},
"window_id": {
"type": "string",
"format": "uuid",
"description": "The window's uuid from list_maintenance_windows. An id that is not a window of this organization is a 404."
},
"monitor_ids": {
"type": "array",
"items": {
"type": "string",
"format": "uuid"
},
"description": "Replaces the list when passed. Monitor uuids from list_monitors, at least one when scope is MONITOR. An id that is not a monitor of this organization is a 404."
},
"duration_minutes": {
"type": "integer",
"maximum": 40320,
"description": "How long each occurrence lasts, 1 to 40320 minutes.",
"exclusiveMinimum": 0
}
},
"additionalProperties": false
}| regions | array | One or more distinct region codes: the plan's choices are limits.allowed_regions in get_plan_usage. An unknown code, or more regions than max_regions, is a 403. SSL, DNS and TCP monitors with a ports list take exactly one region (422 otherwise). |
| monitor_id* | string | The monitor's uuid from list_monitors. An id that is not a monitor of this organization, or a deleted one, is a 404. |
| channel_ids | array | Notification channel uuids from list_channels. When passed it replaces the whole binding; [] clears it, after which every verified, active channel is alerted; omit it to keep the binding. An id that is not an active channel of this organization is a 404. |
| badge_enabled | boolean | Turns the monitor's public status badge on or off. A monitor on a published status page has a badge even when it is false. |
| timeout_seconds | integer | Seconds one check may take before it fails, 1 to 120. |
| interval_seconds | integer | Seconds between checks, 30 to 86400. The plan sets a minimum (limits.min_interval_seconds) and caps how many monitors may check more often than every 60 seconds (max_fast_monitors); breaking either is a 403 naming the limit. |
| confirmation_threshold | integer | How many checks in a row must fail in every region before the monitor goes DOWN and an incident opens, 1 to 10; 3 when not set on create. |
| notification_cooldown_seconds | integer | 60 to 86400 seconds; 1800 when not set on create. Only holds back a duplicate first alert for the same incident within that window. Repeat reminders while an incident stays open and unacknowledged follow the plan's renotify_interval_seconds and renotify_max_count (get_plan_usage; 0 means no reminders) instead, and the recovery alert is never held back. |
{
"type": "object",
"required": [
"monitor_id"
],
"properties": {
"name": {
"type": "string",
"maxLength": 255,
"minLength": 1,
"description": "Display name, 1 to 255 characters."
},
"tags": {
"type": "array",
"items": {
"type": "string"
},
"description": "Replaces the whole tag list when passed, at most 10; [] clears it; omit it to keep the tags. Each tag is trimmed, lowercased and deduplicated on write, at most 50 characters of a-z, 0-9 and : _ . / -, starting with a letter or digit. Reuse names from list_tags rather than inventing near-duplicates."
},
"config": {
"type": "object",
"description": "Replaces the monitor's whole config, not just the keys you send: a field of the type's schema you leave out resets to its default (read the current one with get_monitor first). Sending only port on a TCP monitor whose expected_state is closed resets it to open. A port-list TCP monitor cannot switch to a single port (422). An object whose fields depend on type, all optional unless stated. HTTP: method (GET, HEAD, POST, PUT, PATCH or DELETE, any case; default GET); expected_status_codes (non-empty list of codes 100 to 599, default [200]); keyword (case-sensitive substring the body must contain, at most 1024 characters, not with HEAD); follow_redirects (default true); verify_ssl (default true; false skips TLS verification, so the monitor can never report TLS_INVALID); request_headers (object of name to value: at most 20, a name at most 128 characters of letters, digits and ! # $ % & ' * + - . ^ _ ` | ~, a value at most 1024 bytes of printable ASCII, space or tab, the whole object at most 8192 bytes as JSON; names differing only in case, and Host, Content-Length, Transfer-Encoding, Connection, Upgrade, TE, Trailer, Keep-Alive, CF-Connecting-IP or any Proxy-* name, are a 422); request_body (string, at most 8192 bytes as UTF-8, needs POST, PUT, PATCH or DELETE). Reads return every request_headers value as the literal __FETTLE_UNCHANGED__; writing that literal back in an update keeps the stored value (on create it is a 422). PING: packet_count (1 to 20, default 4). SSL: warn_before_days (1 to 365, default 14) and port (1 to 65535, default 443). TCP: either port (1 to 65535) with expected_state \"open\" (default) or \"closed\", or ports (1 to 20 distinct ports that must all be closed, which needs expected_state \"closed\", exactly one region and interval_seconds of at least 300; a check fails when any of them accepts a connection, and the monitor goes DOWN once confirmation_threshold failures confirm it). Both port and ports, or neither, is a 422; expected_state values are lowercase. DNS: record_type (A, AAAA, CNAME, MX, TXT or NS; required) and expected_value (at most 2048 characters). With expected_value one answer must equal it (case-insensitive, ignoring surrounding spaces and a trailing dot; exact for TXT); without it any answer passes. MX answers are the mail host names without priority (mail.example.com, not 10 mail.example.com). A and AAAA look up every address of the host, IPv4 and IPv6 alike, and any one may match; write an IPv6 address in its shortest form.",
"additionalProperties": true
},
"regions": {
"type": "array",
"items": {
"type": "string"
},
"minItems": 1,
"description": "One or more distinct region codes: the plan's choices are limits.allowed_regions in get_plan_usage. An unknown code, or more regions than max_regions, is a 403. SSL, DNS and TCP monitors with a ports list take exactly one region (422 otherwise)."
},
"monitor_id": {
"type": "string",
"format": "uuid",
"description": "The monitor's uuid from list_monitors. An id that is not a monitor of this organization, or a deleted one, is a 404."
},
"channel_ids": {
"type": "array",
"items": {
"type": "string",
"format": "uuid"
},
"description": "Notification channel uuids from list_channels. When passed it replaces the whole binding; [] clears it, after which every verified, active channel is alerted; omit it to keep the binding. An id that is not an active channel of this organization is a 404."
},
"badge_enabled": {
"type": "boolean",
"description": "Turns the monitor's public status badge on or off. A monitor on a published status page has a badge even when it is false."
},
"timeout_seconds": {
"type": "integer",
"maximum": 120,
"minimum": 1,
"description": "Seconds one check may take before it fails, 1 to 120."
},
"interval_seconds": {
"type": "integer",
"maximum": 86400,
"minimum": 30,
"description": "Seconds between checks, 30 to 86400. The plan sets a minimum (limits.min_interval_seconds) and caps how many monitors may check more often than every 60 seconds (max_fast_monitors); breaking either is a 403 naming the limit."
},
"confirmation_threshold": {
"type": "integer",
"maximum": 10,
"minimum": 1,
"description": "How many checks in a row must fail in every region before the monitor goes DOWN and an incident opens, 1 to 10; 3 when not set on create."
},
"notification_cooldown_seconds": {
"type": "integer",
"maximum": 86400,
"minimum": 60,
"description": "60 to 86400 seconds; 1800 when not set on create. Only holds back a duplicate first alert for the same incident within that window. Repeat reminders while an incident stays open and unacknowledged follow the plan's renotify_interval_seconds and renotify_max_count (get_plan_usage; 0 means no reminders) instead, and the recovery alert is never held back."
}
},
"additionalProperties": false
}