---
title: "Status reports & health endpoint — Alerts for Laravel | Roundly"
description: "Read the current health report in code, on the CLI or as an opt-in JSON endpoint that returns 200 or 503 and filters by tag."
url: https://roundly-consulting.com/open-source/docs/alerts-for-laravel/status-reports
language: en
---

[All packages](https://roundly-consulting.com/open-source.md)

[Alerts for Laravel](https://roundly-consulting.com/open-source/docs/alerts-for-laravel.md)

# Status reports & health endpoint

Build a report of every monitored check with Health::report(), or of one owner’s checks with Health::for($owner)->report() — both optionally filtered by tags, limited to a window with since: and to scheduled checks with scheduledOnly: true:

```php
use RoundlyConsulting\Alerts\Facades\Health;

$report = Health::report();                  // HealthReport across every monitored check
$report->overall();                          // worst Status across all checks (skipped never outranks ok)
$report->isHealthy();                        // bool

foreach ($report->checks() as $check) {
    // CheckStatus: key, name, status, lastAlertAt, message, tags, uptime, p95LatencyMs, muted, meta
}

Health::status();                            // Status roll-up
Health::report(['critical']);                // only checks tagged with one of these (row tags or the check's tags())
Health::report(since: now()->subDay());      // uptime and p95 over the last day only
Health::report(scheduledOnly: true);         // leave out the on-demand rows run() creates
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, in id order — on-demand rows included unless scheduledOnly: true — not from the registry. Each row’s status is the status of its newest open alert, or ok when none is open — so failures below failAfter() still read ok, and a failed on-demand run counts until a passing run() of the same check for that owner closes its alert (or unmonitor() removes the row). 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, ignoring skipped — a report of ok and skipped checks, or of skipped ones only, is ok — 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 newest 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` | ?float — the share of non-alertable runs in the report’s window, two decimals; null when the row has no run in it, or history is off. |
| `p95LatencyMs` | ?int — the nearest-rank 95th-percentile run duration in the same window; null without runs. |
| `muted` | True when a silence in force covers the row — by the run pipeline’s own rule, with or without an open alert. |
| `meta` | The open alert’s raw meta, null without an open alert. With store\_exception\_details on it can hold exception details — sanitise it before showing it to users. |

- 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() always give the full shape, meta included; the endpoint renders only an allow-list of it (below).

## Windows, statistics and query count

since: limits uptime and p95LatencyMs to the runs at or after that moment; without it they cover the whole retained history. A row with no run in the window — or none at all, or with history.enabled off — reports both as null, so a check that never ran no longer looks perfect. status() takes no window and is unchanged.

However many rows and runs there are, the report takes a fixed number of queries — at most five: the rows, their open alerts, one run aggregate, one nearest-rank p95 read with a window function, and one silence snapshot, with row ids batched by 1000. The run queries are skipped with history off and the snapshot with silence off. The JSON endpoint and alerts:status build a report on every hit, so this keeps them cheap.

## Caching the report

For a cache that stores arrays only (cache.serializable\_classes off), store toArray() and read it back with HealthReport::tryFromArray() — or CheckStatus::tryFromArray() for one row. A payload they cannot read is null — a cache miss, never an error — and the stored overall status is ignored and recomputed:

```php
use Illuminate\Support\Facades\Cache;
use RoundlyConsulting\Alerts\Facades\Health;
use RoundlyConsulting\Alerts\Status\HealthReport;

$report = HealthReport::tryFromArray(Cache::get('health'))
    ?? tap(Health::report(since: now()->subDay(), scheduledOnly: true),
        fn (HealthReport $r) => Cache::put('health', $r->toArray(), 60));
```

## On the command line

A CLI summary that exits non-zero when anything is alertable — handy in CI or uptime probes:

```bash
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:

```php
// 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');
```

```json
{
    "status": "warning",
    "checks": [
        {
            "key": "http_ping_check",
            "name": "Http Ping Check",
            "status": "warning",
            "uptime": 99.31,
            "p95_latency_ms": 840
        }
    ]
}
```

By default each check renders only key, name, status, uptime and p95\_latency\_ms — uptime and p95\_latency\_ms are null for a check with no runs. Set route.details (ALERTS\_ROUTE\_DETAILS=true) to add message, tags, last\_alert\_at and muted. That is an explicit allow-list: the raw alert meta is never rendered here, details or not. Still keep details behind middleware — with store\_exception\_details on, a stored alert message can carry raw exception text such as hosts or database names.

The endpoint is unauthenticated unless you chain middleware onto the Route that routes() returns. It reports every monitored check across all owners, so one owner’s failing check turns the whole endpoint 503 — narrow it with ?tag=. 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](https://roundly-consulting.com/support-us.md)

By donating, you agree to our [donation terms](https://roundly-consulting.com/donation-terms.md).

[Support our open source work (opens in a new tab)](https://donate.stripe.com/dRmeVe8FX5PF1Qd9pXcEw00) [Join us on Patreon (opens in a new tab)](https://www.patreon.com/cw/roundly)

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

[Get a quote in 48 hours](https://roundly-consulting.com/contact.md) [Browse all packages](https://roundly-consulting.com/open-source/docs/alerts-for-laravel.md)
