NewWe open-sourced 50+ Laravel packages
Custom AI apps, agents and automation — Roundly ConsultingRoundly
All packages
Alerts for Laravel

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 report

The 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 propertyMeaning
keyThe check key of the scheduled row.
nameThe registered check’s name(), or the key when it is not registered.
statusThe status of the row’s open alert — it follows the latest failing result — or Status::Ok when none is open.
lastAlertAtWhen that open alert was triggered (null when none).
messageThe open alert’s message, from the latest failing result.
tagsEffective tags — row tags merged with the check’s tags().
uptimeuptimePercentage() over the full run history.
p95LatencyMsp95LatencyMs() over the full run history.
mutedTrue 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 tag

JSON 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 crypto

By 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.