Monitors
Create, change, pause and delete monitors, read their checks, uptime and response times, send heartbeats from cron jobs and data to push monitors, and organise monitors into groups.
Paths are relative to https://api.zharp.io/api/v1. Reading needs a read key; anything that changes a monitor needs admin, and the key's owner needs the matching permission (monitors:create, monitors:edit, monitors:delete — every role but Read-only). See Authentication.
/monitorsList monitorsGET/monitors/:idOne monitorPOST/monitorsCreate a monitorPUT/monitors/:idChange a monitorDELETE/monitors/:idDelete a monitorPOST/monitors/bulk-deleteDelete severalPOST/monitors/:id/cloneCopy a monitorPOST/monitors/:id/triggerCheck nowPOST/monitors/:id/toggle-muteMute or unmute alertsPUT/monitors/:id/tagsSet tagsPOST/monitors/:id/test-notificationsSend test alertsPOST/monitors/:id/reset-statsClear its historyGET/monitors/:id/logsRecent check resultsGET/monitors/:id/statsUptime for a rangeGET/monitors/:id/uptime-histogramUptime per dayGET/monitors/:id/latencyResponse timesGET/monitors/:id/incidentsIts incidentsGET/monitors/global-statisticsWorkspace countsGET/monitors/uptime-metricsWorkspace uptimeGET/ping/:tokenCron heartbeat (POST works too)GET/ping/:token/:signalStart, failure or exit codePOST/push/:tokenSend data to a push monitorGET/monitor-groupsList groupsPOST/monitor-groupsCreate a groupStatuses
| status | Meaning |
|---|---|
PENDING | Created or resumed, not checked yet. |
UP | The last check passed. |
DOWN | Checks fail — an incident is open. |
MAINTENANCE | In a maintenance window: checked, but nobody is alerted. |
PAUSED | isActive is false. |
The status is the checks' to decide: pause and resume with isActive; it can't be set directly.
Monitors
/monitorsdailyHistogram has 24 hourly entries: timestamp in epoch seconds, uptime a percentage, or -1 for an hour without checks. metadata here is a summary — the full settings are on GET /monitors/:id.
searchstatustypetagstags=prod,eu or repeated tags=prod&tags=eu.sortByname, createdAt, status, isActive, lastCheckedAt or type.sortOrderasc (default) or desc.curl "https://api.zharp.io/api/v1/monitors?status=DOWN" \ -H "Authorization: Bearer $ZHARP_API_KEY"
{
"message": "Monitors retrieved successfully",
"data": [
{
"id": 42,
"name": "api.acme.io",
"type": "HTTP",
"url": "https://api.acme.io/health",
"interval": 60,
"status": "DOWN",
"isActive": true,
"groupId": 3,
"groupName": "Production",
"lastCheckedAt": "2026-10-26T09:59:30.000Z",
"lastLatency": 182,
"lastStatusCode": 503,
"lastMessage": "HTTP 503 Service Unavailable",
"uptime24h": 99.86,
"activeIncidentId": 481,
"dailyHistogram": [{ "timestamp": 1761386400, "uptime": 100 }, …],
"metadata": { "ssl": { "authorized": true, "validFor": 61 } },
…
}
]
}400An unknownstatus,typeorsortBy.
/monitors/:idCredentials — database and mail passwords, HTTP auth, tokens in headers, passwords inside connection strings — come back as ••••••••. Send that value back unchanged on an update and the stored one is kept.
idcurl "https://api.zharp.io/api/v1/monitors/57" \ -H "Authorization: Bearer $ZHARP_API_KEY"
{
"message": "Monitor details retrieved successfully",
"data": {
"id": 57,
"workspaceId": 3,
"groupId": null,
"name": "api.acme.io",
"type": "HTTP",
"url": "https://api.acme.io/health",
"target": null,
"port": null,
"interval": 60,
"timeout": 30,
"retries": 1,
"retryInterval": 60,
"status": "UP",
"isActive": true,
"regions": ["eu", "us"],
"metadata": {
"method": "GET",
"expectedStatusCodes": [200],
"headers": { "Authorization": "••••••••", "Accept": "application/json" },
"tags": ["prod"]
},
"immediateAlerts": { "email": true, "push": true },
"escalationPolicyId": 4,
"logServiceNames": ["api"],
"lastCheckedAt": "2026-10-26T09:59:30.000Z",
"nextCheckAt": "2026-10-26T10:00:30.000Z",
"upSince": "2026-10-20T08:00:00.000Z",
"downSince": null,
"createdAt": "2026-09-01T10:00:00.000Z",
"updatedAt": "2026-10-26T09:59:30.000Z",
"workspace": { "id": 3, "name": "Acme", "slug": "acme" },
"group": null,
"notificationChannels": [{ "channelId": 41, "delay": 0, "repeat": 0, "repeatInterval": 0 }],
"maintenanceWindows": [{ "scheduleType": "weekly", "days": ["Sun"], "startTime": "02:00", "endTime": "03:00", "timezone": "UTC" }]
}
}404No such monitor in this workspace.
/monitorsZharp checks from the internet, so a url or target on a private network (10.x, 192.168.x, localhost…) is refused — monitor those through the Zharp agent instead.
typenameurlHTTP, API, KEYWORD, SYNTHETIC, BROKEN_LINKS and VULNERABILITY.targetPING, PORT, SSL, DNS, DOMAIN, the databases, mail servers and IP_BLACKLIST.portintervalSYNTHETIC) monitors 1800 on Free, 300 on Pro, 60 on Business. Cron and push monitors aren't checked on a schedule, so the minimum doesn't apply to them.timeoutretriesretryIntervalregionseu, us, as, au. Default all four.isActivefalse pauses it: no checks, no alerts. Default true.notificationChannels[{ "channelId": 41, "delay": 0, "repeat": 0, "repeatInterval": 0 }]. On update it replaces the list. See Alerting.escalationPolicyIdgroupIdimmediateAlerts{ "email": true, "push": true }.logServiceNamesmaintenanceWindows{ "scheduleType": "weekly", "days": ["Sun"], "startTime": "02:00", "endTime": "03:00", "timezone": "UTC" } or { "scheduleType": "once", "startTime": "<ISO>", "endTime": "<ISO>" }. On update it replaces them. Workspace-wide windows are in Maintenance.metadatacurl -X POST "https://api.zharp.io/api/v1/monitors" \
-H "Authorization: Bearer $ZHARP_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"type": "HTTP",
"name": "API health",
"url": "https://api.acme.io/health",
"interval": 60,
"regions": [
"eu",
"us"
],
"escalationPolicyId": 4,
"metadata": {
"method": "GET",
"expectedStatusCodes": [
200
],
"keyword": "ok"
}
}'{ "message": "Monitor created successfully", "data": { "id": 57, "status": "PENDING", … } }400A field fails validation, an alert connection, policy or group from outside the workspace, or a target on a private network (see below).403The plan's limit — monitors, user-journey monitors or the fastest interval.errorCodeislimitExceeded.
Monitor types
type sets what's checked; metadata holds that type's settings. The common ones are below — every setting the dashboard's form offers can be sent, and is shown on each type's page.
| type | Needs | Main metadata |
|---|---|---|
HTTP | url | method, headers, body, auth ({ username, password }), expectedStatusCodes, followRedirects, keyword, keywordShouldExist, jsonAssertions |
API, KEYWORD | url | As HTTP. |
PING | target | packetCount (1–10), slowResponseAlert, slowResponseThreshold |
PORT | target, port | protocol (tcp/udp), portSendString, portExpectedString |
SSL | target | sslExpiryWarnDays, sslPort |
DOMAIN | target | domainExpiryWarnDays, alertOnRegistrarChange |
DNS | target | dnsChecks (at least one), dnsRecordType, dnsServer |
POSTGRES, MYSQL, MONGODB, REDIS | target | dbName, dbUser, dbPassword (or redisPassword), dbQuery — or one dbConnectionString; connectionMode: "internal" with agentDatabaseId for a database the agent reports |
SMTP, IMAP, POP3 | target | emailUser, emailPass, emailEncryption |
SYNTHETIC | url | A user journey: syntheticCode (a Playwright script), synBrowser, synViewport |
MULTI_STEP | — | steps: a chain of API requests |
CRON | — | cronInterval and cronIntervalUnit (required), or cronMode: "expression" with cronExpression and cronTimezone; cronGracePeriod. See Heartbeats. |
CUSTOM | — | customMode: push (you send data — see push) or pull; triggerRules, expectedDataWindow |
LOGS | — | services and patterns (at least one each), logsEnvironments, logsFieldFilters |
APPLICATION | — | apmService, apmOperation, apmMetric (p95, p99, avg, errorRate, throughput), apmThreshold |
BROKEN_LINKS, VULNERABILITY, IP_BLACKLIST | url / target | crawlMaxDepth; scanType, minSeverity |
Two settings work on any type: confirmationPeriod and recoveryPeriod, the seconds a failure or a recovery must last before it counts.
/monitors/:idtype. metadata is merged: keys you send replace, a key sent as null is removed, the rest stay. The result must still meet the type's rules. "isActive": false pauses it; true resumes it.idcurl -X PUT "https://api.zharp.io/api/v1/monitors/57" \
-H "Authorization: Bearer $ZHARP_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"interval": 30,
"metadata": {
"keyword": "healthy"
}
}'{ "message": "Monitor updated successfully", "data": { "id": 57, "interval": 30, … } }400A field fails validation, or the change leaves the type's settings incomplete.403The interval is faster than the plan allows.404No such monitor in this workspace.
/monitors/:ididcurl -X DELETE "https://api.zharp.io/api/v1/monitors/57" \ -H "Authorization: Bearer $ZHARP_API_KEY"
{ "message": "Monitor deleted successfully", "data": null }404No such monitor in this workspace.
/monitors/bulk-deleteidscurl -X POST "https://api.zharp.io/api/v1/monitors/bulk-delete" \
-H "Authorization: Bearer $ZHARP_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"ids": [
57,
58
]
}'{ "message": "Monitor deleted successfully", "data": null }400idsmissing or not a list of ids.404One of them isn't in this workspace.
/monitors/:id/cloneidcurl -X POST "https://api.zharp.io/api/v1/monitors/57/clone" \ -H "Authorization: Bearer $ZHARP_API_KEY"
{ "message": "Monitor created successfully", "data": { "id": 61, "name": "API health (copy)", … } }403The plan's monitor limit.404No such monitor in this workspace.
/monitors/:id/triggeridcurl -X POST "https://api.zharp.io/api/v1/monitors/57/trigger" \ -H "Authorization: Bearer $ZHARP_API_KEY"
{ "message": "Check triggered successfully", "data": null }404No such monitor in this workspace.
/monitors/:id/toggle-muteidcurl -X POST "https://api.zharp.io/api/v1/monitors/57/toggle-mute" \ -H "Authorization: Bearer $ZHARP_API_KEY"
{ "message": "Alerts muted", "data": { "alertsMuted": true } }404No such monitor in this workspace.
/monitors/:id/test-notificationssent means queued for delivery — the connection's recent deliveries say whether it arrived.
idsendToIntegrationstrue.sendToSubscribersfalse.curl -X POST "https://api.zharp.io/api/v1/monitors/57/test-notifications" \
-H "Authorization: Bearer $ZHARP_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"sendToSubscribers": false
}'{
"message": "Test notification sent successfully",
"data": {
"connections": [
{ "channelId": 41, "name": "#ops", "type": "SLACK", "result": "sent" },
{ "channelId": 52, "name": "On-call", "type": "PAGERDUTY", "result": "skipped", "reason": "Paused" }
],
"people": []
}
}404No such monitor in this workspace.
/monitors/:id/reset-statsidcurl -X POST "https://api.zharp.io/api/v1/monitors/57/reset-stats" \ -H "Authorization: Bearer $ZHARP_API_KEY"
{ "message": "Statistics reset", "data": { "statsResetAt": "2026-10-26T10:00:00.000Z" } }404No such monitor in this workspace.
Checks, uptime and response times
/monitors/:id/logsidlimitcurl "https://api.zharp.io/api/v1/monitors/57/logs?limit=20" \ -H "Authorization: Bearer $ZHARP_API_KEY"
{
"message": "Check results retrieved",
"data": [
{
"id": 991,
"monitorId": 57,
"status": "UP",
"latency": 180,
"statusCode": 200,
"message": "OK",
"region": "eu",
"responseHeaders": { "content-type": "application/json" },
"metadata": { "timings": { "dns": 4, "connect": 21, "tls": 38, "firstByte": 112 } },
"timestamp": "2026-10-26T09:59:30.000Z"
}
]
}404No such monitor in this workspace.
/monitors/:id/statsPercentages run from 0 to 100. dailyHistogram has 24 hourly entries for 24h only.
idrange24h (default), 7d or 30d.curl "https://api.zharp.io/api/v1/monitors/57/stats?range=7d" \ -H "Authorization: Bearer $ZHARP_API_KEY"
{
"message": "Monitor statistics retrieved",
"data": {
"total": 10080,
"up": 10066,
"percentage": 99.86,
"dailyHistogram": [],
"stats7d": { "percentage": 99.9, "incidents": 1, "downtime": "4m" },
"stats30d": { "percentage": 99.95, "incidents": 2, "downtime": "12m" },
"stats365d": { "percentage": 99.97, "incidents": 5, "downtime": "2h" }
}
}404No such monitor in this workspace.
/monitors/:id/uptime-histogramtimestamp is the day's start in epoch seconds; uptime a percentage, or -1 for a day without checks.
iddayscurl "https://api.zharp.io/api/v1/monitors/57/uptime-histogram?days=7" \ -H "Authorization: Bearer $ZHARP_API_KEY"
{ "message": "…", "data": [{ "timestamp": 1761004800, "uptime": 99.93 }, { "timestamp": 1761091200, "uptime": -1 }, …] }404No such monitor in this workspace.
/monitors/:id/latencyidrange1h (default), 6h or 24h.curl "https://api.zharp.io/api/v1/monitors/57/latency?range=6h" \ -H "Authorization: Bearer $ZHARP_API_KEY"
{ "message": "Response times retrieved", "data": [{ "timestamp": 1761472800, "latency": 182 }, …] }404No such monitor in this workspace.
/monitors/:id/incidentsidlimitcurl "https://api.zharp.io/api/v1/monitors/57/incidents?limit=10" \ -H "Authorization: Bearer $ZHARP_API_KEY"
{ "message": "Monitor incidents retrieved", "data": [{ "id": 481, "title": "api.acme.io is down", "status": "RESOLVED", "startedAt": "…", "resolvedAt": "…", … }] }404No such monitor in this workspace.
/monitors/global-statisticsmonitorsPaused includes monitors in maintenance; totalPlanMonitors is 9999 for unlimited.
curl "https://api.zharp.io/api/v1/monitors/global-statistics" \ -H "Authorization: Bearer $ZHARP_API_KEY"
{
"message": "Global statistics retrieved successfully",
"data": { "monitorsUp": 12, "monitorsDown": 1, "monitorsPaused": 2, "totalPlanMonitors": 50, "totalUsedMonitors": 15, "avgLatency": 211 }
}/monitors/uptime-metricstotalTimeWithoutIncidentsis seconds since the last incident ended, 0 while one is open.incidentsByDaycovers this week, Sunday first.
range24h (default), 7d or 30d.curl "https://api.zharp.io/api/v1/monitors/uptime-metrics?range=30d" \ -H "Authorization: Bearer $ZHARP_API_KEY"
{
"message": "Uptime statistics retrieved successfully",
"data": { "overallUptime": 99.87, "totalIncidents": 3, "activeIncidents": 0, "totalTimeWithoutIncidents": 43200, "affectedMonitors": 2, "incidentsByDay": [0, 1, 0, 0, 2, 0, 0] }
}Heartbeats
A CRON monitor waits for your job to ping it; if no ping comes in time, it goes down. Its address is https://api.zharp.io/api/v1/ping/<pingToken>, with the token in the monitor's metadata.pingToken. No API key — the token is the credential. See Cron Jobs & Heartbeats.
/ping/:tokenPOST works the same and keeps the request body (JSON or plain text) as the run's output — its last 4,000 characters. A paused monitor answers 200 and records nothing.tokenpingToken.hostX-Host header.# Last line of the job curl -fsS --retry 3 "https://api.zharp.io/api/v1/ping/m_8f2c41d09ab3e5f7"
{ "message": "Heartbeat received and queued for processing", "data": { "nextExpected": "2026-10-26T11:05:00.000Z", "status": "UP" } }404No monitor has that token.
/ping/:token/:signalA failure takes the monitor down at once, with “Job failed with exit code N”, rather than waiting for the deadline.
tokenpingToken.signalstart — it started (the next ping is timed against this); fail — it failed; success — it succeeded; or an exit code 0–255, where 0 is success.curl -fsS "https://api.zharp.io/api/v1/ping/m_8f2c41d09ab3e5f7/start" ./backup.sh curl -fsS "https://api.zharp.io/api/v1/ping/m_8f2c41d09ab3e5f7/$?"
{ "message": "Heartbeat received and queued for processing", "data": { "nextExpected": "…", "status": "DOWN" } }400Not one of those signals.404No monitor has that token.
/push/:tokenCUSTOM with customMode: "push"). Its trigger rules run on the data straight away and may take it down or bring it back up. Its address uses metadata.pushToken.A paused monitor answers 200 with "status": "PAUSED" and evaluates nothing. With expectedDataWindow set, data that stops arriving counts as a failure.
tokenpushToken.(any JSON)curl -X POST "https://api.zharp.io/api/v1/push/m_51c0be7a2d94f816" \
-H "Content-Type: application/json" \
-d '{ "queueDepth": 1240, "workers": 3 }'{ "message": "Payload received and evaluated", "data": { "status": "DOWN", "firedTriggers": [ … ] } }404No push monitor has that token.
Monitor groups
/monitor-groupscurl "https://api.zharp.io/api/v1/monitor-groups" \ -H "Authorization: Bearer $ZHARP_API_KEY"
{
"message": "Monitor groups retrieved successfully",
"data": [{ "id": 3, "workspaceId": 3, "name": "Production", "description": "Customer-facing", "order": 0, "createdAt": "…", "updatedAt": "…", "_count": { "monitors": 8 } }]
}/monitor-groupsgroupId.namedescriptionordercurl -X POST "https://api.zharp.io/api/v1/monitor-groups" \
-H "Authorization: Bearer $ZHARP_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Production",
"description": "Customer-facing"
}'{ "message": "Monitor group created successfully", "data": { "id": 3, "name": "Production", … } }400A field fails validation, or an unknown field.
/monitor-groups/:ididcurl -X PUT "https://api.zharp.io/api/v1/monitor-groups/3" \
-H "Authorization: Bearer $ZHARP_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Prod"
}'{ "message": "Monitor group updated successfully", "data": { "id": 3, "name": "Prod", … } }404No such group in this workspace.
/monitor-groups/reordergroupIdscurl -X POST "https://api.zharp.io/api/v1/monitor-groups/reorder" \
-H "Authorization: Bearer $ZHARP_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"groupIds": [
5,
3,
9
]
}'{ "message": "Monitor groups reordered", "data": null }400groupIdsisn't a list of ids.
/monitor-groups/:ididcurl -X DELETE "https://api.zharp.io/api/v1/monitor-groups/3" \ -H "Authorization: Bearer $ZHARP_API_KEY"
{ "message": "Monitor group deleted successfully", "data": null }404No such group in this workspace.