Status reports & health endpoint
Build a report of every scheduled check with Health::report(), or of one owner’s checks with Health::for($owner)->report() — both optionally filtered by tags:
use RoundlyConsulting\Alerts\Facades\Health;
$report = Health::report(); // HealthReport across every scheduled check
$report->overall(); // worst Status across all checks
$report->isHealthy(); // bool
foreach ($report->checks() as $check) {
// CheckStatus: key, name, status, lastAlertAt, message, tags, uptime, p95LatencyMs, muted
}
Health::status(); // Status roll-up
Health::report(['critical']); // only checks tagged with one of these (row tags or the check's tags())
Health::for($team)->report(['critical']); // one owner, optionally tag-filtered
Health::for($team)->status(); // that owner's roll-up
$report->whereTag('db'); // filter an in-memory reportThe report is built from the health_checks rows — on-demand rows included — not from the registry. Each row’s status is the status of its open alert, or ok when none is open — so failures below failAfter() still read ok. An open alert follows the latest failing result, so a warning that turns into a failure reports as failed. overall() is the worst status by severity, and isHealthy() is true when nothing is alertable.
| CheckStatus property | Meaning |
|---|---|
key | The check key of the scheduled row. |
name | The registered check’s name(), or the key when it is not registered. |
status | The status of the row’s open alert — it follows the latest failing result — or Status::Ok when none is open. |
lastAlertAt | When that open alert was triggered (null when none). |
message | The open alert’s message, from the latest failing result. |
tags | Effective tags — row tags merged with the check’s tags(). |
uptime | uptimePercentage() over the full run history. |
p95LatencyMs | p95LatencyMs() over the full run history. |
muted | True when the open alert’s latest failing run happened during a mute. |
- Health::report(['critical']) and Health::for($team)->report(['critical']) keep rows tagged with any of the given tags — on the row or by the check’s tags(); status(), alerts:status --tag and /health?tag= filter the same way.
- $report->whereTag('db') filters a built report on effective tags — row tags merged with the check class’s tags().
- HealthReport::toArray() and CheckStatus::toArray() give the JSON shape the endpoint returns.
On the command line
A CLI summary that exits non-zero when anything is alertable — handy in CI or uptime probes:
php artisan alerts:status
php artisan alerts:status --tag=db # filter by tagJSON endpoint
An opt-in endpoint that returns 200 when healthy and 503 otherwise. Register it from your own routes file — the package never adds routes by default:
// routes/web.php
use RoundlyConsulting\Alerts\Facades\Health;
Health::routes(); // GET /health -> { "status": "...", "checks": [...] }
// GET /health?tag=critical -> only checks tagged "critical" (on the row or by the check's tags())
// Or on your own URI, behind your own middleware (routes() returns the Route):
Health::routes('internal/health')->middleware('auth.basic');{
"status": "warning",
"checks": [
{
"key": "http_ping_check",
"name": "Http Ping Check",
"status": "warning",
"last_alert_at": "2026-09-26T08:15:00+00:00",
"message": "Endpoint [https://api.example.com] responded slowly (912 ms).",
"tags": ["critical"],
"uptime": 99.31,
"p95_latency_ms": 840,
"muted": false
}
]
}The endpoint reports every scheduled check across all owners and is unauthenticated by design. Put it behind middleware or an unguessable URI if the check names should stay private. The URI and route name default to route.uri and route.name from the config.
Show your open-source love
This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.
More ways to support, including cryptoBy donating, you agree to our donation terms.
Want this built into your product?
We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.