Monitors
A monitor is one thing being watched and its current verdict. Health checks and heartbeats are the same resource here, separated by type, because that is how they are read: one list answering whether anything is wrong right now. On this page we will go through the endpoints for creating, reading and removing them.
The monitor model #
- id integer
-
Unique identifier for the monitor.
- type string
-
httpfor a health check,heartbeatfor a job that reports in. Fixed at creation. - name string
-
What the monitor is called in the Console and in notifications.
- is_active boolean
-
Whether the monitor is being checked. An inactive monitor holds no place against your plan’s limit.
- current_status string
-
pendinguntil the first verdict, thenupordown. - monitorable_type string
-
The kind of thing being watched:
App\Models\ProjectorApp\Models\Domainfor a health check,App\Models\SchedulerJoborApp\Models\BackgroundJobfor a heartbeat. - monitorable_id integer
-
The id of that thing. Fixed at creation, along with
monitorable_type. - config object
-
Type-specific settings — see the two tables below.
- ping_url string
-
Heartbeats only: the address the watched job reports to. Depfloy puts it into the job for you; it is here so you can see what was installed.
- last_checked_at timestamp
-
When the monitor was last evaluated.
- last_ping_at timestamp
-
Heartbeats only: when a report last arrived.
- last_status_changed_at timestamp
-
When
current_statuslast changed. This is what “down for 20 minutes” is measured from. - uptime_24h number
-
Percentage of the last 24 hours the monitor was up, or
nullwhen there is not enough history. Present on list and read responses. - uptime_7d number
-
The same figure over the last 7 days.
Health check config #
- url string
-
The address to request.
- method string
-
GET,HEADorPOST. Defaults toGET. - expected_status integer
-
The status that counts as healthy, between 100 and 599. Omit it and any 2xx passes. Redirects are not followed, so a URL that answers
301fails unless you ask for301here. - interval_seconds integer
-
How often to check, up to 86400. The fastest value depends on the plan — 60 on Pro and Business, 300 on Starter — and
GET /api/v1/monitors/optionsreports the range yours allows. Asking for less is refused with a422. A monitor already set faster than its plan permits keeps the value and is checked at the plan’s frequency. - timeout_seconds integer
-
How long to wait for an answer, from 1 to 60. Defaults to 10.
- confirmation_threshold integer
-
Consecutive failures before the monitor is reported down, from 1 to 10. Defaults to 3.
Heartbeat config #
Every field is optional. For a scheduled job the timing is read from its cron expression, so the common create carries no config at all.
- expected_period_seconds integer
-
How often a report is expected, from 60 to 2592000. Derived from the job’s schedule when you omit it. Supply it for a job that usually finishes in seconds but occasionally takes an hour — nobody but its owner knows that.
- grace_seconds integer
-
How late a report may be before it counts against the monitor, from 30 to 86400. For a scheduled job it defaults to half the period and never less than 60; a background job, whose listener reports every minute, defaults to 150.
- confirmation_threshold integer
-
Consecutive failed evaluations before the monitor is reported down, from 1 to 10. Defaults to 3.
/api/v1/monitorsList monitors #
Returns the monitors you can reach, 25 per page, with the ones that are down first — the question a monitor list answers is whether anything is wrong right now, and an alphabetical sort buries that.
Requires monitor:read.
Optional attributes #
- type string
-
Narrow to
httporheartbeat. - status string
-
Narrow to
up,downorpending. - page integer
-
Page to fetch.
curl https://app.depfloy.com/api/v1/monitors?status=down \
-H "Authorization: Bearer {API_KEY}" \
-H "Accept: application/json"{
"current_page": 1,
"data": [
{
"id": 14,
"type": "heartbeat",
"name": "Nightly invoices",
"is_active": true,
"current_status": "down",
"monitorable_type": "App\\Models\\SchedulerJob",
"monitorable_id": 87,
"config": {
"expected_period_seconds": 86400,
"grace_seconds": 43200
},
"ping_url": "https://app.depfloy.com/api/ping/9f2c…",
"last_ping_at": "2026-08-23T02:00:04+00:00",
"last_status_changed_at": "2026-08-24T14:03:11+00:00",
"uptime_24h": 41.75,
"uptime_7d": 91.68
}
],
"per_page": 25,
"total_items": 1,
"total_pages": 1
}/api/v1/monitors/:idRead a monitor #
Returns one monitor with its target and its 50 most recent events, newest first. An event is written when the status changes, so this is the outage history rather than a log of every check.
Each event carries a status of down or up, the message explaining it, a context object with whatever the check saw — the HTTP status, the latency, the error — and resolved_at, which is filled in on the outage when it ends.
Requires monitor:read. A monitor in another organization, or one whose project you cannot reach, answers 404.
curl https://app.depfloy.com/api/v1/monitors/14 \
-H "Authorization: Bearer {API_KEY}" \
-H "Accept: application/json"{
"status": "success",
"data": {
"id": 14,
"type": "heartbeat",
"name": "Nightly invoices",
"current_status": "down",
"events": [
{
"id": 502,
"status": "down",
"message": "No report since 2026-08-23 02:00:04 UTC.",
"context": { "expected_period_seconds": 86400 },
"resolved_at": null,
"created_at": "2026-08-24T14:03:11+00:00"
}
]
}
}/api/v1/monitorsCreate a monitor #
Creates a health check or a heartbeat. The new monitor starts at pending and stays quiet until it has enough evidence for a verdict.
Creating a heartbeat also installs the reporting: a scheduled job’s crontab line is rewritten to report in, and a server that gains its first watched background job gains the listener that reports for it.
Requires monitor:manage. A target you cannot reach answers 404, and exceeding your plan’s limit for that type answers 403 with the number you are allowed.
Attributes #
- type string
-
httporheartbeat. - name string
-
Up to 100 characters.
- monitorable_type string
-
What to watch. A health check takes
App\Models\ProjectorApp\Models\Domain; a heartbeat takesApp\Models\SchedulerJoborApp\Models\BackgroundJob. - monitorable_id integer
-
The id of that record.
- config object
-
Required for a health check, optional for a heartbeat. See the tables above.
- is_active boolean
-
Defaults to
true.
curl -X POST https://app.depfloy.com/api/v1/monitors \
-H "Authorization: Bearer {API_KEY}" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"type": "http",
"name": "Storefront",
"monitorable_type": "App\\Models\\Project",
"monitorable_id": 412,
"config": {
"url": "https://shop.example.com",
"expected_status": 200,
"interval_seconds": 300
}
}'{
"status": "success",
"message": "Monitor created",
"data": {
"id": 15,
"type": "http",
"name": "Storefront",
"current_status": "pending",
"is_active": true
}
}{
"status": "error",
"message": "Your plan allows 1 active Health Check monitor(s). Turn one off or upgrade your plan to add another."
}/api/v1/monitors/:idUpdate a monitor #
Changes name, config, notification_channels or is_active.
type, monitorable_type and monitorable_id are rejected. They are settled at creation: a monitor pointed at something else would carry an event history describing two different things as though they were one.
Turning a monitor back on returns it to pending and clears its counters, so its first check after the gap is judged on its own evidence rather than on failures from weeks ago. It also has to fit your plan’s limit again.
Requires monitor:manage.
curl -X PUT https://app.depfloy.com/api/v1/monitors/15 \
-H "Authorization: Bearer {API_KEY}" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"is_active": false
}'{
"status": "success",
"message": "Monitor updated",
"data": {
"id": 15,
"is_active": false
}
}/api/v1/monitors/:idDelete a monitor #
Removes the monitor and its events. For a heartbeat this also takes the reporting back out: the crontab line is rewritten without it, and a server whose last watched background job is removed loses the listener.
Requires monitor:manage.
curl -X DELETE https://app.depfloy.com/api/v1/monitors/15 \
-H "Authorization: Bearer {API_KEY}" \
-H "Accept: application/json"{
"status": "success",
"message": "Monitor deleted"
}/api/v1/monitors/optionsMonitor options #
Returns the monitor types and the targets each one accepts, so a client can build a create form without hardcoding the catalog.
It also reports what the caller’s own plan allows: interval.min_seconds, interval.max_seconds and interval.default_seconds for the check frequency, and monitor_limit for how many active monitors of one type the plan covers (null when unlimited). Reading them here keeps a client from hardcoding numbers that differ per plan.
Requires monitor:read.
curl https://app.depfloy.com/api/v1/monitors/options \
-H "Authorization: Bearer {API_KEY}" \
-H "Accept: application/json"