Introduction
REST API for the Miterl uptime monitoring platform.
This documentation covers the Miterl public REST API (v1). All endpoints are served under /api/v1.
Authentication. Send your API key as a Bearer token (Authorization: Bearer <key>) or via the X-API-Key header. Generate keys in your dashboard under Settings β API Keys (owner only).
Errors. Failures return a consistent JSON body: {"message": "...", "code": "...", "errors": {...}}. The errors object is only present on validation failures (HTTP 422).
Pagination. List endpoints accept page and per_page (default 25, max 100), and return data, links, and meta.
Rate limiting. Requests are throttled per API key; exceeding the limit returns HTTP 429.
Authenticating requests
To authenticate requests, include an Authorization header with the value "Bearer {YOUR_AUTH_KEY}".
All authenticated endpoints are marked with a requires authentication badge in the documentation below.
Generate an API key from your dashboard under Settings → API Keys (owner only). Send it as a Bearer token (Authorization: Bearer <key>) or via the X-API-Key header.
Monitors
Create and manage uptime monitors. All endpoints are scoped to the authenticated company.
List monitors
requires authentication
Returns a paginated list of the company's monitors, newest first.
Example request:
curl --request GET \
--get "https://miterl.com/api/v1/monitors?status=up&type=http&location=default&workspace_id=3&search=example.com&per_page=25&page=1" \
--header "Authorization: Bearer {YOUR_AUTH_KEY}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"https://miterl.com/api/v1/monitors"
);
const params = {
"status": "up",
"type": "http",
"location": "default",
"workspace_id": "3",
"search": "example.com",
"per_page": "25",
"page": "1",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (200, Success):
{
"data": [
{
"id": 1,
"name": "Example Site",
"type": "http",
"url": "https://example.com",
"status": "up",
"interval_seconds": 300,
"timeout_seconds": 30,
"failure_threshold": 2,
"consecutive_failures": 0,
"is_active": true,
"last_checked_at": "2026-05-21T12:00:00+00:00",
"location": "default",
"workspace_id": null,
"created_at": "2026-05-01T09:00:00+00:00",
"uptime_30d": 99.95
}
],
"links": {
"first": "https://miterl.com/api/v1/monitors?page=1",
"last": "https://miterl.com/api/v1/monitors?page=1",
"prev": null,
"next": null
},
"meta": {
"current_page": 1,
"from": 1,
"last_page": 1,
"path": "https://miterl.com/api/v1/monitors",
"per_page": 25,
"to": 1,
"total": 1
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Create a monitor
requires authentication
Example request:
curl --request POST \
"https://miterl.com/api/v1/monitors" \
--header "Authorization: Bearer {YOUR_AUTH_KEY}" \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data "{
\"name\": \"Example Site\",
\"type\": \"http\",
\"url\": \"https:\\/\\/example.com\",
\"interval_seconds\": 300,
\"timeout_seconds\": 30,
\"failure_threshold\": 2,
\"keyword\": \"Welcome\",
\"keyword_exists\": true,
\"ssl_expiry_alert_days\": 14,
\"whois_expiry_alert_days\": 30,
\"location\": \"default\",
\"workspace_id\": 3,
\"alert_contact_ids\": [
1,
2
]
}"
const url = new URL(
"https://miterl.com/api/v1/monitors"
);
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"name": "Example Site",
"type": "http",
"url": "https:\/\/example.com",
"interval_seconds": 300,
"timeout_seconds": 30,
"failure_threshold": 2,
"keyword": "Welcome",
"keyword_exists": true,
"ssl_expiry_alert_days": 14,
"whois_expiry_alert_days": 30,
"location": "default",
"workspace_id": 3,
"alert_contact_ids": [
1,
2
]
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Example response (201, Created):
{
"data": {
"id": 1,
"name": "Example Site",
"type": "http",
"url": "https://example.com",
"status": "pending",
"interval_seconds": 300,
"timeout_seconds": 30,
"failure_threshold": 2,
"consecutive_failures": 0,
"is_active": true,
"last_checked_at": null,
"location": "default",
"workspace_id": null,
"created_at": "2026-05-21T12:00:00+00:00",
"uptime_30d": 100
}
}
Example response (422, Validation error):
{
"message": "The name field is required.",
"code": "validation_failed",
"errors": {
"name": [
"The name field is required."
]
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Get a monitor
requires authentication
Example request:
curl --request GET \
--get "https://miterl.com/api/v1/monitors/1" \
--header "Authorization: Bearer {YOUR_AUTH_KEY}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"https://miterl.com/api/v1/monitors/1"
);
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (200, Success):
{
"data": {
"id": 1,
"name": "Example Site",
"type": "http",
"url": "https://example.com",
"status": "up",
"interval_seconds": 300,
"timeout_seconds": 30,
"failure_threshold": 2,
"consecutive_failures": 0,
"is_active": true,
"last_checked_at": "2026-05-21T12:00:00+00:00",
"location": "default",
"workspace_id": null,
"created_at": "2026-05-01T09:00:00+00:00",
"uptime_30d": 99.95
}
}
Example response (404, Not found):
{
"message": "Resource not found.",
"code": "not_found"
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Preview a monitor's alert routing
requires authentication
Answers "if this monitor went down right now, would anyone be paged β
and if not, why?" without waiting for a real outage. Returns the
resolved recipients (the monitor's own active contacts merged with its
workspace's) and the two silent-suppression gates the down-listener
applies: an active maintenance window and the company's quiet hours.
would_notify_now is true only when there is at least one recipient and
neither gate is active. A monitor that is down while this is false is
the classic "silent" case (no contacts wired, or suppressed).
Example request:
curl --request GET \
--get "https://miterl.com/api/v1/monitors/1/alert-status" \
--header "Authorization: Bearer {YOUR_AUTH_KEY}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"https://miterl.com/api/v1/monitors/1/alert-status"
);
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (200, Would notify):
{
"data": {
"monitor_id": 1,
"would_notify_now": true,
"under_maintenance": false,
"quiet_hours": false,
"contacts": [
{
"id": 1,
"type": "slack",
"name": "Ops Slack",
"is_active": true
}
]
}
}
Example response (200, Suppressed by quiet hours):
{
"data": {
"monitor_id": 1,
"would_notify_now": false,
"under_maintenance": false,
"quiet_hours": true,
"contacts": [
{
"id": 1,
"type": "slack",
"name": "Ops Slack",
"is_active": true
}
]
}
}
Example response (404, Not found):
{
"message": "Resource not found.",
"code": "not_found"
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Update a monitor
requires authentication
Accepts the same fields as creation (all optional); the monitor type cannot be changed.
Example request:
curl --request PUT \
"https://miterl.com/api/v1/monitors/1" \
--header "Authorization: Bearer {YOUR_AUTH_KEY}" \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data "{
\"name\": \"Example Site\",
\"url\": \"https:\\/\\/example.com\",
\"interval_seconds\": 300,
\"timeout_seconds\": 30,
\"failure_threshold\": 2,
\"keyword\": \"Welcome\",
\"keyword_exists\": true,
\"ssl_expiry_alert_days\": 14,
\"whois_expiry_alert_days\": 30,
\"location\": \"default\",
\"workspace_id\": 3,
\"alert_contact_ids\": [
1,
2
]
}"
const url = new URL(
"https://miterl.com/api/v1/monitors/1"
);
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"name": "Example Site",
"url": "https:\/\/example.com",
"interval_seconds": 300,
"timeout_seconds": 30,
"failure_threshold": 2,
"keyword": "Welcome",
"keyword_exists": true,
"ssl_expiry_alert_days": 14,
"whois_expiry_alert_days": 30,
"location": "default",
"workspace_id": 3,
"alert_contact_ids": [
1,
2
]
};
fetch(url, {
method: "PUT",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Example response (200, Updated):
{
"data": {
"id": 1,
"name": "Example Site",
"type": "http",
"url": "https://example.com",
"status": "up",
"interval_seconds": 300,
"timeout_seconds": 30,
"failure_threshold": 2,
"consecutive_failures": 0,
"is_active": true,
"last_checked_at": "2026-05-21T12:00:00+00:00",
"location": "default",
"workspace_id": null,
"created_at": "2026-05-01T09:00:00+00:00",
"uptime_30d": 99.95
}
}
Example response (404, Not found):
{
"message": "Resource not found.",
"code": "not_found"
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Delete a monitor
requires authentication
Example request:
curl --request DELETE \
"https://miterl.com/api/v1/monitors/1" \
--header "Authorization: Bearer {YOUR_AUTH_KEY}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"https://miterl.com/api/v1/monitors/1"
);
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "DELETE",
headers,
}).then(response => response.json());Example response (204, Deleted):
Empty response
Example response (404, Not found):
{
"message": "Resource not found.",
"code": "not_found"
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Pause a monitor
requires authentication
Stops checks until the monitor is resumed.
Example request:
curl --request POST \
"https://miterl.com/api/v1/monitors/1/pause" \
--header "Authorization: Bearer {YOUR_AUTH_KEY}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"https://miterl.com/api/v1/monitors/1/pause"
);
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "POST",
headers,
}).then(response => response.json());Example response (200, Paused):
{
"data": {
"id": 1,
"name": "Example Site",
"type": "http",
"url": "https://example.com",
"status": "paused",
"interval_seconds": 300,
"timeout_seconds": 30,
"failure_threshold": 2,
"consecutive_failures": 0,
"is_active": true,
"last_checked_at": "2026-05-21T12:00:00+00:00",
"location": "default",
"workspace_id": null,
"created_at": "2026-05-01T09:00:00+00:00",
"uptime_30d": 99.95
}
}
Example response (404, Not found):
{
"message": "Resource not found.",
"code": "not_found"
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Resume a monitor
requires authentication
Re-activates a paused monitor and schedules an immediate check.
Example request:
curl --request POST \
"https://miterl.com/api/v1/monitors/1/resume" \
--header "Authorization: Bearer {YOUR_AUTH_KEY}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"https://miterl.com/api/v1/monitors/1/resume"
);
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "POST",
headers,
}).then(response => response.json());Example response (200, Resumed):
{
"data": {
"id": 1,
"name": "Example Site",
"type": "http",
"url": "https://example.com",
"status": "up",
"interval_seconds": 300,
"timeout_seconds": 30,
"failure_threshold": 2,
"consecutive_failures": 0,
"is_active": true,
"last_checked_at": "2026-05-21T12:00:00+00:00",
"location": "default",
"workspace_id": null,
"created_at": "2026-05-01T09:00:00+00:00",
"uptime_30d": 99.95
}
}
Example response (404, Not found):
{
"message": "Resource not found.",
"code": "not_found"
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Workspaces
Workspaces group monitors into management units. Scoped to the authenticated company.
List workspaces
requires authentication
Returns a paginated list of the company's workspaces, ordered by name.
Example request:
curl --request GET \
--get "https://miterl.com/api/v1/workspaces?per_page=25&page=1" \
--header "Authorization: Bearer {YOUR_AUTH_KEY}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"https://miterl.com/api/v1/workspaces"
);
const params = {
"per_page": "25",
"page": "1",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (200, Success):
{
"data": [
{
"id": 1,
"name": "Production",
"description": "Live customer sites",
"created_at": "2026-05-01T09:00:00+00:00"
}
],
"links": {
"first": "https://miterl.com/api/v1/workspaces?page=1",
"last": "https://miterl.com/api/v1/workspaces?page=1",
"prev": null,
"next": null
},
"meta": {
"current_page": 1,
"from": 1,
"last_page": 1,
"path": "https://miterl.com/api/v1/workspaces",
"per_page": 25,
"to": 1,
"total": 1
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Create a workspace
requires authentication
Example request:
curl --request POST \
"https://miterl.com/api/v1/workspaces" \
--header "Authorization: Bearer {YOUR_AUTH_KEY}" \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data "{
\"name\": \"Production\",
\"description\": \"Live customer sites\"
}"
const url = new URL(
"https://miterl.com/api/v1/workspaces"
);
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"name": "Production",
"description": "Live customer sites"
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Example response (201, Created):
{
"data": {
"id": 1,
"name": "Production",
"description": "Live customer sites",
"created_at": "2026-05-21T12:00:00+00:00"
}
}
Example response (422, Limit reached):
{
"message": "Workspace limit reached (5). Upgrade the plan to add more.",
"code": "workspace_limit_reached"
}
Example response (422, Validation error):
{
"message": "The name field is required.",
"code": "validation_failed",
"errors": {
"name": [
"The name field is required."
]
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Incidents
Read-only incident history, scoped to the authenticated company's monitors. Internal drills are never exposed.
List incidents
requires authentication
Returns a paginated incident history across the company's monitors, newest first.
Example request:
curl --request GET \
--get "https://miterl.com/api/v1/incidents?monitor_id=1&severity=critical&status=resolved&per_page=25&page=1" \
--header "Authorization: Bearer {YOUR_AUTH_KEY}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"https://miterl.com/api/v1/incidents"
);
const params = {
"monitor_id": "1",
"severity": "critical",
"status": "resolved",
"per_page": "25",
"page": "1",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (200, Success):
{
"data": [
{
"id": 1,
"monitor_id": 1,
"severity": "critical",
"cause": "Connection timeout",
"started_at": "2026-05-20T03:00:00+00:00",
"resolved_at": "2026-05-20T03:15:00+00:00",
"acknowledged_at": "2026-05-20T03:05:00+00:00",
"duration_seconds": 900,
"is_resolved": true,
"is_acknowledged": true,
"has_postmortem": false,
"created_at": "2026-05-20T03:00:00+00:00",
"monitor": {
"id": 1,
"name": "Example Site",
"url": "https://example.com"
}
}
],
"links": {
"first": "https://miterl.com/api/v1/incidents?page=1",
"last": "https://miterl.com/api/v1/incidents?page=1",
"prev": null,
"next": null
},
"meta": {
"current_page": 1,
"from": 1,
"last_page": 1,
"path": "https://miterl.com/api/v1/incidents",
"per_page": 25,
"to": 1,
"total": 1
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Get an incident
requires authentication
Includes the related monitor and the incident's public updates.
Example request:
curl --request GET \
--get "https://miterl.com/api/v1/incidents/1" \
--header "Authorization: Bearer {YOUR_AUTH_KEY}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"https://miterl.com/api/v1/incidents/1"
);
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (200, Success):
{
"data": {
"id": 1,
"monitor_id": 1,
"severity": "critical",
"cause": "Connection timeout",
"started_at": "2026-05-20T03:00:00+00:00",
"resolved_at": "2026-05-20T03:15:00+00:00",
"acknowledged_at": "2026-05-20T03:05:00+00:00",
"duration_seconds": 900,
"is_resolved": true,
"is_acknowledged": true,
"has_postmortem": false,
"created_at": "2026-05-20T03:00:00+00:00",
"monitor": {
"id": 1,
"name": "Example Site",
"url": "https://example.com"
},
"updates": []
}
}
Example response (404, Not found):
{
"message": "Resource not found.",
"code": "not_found"
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Alerts
Read-only history of alert deliveries (email, Slack, webhook, ...) for the authenticated company.
List alert deliveries
requires authentication
Returns a paginated history of alert deliveries, newest first.
Example request:
curl --request GET \
--get "https://miterl.com/api/v1/alerts?status=sent&channel=email&event=monitor.down&monitor_id=1&per_page=25&page=1" \
--header "Authorization: Bearer {YOUR_AUTH_KEY}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"https://miterl.com/api/v1/alerts"
);
const params = {
"status": "sent",
"channel": "email",
"event": "monitor.down",
"monitor_id": "1",
"per_page": "25",
"page": "1",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (200, Success):
{
"data": [
{
"id": 1,
"monitor_id": 1,
"incident_id": 1,
"channel": "email",
"event": "monitor.down",
"status": "sent",
"recipient": "[email protected]",
"error_message": null,
"sent_at": "2026-05-20T03:01:00+00:00",
"created_at": "2026-05-20T03:01:00+00:00"
}
],
"links": {
"first": "https://miterl.com/api/v1/alerts?page=1",
"last": "https://miterl.com/api/v1/alerts?page=1",
"prev": null,
"next": null
},
"meta": {
"current_page": 1,
"from": 1,
"last_page": 1,
"path": "https://miterl.com/api/v1/alerts",
"per_page": 25,
"to": 1,
"total": 1
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Pre-launch Audits
Batch pre-deployment checks (HTTP status, response time, SSL, optional keyword) for CI/CD gating. Scoped to the authenticated company.
Start a pre-launch audit
requires authentication
Queues a batch check of one or more URLs. Returns immediately with status
pending; poll the show endpoint for results.
Example request:
curl --request POST \
"https://miterl.com/api/v1/pre-launch" \
--header "Authorization: Bearer {YOUR_AUTH_KEY}" \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data "{
\"name\": \"Release v2.0 smoke test\",
\"urls\": [
\"https:\\/\\/example.com\"
],
\"required_keyword\": \"Welcome\"
}"
const url = new URL(
"https://miterl.com/api/v1/pre-launch"
);
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"name": "Release v2.0 smoke test",
"urls": [
"https:\/\/example.com"
],
"required_keyword": "Welcome"
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Example response (202, Accepted):
{
"data": {
"id": 1,
"name": "Release v2.0 smoke test",
"status": "pending",
"started_at": null,
"completed_at": null,
"passed_count": 0,
"failed_count": 0,
"results": []
}
}
Example response (422, Validation error):
{
"message": "The urls field is required.",
"code": "validation_failed",
"errors": {
"urls": [
"The urls field is required."
]
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Get a pre-launch audit
requires authentication
The HTTP status mirrors the audit outcome so CI/CD can branch on it:
200 all passed, 422 some failed, 202 still running, 500 audit errored.
Example request:
curl --request GET \
--get "https://miterl.com/api/v1/pre-launch/1" \
--header "Authorization: Bearer {YOUR_AUTH_KEY}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"https://miterl.com/api/v1/pre-launch/1"
);
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (200, All passed):
{
"data": {
"id": 1,
"name": "Release v2.0 smoke test",
"status": "completed",
"started_at": "2026-05-21T12:00:00+00:00",
"completed_at": "2026-05-21T12:00:05+00:00",
"passed_count": 1,
"failed_count": 0,
"results": [
{
"url": "https://example.com",
"passed": true,
"status_code": 200,
"response_time_ms": 124,
"ssl_valid": true,
"ssl_days_until_expiry": 78,
"ssl_expires_at": "2026-08-07T12:00:00+00:00",
"ssl_issuer": "R3",
"keyword_present": true,
"errors": []
}
]
}
}
Example response (202, Still running):
{
"data": {
"id": 1,
"name": "Release v2.0 smoke test",
"status": "running",
"started_at": "2026-05-21T12:00:00+00:00",
"completed_at": null,
"passed_count": 0,
"failed_count": 0,
"results": []
}
}
Example response (404, Not found):
{
"message": "Resource not found.",
"code": "not_found"
}
Example response (422, Some failed):
{
"data": {
"id": 1,
"name": "Release v2.0 smoke test",
"status": "completed",
"started_at": "2026-05-21T12:00:00+00:00",
"completed_at": "2026-05-21T12:00:05+00:00",
"passed_count": 0,
"failed_count": 1,
"results": [
{
"url": "https://example.com",
"passed": false,
"status_code": 500,
"response_time_ms": 210,
"ssl_valid": true,
"ssl_days_until_expiry": 78,
"ssl_expires_at": "2026-08-07T12:00:00+00:00",
"ssl_issuer": "R3",
"keyword_present": null,
"errors": [
"http_status_not_2xx"
]
}
]
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Status Pages
Public, read-only access to status pages. No authentication required.
Get a public status page
Public, unauthenticated JSON view of a status page. Only fully public pages are exposed β password-protected ("restricted") and disabled pages return 404 so their existence stays hidden.
Example request:
curl --request GET \
--get "https://miterl.com/api/v1/status-pages/acme" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"https://miterl.com/api/v1/status-pages/acme"
);
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (200, Success):
{
"data": {
"name": "Acme Status",
"slug": "acme",
"overall_status": "operational",
"monitors": [
{
"id": 1,
"name": "Website",
"status": "up",
"uptime_90d": 99.9,
"last_checked_at": "2026-05-21T12:00:00+00:00",
"uptime_daily": [
{
"date": "2026-05-21",
"uptime": 100
}
],
"group_name": null,
"last_dom_issue": null
}
],
"incidents": [],
"upcoming_maintenance": []
}
}
Example response (404, Not found or not public):
{
"message": "Resource not found.",
"code": "not_found"
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Webhooks
CI/CD webhooks authenticated by a per-monitor path token (no API key).
Trigger release mode
Starts intensive release monitoring for the monitor identified by the path token. Authenticated by the webhook token only.
Example request:
curl --request POST \
"https://miterl.com/api/v1/webhooks/release-mode/a1b2c3d4e5f6" \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data "{
\"duration_hours\": 24,
\"interval_seconds\": 15
}"
const url = new URL(
"https://miterl.com/api/v1/webhooks/release-mode/a1b2c3d4e5f6"
);
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"duration_hours": 24,
"interval_seconds": 15
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Example response (200, Started):
{
"status": "started",
"monitor_id": 1,
"monitor_name": "Example Site",
"release_mode_run_id": 10,
"duration_hours": 24,
"interval_seconds": 15,
"started_at": "2026-05-21T12:00:00+00:00",
"until": "2026-05-22T12:00:00+00:00"
}
Example response (403, Not on plan):
{
"error": "Release mode is not available on this plan",
"monitor_id": 1
}
Example response (404, Invalid token):
{
"error": "Invalid webhook token"
}
Example response (409, Under maintenance):
{
"error": "Monitor is under an active maintenance window",
"monitor_id": 1
}
Example response (422, Already active):
{
"error": "Release mode is already active for this monitor",
"monitor_id": 1,
"until": "2026-05-22T12:00:00+00:00"
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Start a maintenance window
Opens a maintenance window for the monitor identified by the path token, suppressing alerts during a deployment. Authenticated by the webhook token only.
Example request:
curl --request POST \
"https://miterl.com/api/v1/webhooks/maintenance/a1b2c3d4e5f6/start" \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data "{
\"duration_hours\": 4,
\"name\": \"Deployment maintenance\"
}"
const url = new URL(
"https://miterl.com/api/v1/webhooks/maintenance/a1b2c3d4e5f6/start"
);
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"duration_hours": 4,
"name": "Deployment maintenance"
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Example response (200, Started):
{
"status": "started",
"monitor_id": 1,
"monitor_name": "Example Site",
"window_id": 5,
"started_at": "2026-05-21T12:00:00+00:00",
"until": "2026-05-21T16:00:00+00:00"
}
Example response (404, Invalid token):
{
"error": "Invalid webhook token"
}
Example response (422, Already active):
{
"error": "A webhook-initiated maintenance window is already active for this monitor",
"monitor_id": 1
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
End a maintenance window
Closes the active webhook-initiated maintenance window for the monitor.
Example request:
curl --request POST \
"https://miterl.com/api/v1/webhooks/maintenance/a1b2c3d4e5f6/end" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"https://miterl.com/api/v1/webhooks/maintenance/a1b2c3d4e5f6/end"
);
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "POST",
headers,
}).then(response => response.json());Example response (200, Ended):
{
"status": "ended",
"monitor_id": 1,
"window_id": 5
}
Example response (404, No active window):
{
"error": "No active webhook-initiated maintenance window found",
"monitor_id": 1
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Alert Contacts
Read-only listing of the company's alert contacts (Slack, email, etc.).
Use an id with a monitor's alert_contact_ids to wire who gets notified
on down/recovery. Contacts themselves are created in the dashboard.
List alert contacts
requires authentication
Returns the company's alert contacts, ordered by name.
Example request:
curl --request GET \
--get "https://miterl.com/api/v1/alert-contacts?per_page=25&page=1" \
--header "Authorization: Bearer {YOUR_AUTH_KEY}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"https://miterl.com/api/v1/alert-contacts"
);
const params = {
"per_page": "25",
"page": "1",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (200, Success):
{
"data": [
{
"id": 1,
"type": "slack",
"name": "Ops Slack",
"is_active": true
}
],
"links": {
"first": null,
"last": null,
"prev": null,
"next": null
},
"meta": {
"current_page": 1,
"from": 1,
"last_page": 1,
"path": "https://miterl.com/api/v1/alert-contacts",
"per_page": 25,
"to": 1,
"total": 1
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
WordPress Connect
Endpoints called by the Miterl Connect WordPress plugin, authenticated by a per-monitor connect key in the path (no API key).
Ping (connection test)
Returns the identity of the monitor linked to the connect key. Used by the plugin's "Test connection" button.
Example request:
curl --request GET \
--get "https://miterl.com/api/v1/wp/mc_abcdefghijklmnopqrstuvwxyz0123456789ABCD/ping" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"https://miterl.com/api/v1/wp/mc_abcdefghijklmnopqrstuvwxyz0123456789ABCD/ping"
);
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (200, Connected):
{
"ok": true,
"monitor_id": 1,
"monitor_name": "client-a.example.com",
"site_url": "https://client-a.example.com/"
}
Example response (404, Invalid key):
{
"error": "Invalid connect key"
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
wp-cron heartbeat
Records a heartbeat on the wp-cron Heartbeat monitor linked to the connection
(same effect as GET /heartbeat/{token}). When the connection has no
Heartbeat monitor, or that monitor is paused, nothing is recorded and
heartbeat is false.
Example request:
curl --request POST \
"https://miterl.com/api/v1/wp/mc_abcdefghijklmnopqrstuvwxyz0123456789ABCD/heartbeat" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"https://miterl.com/api/v1/wp/mc_abcdefghijklmnopqrstuvwxyz0123456789ABCD/heartbeat"
);
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "POST",
headers,
}).then(response => response.json());Example response (200, Recorded):
{
"ok": true,
"heartbeat": true
}
Example response (200, No heartbeat monitor):
{
"ok": true,
"heartbeat": false
}
Example response (404, Invalid key):
{
"error": "Invalid connect key"
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Start a maintenance window (before a WordPress update)
Opens a webhook-initiated maintenance window on the monitor linked to the connect
key (same effect and response as POST /api/v1/webhooks/maintenance/{token}/start),
so the temporary 5xx during a core/plugin/theme update is not alerted.
Example request:
curl --request POST \
"https://miterl.com/api/v1/wp/mc_abcdefghijklmnopqrstuvwxyz0123456789ABCD/maintenance/start" \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data "{
\"duration_hours\": 1,
\"name\": \"Plugin update\"
}"
const url = new URL(
"https://miterl.com/api/v1/wp/mc_abcdefghijklmnopqrstuvwxyz0123456789ABCD/maintenance/start"
);
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"duration_hours": 1,
"name": "Plugin update"
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Example response (200, Started):
{
"status": "started",
"monitor_id": 1,
"monitor_name": "client-a.example.com",
"window_id": 5,
"started_at": "2026-09-13T12:00:00+00:00",
"until": "2026-09-13T13:00:00+00:00"
}
Example response (404, Invalid key):
{
"error": "Invalid connect key"
}
Example response (422, Already active):
{
"error": "A webhook-initiated maintenance window is already active for this monitor",
"monitor_id": 1
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
End a maintenance window (after a WordPress update)
Closes the active webhook-initiated maintenance window on the linked monitor
(same effect and response as POST /api/v1/webhooks/maintenance/{token}/end).
Example request:
curl --request POST \
"https://miterl.com/api/v1/wp/mc_abcdefghijklmnopqrstuvwxyz0123456789ABCD/maintenance/end" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"https://miterl.com/api/v1/wp/mc_abcdefghijklmnopqrstuvwxyz0123456789ABCD/maintenance/end"
);
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "POST",
headers,
}).then(response => response.json());Example response (200, Ended):
{
"status": "ended",
"monitor_id": 1,
"window_id": 5
}
Example response (404, Invalid key):
{
"error": "Invalid connect key"
}
Example response (404, No active window):
{
"error": "No active webhook-initiated maintenance window found",
"monitor_id": 1
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Send the site inventory (daily)
Stores the WordPress core / plugin / theme versions and available updates on the connection, replacing the previous inventory (no history is kept). Miterl shows the pending updates on the monitor page and, on Standard and above, alerts when they are left unapplied.
Example request:
curl --request POST \
"https://miterl.com/api/v1/wp/mc_abcdefghijklmnopqrstuvwxyz0123456789ABCD/inventory" \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data "{
\"wp_version\": \"6.8.1\",
\"php_version\": \"8.3.12\",
\"core\": {
\"version\": \"6.8.1\",
\"new_version\": \"6.8.2\"
},
\"plugins\": [
{
\"slug\": \"akismet\",
\"name\": \"Akismet\",
\"version\": \"5.3\",
\"new_version\": \"5.4\",
\"active\": true
}
],
\"themes\": [
{
\"slug\": \"y\",
\"name\": \"v\",
\"version\": \"d\",
\"new_version\": \"l\",
\"active\": true
}
]
}"
const url = new URL(
"https://miterl.com/api/v1/wp/mc_abcdefghijklmnopqrstuvwxyz0123456789ABCD/inventory"
);
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"wp_version": "6.8.1",
"php_version": "8.3.12",
"core": {
"version": "6.8.1",
"new_version": "6.8.2"
},
"plugins": [
{
"slug": "akismet",
"name": "Akismet",
"version": "5.3",
"new_version": "5.4",
"active": true
}
],
"themes": [
{
"slug": "y",
"name": "v",
"version": "d",
"new_version": "l",
"active": true
}
]
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Example response (200, Stored):
{
"ok": true,
"pending_updates": 2
}
Example response (404, Invalid key):
{
"error": "Invalid connect key"
}
Example response (422, Malformed):
{
"message": "The php version field is required. (and 1 more error)",
"errors": {
"php_version": [
"The php version field is required."
],
"plugins": [
"The plugins field must be an array."
]
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.