---
title: "Escalation policies — Alerts for Laravel | Roundly"
description: "Notify wider groups as a failure persists — threshold ⇒ group policies, named groups on the owner, a global default and exactly-once events."
url: https://roundly-consulting.com/open-source/docs/alerts-for-laravel/escalation
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)

# Escalation policies

Notify wider audiences as a failure persists. Declare a threshold ⇒ group map with escalate(\[...\]): the keys are consecutive-failure counts, the values are named notifiable groups your owner model resolves:

```php
Health::for($team)->monitor(DatabaseCheck::class)
    ->everyMinute()
    ->escalate([1 => 'owner', 3 => 'team', 5 => 'oncall'])
    ->save();
```

```php
use Closure;
use Illuminate\Database\Eloquent\Model;
use RoundlyConsulting\Alerts\Interfaces\HasNotifiablesForAlerts;
use RoundlyConsulting\Alerts\Traits\UsesHealthChecks;

class Team extends Model implements HasNotifiablesForAlerts
{
    use UsesHealthChecks; // provides a default notifiablesForAlertGroup()

    public function forEachNotifiableForAlerts(Closure $callback): void { /* default group */ }

    // Override to map named groups to real notifiables:
    public function notifiablesForAlertGroup(string $group): iterable
    {
        return match ($group) {
            'owner'  => [$this->owner],
            'team'   => $this->members,
            'oncall' => $this->onCallEngineers(),
            default  => $this->members,
        };
    }
}
```

The ResolvesAlertGroups trait, bundled into UsesHealthChecks, returns the default group for any name. Owners need no change unless they want named groups.

Every threshold must be a whole number of at least 1. escalate() — on a monitor or on define() — and ScheduleHealthCheckData refuse 0, a negative number, a name or a list-style \['owner', 'team'\] (keyed 0 and 1) with InvalidHealthCheck, since such a group could never be paged.

## How levels advance

- The level is the highest threshold at or below the current consecutive-failure count. It is stored on the alert as escalation\_level and resets to 0 on recovery.
- Each newly reached level notifies only its own group(s). If one run jumps several thresholds — say failAfter(3) with \[1 => 'owner', 3 => 'team'\] — every group in between is notified once.
- HealthCheckEscalated($alert, $fromLevel, $toLevel) fires exactly once per transition. The level is claimed with a compare-and-swap, so two overlapping runs never page a tier twice.
- With a policy, nobody is notified before the first threshold is reached. Start at 1 if the very first alerting failure should reach someone. HealthCheckFailed still fires on every failing run.
- Channels for a level come from notifyVia(\[...\], level: n), where n is the threshold number.

## A global default

A check that declares no policy of its own inherits the global default from config('alerts.escalation'), so you can set one policy for the whole application and override it per check:

```php
// config/alerts.php
'escalation' => [1 => 'owner', 3 => 'team', 5 => 'oncall'],
```

The policy is data only — routing to real people always happens through the owner’s notifiablesForAlertGroup().

## Reacting to escalation

```php
use Illuminate\Support\Facades\Event;
use RoundlyConsulting\Alerts\Events\HealthCheckEscalated;

Event::listen(function (HealthCheckEscalated $event) {
    logger()->warning('Health check escalated', [
        'check' => $event->alert->healthCheck?->health_check,
        'from' => $event->fromLevel,
        'to' => $event->toLevel,
    ]);
});
```

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