Download OpenAPI specification:
Infrastructure monitoring API — HTTP(S) checks, heartbeat/cron monitoring, TLS/domain expiry checks, and alert channels.
Owners/admins only. Only the display prefix is returned, never the raw key.
[- {
- "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
- "name": "string",
- "key_prefix": "sk_ab12cd34",
- "created_at": "2019-08-24T14:15:22Z"
}
]Owners/admins only. The raw key is returned once, at creation — store it somewhere safe; only key_prefix is retrievable afterwards.
| name required | string |
{- "name": "string"
}{- "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
- "name": "string",
- "key_prefix": "sk_ab12cd34",
- "created_at": "2019-08-24T14:15:22Z",
- "key": "string"
}[- {
- "type": "http",
- "name": "string",
- "url": "string",
- "method": "GET",
- "interval_seconds": 300,
- "timeout_seconds": 10,
- "grace_seconds": 0,
- "cron_expression": "0 3 * * 1-5",
- "cron_timezone": "Europe/Paris",
- "failure_threshold": 2,
- "assertions": [
- {
- "type": "status_code",
- "name": "string",
- "value": "string"
}
], - "enabled": true,
- "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
- "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
- "state": "up",
- "consecutive_failures": 0,
- "ping_token": "string",
- "next_expected_ping_at": "2019-08-24T14:15:22Z",
- "next_run_at": "2019-08-24T14:15:22Z",
- "created_at": "2019-08-24T14:15:22Z",
- "updated_at": "2019-08-24T14:15:22Z"
}
]interval_seconds is floored by your plan (free 300s, starter 60s, pro 30s). For type: heartbeat or cron, omit url/method/assertions and send grace_seconds instead — the response includes a ping_token. A cron check may carry cron_expression (+ cron_timezone) to be judged against a crontab schedule rather than a flat period.
| type | string Default: "http" Enum: "http" "heartbeat" "cron" "tls" "domain" |
| name required | string |
| url | string Required for http/tls/domain |
| method | string Default: "GET" |
| interval_seconds | integer [ 30 .. 86400 ] Default: 300 |
| timeout_seconds | integer Default: 10 |
| grace_seconds | integer heartbeat/cron only |
| cron_expression | string Optional crontab schedule, |
| cron_timezone | string IANA zone the expression is read in, default UTC. Per check because a crontab fires in its host's local zone, and a fixed offset would drift an hour at each DST change. |
| failure_threshold | integer [ 1 .. 10 ] Default: 2 |
Array of objects (Assertion) | |
| enabled | boolean Default: true |
{- "type": "http",
- "name": "string",
- "url": "string",
- "method": "GET",
- "interval_seconds": 300,
- "timeout_seconds": 10,
- "grace_seconds": 0,
- "cron_expression": "0 3 * * 1-5",
- "cron_timezone": "Europe/Paris",
- "failure_threshold": 2,
- "assertions": [
- {
- "type": "status_code",
- "name": "string",
- "value": "string"
}
], - "enabled": true
}{- "type": "http",
- "name": "string",
- "url": "string",
- "method": "GET",
- "interval_seconds": 300,
- "timeout_seconds": 10,
- "grace_seconds": 0,
- "cron_expression": "0 3 * * 1-5",
- "cron_timezone": "Europe/Paris",
- "failure_threshold": 2,
- "assertions": [
- {
- "type": "status_code",
- "name": "string",
- "value": "string"
}
], - "enabled": true,
- "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
- "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
- "state": "up",
- "consecutive_failures": 0,
- "ping_token": "string",
- "next_expected_ping_at": "2019-08-24T14:15:22Z",
- "next_run_at": "2019-08-24T14:15:22Z",
- "created_at": "2019-08-24T14:15:22Z",
- "updated_at": "2019-08-24T14:15:22Z"
}{- "type": "http",
- "name": "string",
- "url": "string",
- "method": "GET",
- "interval_seconds": 300,
- "timeout_seconds": 10,
- "grace_seconds": 0,
- "cron_expression": "0 3 * * 1-5",
- "cron_timezone": "Europe/Paris",
- "failure_threshold": 2,
- "assertions": [
- {
- "type": "status_code",
- "name": "string",
- "value": "string"
}
], - "enabled": true,
- "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
- "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
- "state": "up",
- "consecutive_failures": 0,
- "ping_token": "string",
- "next_expected_ping_at": "2019-08-24T14:15:22Z",
- "next_run_at": "2019-08-24T14:15:22Z",
- "created_at": "2019-08-24T14:15:22Z",
- "updated_at": "2019-08-24T14:15:22Z"
}Replaces the whole check — it does not patch. Omitted fields are not kept from the existing row. type is immutable after creation.
| id required | string <uuid> |
| type | string Default: "http" Enum: "http" "heartbeat" "cron" "tls" "domain" |
| name required | string |
| url | string Required for http/tls/domain |
| method | string Default: "GET" |
| interval_seconds | integer [ 30 .. 86400 ] Default: 300 |
| timeout_seconds | integer Default: 10 |
| grace_seconds | integer heartbeat/cron only |
| cron_expression | string Optional crontab schedule, |
| cron_timezone | string IANA zone the expression is read in, default UTC. Per check because a crontab fires in its host's local zone, and a fixed offset would drift an hour at each DST change. |
| failure_threshold | integer [ 1 .. 10 ] Default: 2 |
Array of objects (Assertion) | |
| enabled | boolean Default: true |
{- "type": "http",
- "name": "string",
- "url": "string",
- "method": "GET",
- "interval_seconds": 300,
- "timeout_seconds": 10,
- "grace_seconds": 0,
- "cron_expression": "0 3 * * 1-5",
- "cron_timezone": "Europe/Paris",
- "failure_threshold": 2,
- "assertions": [
- {
- "type": "status_code",
- "name": "string",
- "value": "string"
}
], - "enabled": true
}{- "type": "http",
- "name": "string",
- "url": "string",
- "method": "GET",
- "interval_seconds": 300,
- "timeout_seconds": 10,
- "grace_seconds": 0,
- "cron_expression": "0 3 * * 1-5",
- "cron_timezone": "Europe/Paris",
- "failure_threshold": 2,
- "assertions": [
- {
- "type": "status_code",
- "name": "string",
- "value": "string"
}
], - "enabled": true,
- "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
- "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
- "state": "up",
- "consecutive_failures": 0,
- "ping_token": "string",
- "next_expected_ping_at": "2019-08-24T14:15:22Z",
- "next_run_at": "2019-08-24T14:15:22Z",
- "created_at": "2019-08-24T14:15:22Z",
- "updated_at": "2019-08-24T14:15:22Z"
}Resolves a crontab expression to its next runs without writing anything, so a schedule can be checked before it is relied on. Uses the same parser as the silence detector, so the preview and the deadline that actually pages you cannot disagree.
| cron_expression required | string Example: cron_expression=0 3 * * 1-5 |
| cron_timezone | string Default: "UTC" Example: cron_timezone=Europe/Paris |
{- "cron_expression": "string",
- "cron_timezone": "string",
- "next": [
- "2019-08-24T14:15:22Z"
], - "min_gap_seconds": 0,
- "plan_min_interval_seconds": 0,
- "below_plan_minimum": true
}| id required | string <uuid> |
| period | string Default: "day" Enum: "day" "week" "month" day = 15-min buckets, week = 2h buckets, month = 12h buckets |
[- {
- "bucket": "2019-08-24T14:15:22Z",
- "avg_latency_ms": 0,
- "uptime_pct": 0,
- "count": 0
}
]The last 20 delivery attempts, newest first. A state change and a delivered notification are different things; this reports the second. A skipped row with no channel means nobody was told, and detail says why — no channels, none confirmed, routing that excludes them, or a maintenance window. Records begin when the endpoint shipped.
| id required | string <uuid> |
[- {
- "outcome": "delivered",
- "channel_type": "email",
- "channel_name": "string",
- "detail": "string",
- "created_at": "2019-08-24T14:15:22Z"
}
]No rows = every org channel fires (default). Assigned rows = only those channels fire.
| id required | string <uuid> |
{- "channel_ids": [
- "497f6eca-6276-4993-bfeb-53cbbbba6f08"
]
}| id required | string <uuid> |
| channel_ids required | Array of strings <uuid> [ items <uuid > ] |
{- "channel_ids": [
- "497f6eca-6276-4993-bfeb-53cbbbba6f08"
]
}{- "channel_ids": [
- "497f6eca-6276-4993-bfeb-53cbbbba6f08"
]
}[- {
- "type": "email",
- "name": "string",
- "config": {
- "to": "string",
- "url": "string",
- "secret": "string",
- "webhook_url": "string"
}, - "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
- "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
- "connect_url": "string"
}
]For type: webhook, a signing secret is auto-generated if config.secret is omitted — always send the existing secret back unchanged on update, or a new one is minted and downstream signature verification silently breaks.
| type required | string Enum: "email" "webhook" "slack" "discord" "telegram" |
| name required | string |
required | object email: {to}. webhook: {url, secret?}. slack: {webhook_url}. discord: {webhook_url} — the incoming-webhook URL from the Discord channel's own settings (Edit Channel → Integrations → Webhooks), including the token after the webhook id. telegram: {} — our bot is the destination, so there is nothing to supply; the response carries a one-time connect_url to open and press Start. |
{- "type": "email",
- "name": "string",
- "config": {
- "to": "string",
- "url": "string",
- "secret": "string",
- "webhook_url": "string"
}
}{- "type": "email",
- "name": "string",
- "config": {
- "to": "string",
- "url": "string",
- "secret": "string",
- "webhook_url": "string"
}, - "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
- "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
- "connect_url": "string"
}| id required | string <uuid> |
| type required | string Enum: "email" "webhook" "slack" "discord" "telegram" |
| name required | string |
required | object email: {to}. webhook: {url, secret?}. slack: {webhook_url}. discord: {webhook_url} — the incoming-webhook URL from the Discord channel's own settings (Edit Channel → Integrations → Webhooks), including the token after the webhook id. telegram: {} — our bot is the destination, so there is nothing to supply; the response carries a one-time connect_url to open and press Start. |
{- "type": "email",
- "name": "string",
- "config": {
- "to": "string",
- "url": "string",
- "secret": "string",
- "webhook_url": "string"
}
}{- "type": "email",
- "name": "string",
- "config": {
- "to": "string",
- "url": "string",
- "secret": "string",
- "webhook_url": "string"
}, - "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
- "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6",
- "connect_url": "string"
}Read-only with an API key; changing the page's configuration (slug, components, custom domain, publishing the page) is session-only, so a leaked key can't repoint your public address. components[].id is what component_ids on an incident refers to. 404 until the page exists.
{- "slug": "string",
- "title": "string",
- "published": true,
- "theme": "dark",
- "public_url": "string",
- "domain": {
- "domain": "string",
- "verified": true,
- "verified_at": "2019-08-24T14:15:22Z",
- "cname_target": "string"
}, - "components": [
- {
- "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
- "check_id": "051490c8-9abf-480a-9348-72c146208cd5",
- "name": "string"
}
]
}Every incident on the page, drafts included — this is the authoring view, not the public one. Newest first.
[- {
- "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
- "title": "string",
- "status": "investigating",
- "impact": "minor",
- "published": true,
- "auto_drafted": true,
- "started_at": "2019-08-24T14:15:22Z",
- "resolved_at": "2019-08-24T14:15:22Z",
- "archived_at": "2019-08-24T14:15:22Z",
- "component_ids": [
- "497f6eca-6276-4993-bfeb-53cbbbba6f08"
], - "updates": [
- {
- "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
- "status": "investigating",
- "body": "string",
- "automated": true,
- "created_at": "2019-08-24T14:15:22Z"
}
]
}
]Owner/admin, Pro plan or above. Starts as a private draft — send published: true (here or on a later PUT) to show it to visitors. body, when present, becomes the first timeline entry.
| title required | string <= 120 characters |
| status | string (IncidentStatus) Default: "investigating" Enum: "investigating" "identified" "monitoring" "resolved" |
| impact | string Default: "minor" Enum: "minor" "major" "critical" |
| published | boolean Absent leaves it unchanged. New incidents are unpublished — invisible to visitors — until this is true. |
| archived | boolean Absent leaves it unchanged. True closes the incident and files it into the page's history, which visitors reach by clicking the status panel. |
| component_ids | Array of strings <uuid> [ items <uuid > ] Replaces the affected set wholesale. Ids from GET /status-page. |
| body | string <= 4000 characters Create only — becomes the first timeline entry |
{- "title": "string",
- "status": "investigating",
- "impact": "minor",
- "published": true,
- "archived": true,
- "component_ids": [
- "497f6eca-6276-4993-bfeb-53cbbbba6f08"
], - "body": "string"
}{- "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
- "title": "string",
- "status": "investigating",
- "impact": "minor",
- "published": true,
- "auto_drafted": true,
- "started_at": "2019-08-24T14:15:22Z",
- "resolved_at": "2019-08-24T14:15:22Z",
- "archived_at": "2019-08-24T14:15:22Z",
- "component_ids": [
- "497f6eca-6276-4993-bfeb-53cbbbba6f08"
], - "updates": [
- {
- "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
- "status": "investigating",
- "body": "string",
- "automated": true,
- "created_at": "2019-08-24T14:15:22Z"
}
]
}Replaces the incident itself — title, severity, affected components, whether it's published. Timeline entries are posted separately and are never rewritten from here. published and archived are applied only when present, so an edit that omits them can't silently unpublish a live incident. archived: true also resolves it.
| id required | string <uuid> |
| title required | string <= 120 characters |
| status | string (IncidentStatus) Default: "investigating" Enum: "investigating" "identified" "monitoring" "resolved" |
| impact | string Default: "minor" Enum: "minor" "major" "critical" |
| published | boolean Absent leaves it unchanged. New incidents are unpublished — invisible to visitors — until this is true. |
| archived | boolean Absent leaves it unchanged. True closes the incident and files it into the page's history, which visitors reach by clicking the status panel. |
| component_ids | Array of strings <uuid> [ items <uuid > ] Replaces the affected set wholesale. Ids from GET /status-page. |
| body | string <= 4000 characters Create only — becomes the first timeline entry |
{- "title": "string",
- "status": "investigating",
- "impact": "minor",
- "published": true,
- "archived": true,
- "component_ids": [
- "497f6eca-6276-4993-bfeb-53cbbbba6f08"
], - "body": "string"
}{- "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
- "title": "string",
- "status": "investigating",
- "impact": "minor",
- "published": true,
- "auto_drafted": true,
- "started_at": "2019-08-24T14:15:22Z",
- "resolved_at": "2019-08-24T14:15:22Z",
- "archived_at": "2019-08-24T14:15:22Z",
- "component_ids": [
- "497f6eca-6276-4993-bfeb-53cbbbba6f08"
], - "updates": [
- {
- "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
- "status": "investigating",
- "body": "string",
- "automated": true,
- "created_at": "2019-08-24T14:15:22Z"
}
]
}Append-only. status is optional — an update that doesn't move the incident along is still worth posting; when present it moves the incident, and resolved stamps resolved_at. Posting to an archived incident un-archives it.
| id required | string <uuid> |
| body required | string <= 4000 characters |
| status | string (IncidentStatus) Default: "investigating" Enum: "investigating" "identified" "monitoring" "resolved" |
{- "body": "string",
- "status": "investigating"
}{- "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
- "title": "string",
- "status": "investigating",
- "impact": "minor",
- "published": true,
- "auto_drafted": true,
- "started_at": "2019-08-24T14:15:22Z",
- "resolved_at": "2019-08-24T14:15:22Z",
- "archived_at": "2019-08-24T14:15:22Z",
- "component_ids": [
- "497f6eca-6276-4993-bfeb-53cbbbba6f08"
], - "updates": [
- {
- "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
- "status": "investigating",
- "body": "string",
- "automated": true,
- "created_at": "2019-08-24T14:15:22Z"
}
]
}