Recipes

Everything the package knows comes out through the two events and ApiKeyAlert::failure(). These examples build on that; copy what fits.

An alert in Slack

Listen to the events and post to an incoming webhook. ApiKeyFailed fires on every detection; alerted is true only for the one that also mailed, so you get one Slack message per throttle window too.

// app/Listeners/PostApiKeyAlertToSlack.php
namespace App\Listeners;

use Darvis\LaravelApiKeyAlert\Events\ApiKeyFailed;
use Darvis\LaravelApiKeyAlert\Events\ApiKeyRecovered;
use Illuminate\Support\Facades\Http;

class PostApiKeyAlertToSlack
{
    public function handle(ApiKeyFailed|ApiKeyRecovered $event): void
    {
        if ($event instanceof ApiKeyFailed && ! $event->alerted) {
            return;
        }

        $text = $event instanceof ApiKeyFailed
            ? ":rotating_light: {$event->service} rejected the API key: {$event->reason}"
            : ":white_check_mark: {$event->service} accepts the API key again";

        Http::post(config('services.slack.alerts_webhook'), ['text' => $text]);
    }
}
// app/Providers/AppServiceProvider.php
use Darvis\LaravelApiKeyAlert\Events\ApiKeyFailed;
use Darvis\LaravelApiKeyAlert\Events\ApiKeyRecovered;
use Illuminate\Support\Facades\Event;

public function boot(): void
{
    Event::listen([ApiKeyFailed::class, ApiKeyRecovered::class], PostApiKeyAlertToSlack::class);
}

The reason is already redacted. Leave API_KEY_ALERT_MAIL_TO empty if Slack is enough; the events fire without a recipient.

A check for spatie/laravel-health

If the app already uses spatie/laravel-health, one check puts the keys on the same dashboard and in the same notifications.

// app/Health/ApiKeysCheck.php
namespace App\Health;

use Darvis\LaravelApiKeyAlert\Facades\ApiKeyAlert;
use Darvis\LaravelApiKeyAlert\Support\ApiKeyAlertConfig;
use Spatie\Health\Checks\Check;
use Spatie\Health\Checks\Result;

class ApiKeysCheck extends Check
{
    public function run(): Result
    {
        $failing = array_filter(ApiKeyAlertConfig::services(), fn (string $service) => ApiKeyAlert::isFailing($service));

        return $failing === []
            ? Result::make()->ok('All API keys are accepted')
            : Result::make()->failed('Rejected API key: '.implode(', ', $failing));
    }
}
// app/Providers/AppServiceProvider.php
use App\Health\ApiKeysCheck;
use Spatie\Health\Facades\Health;

Health::checks([
    ApiKeysCheck::new(),
]);

The check reads the cache, so it is free to run every minute.

A warning in your admin

For a notice at the top of an admin layout, ask the package in the view. failure() returns null when the key works, so the block renders nothing most of the time.

@if ($failure = \Darvis\LaravelApiKeyAlert\Facades\ApiKeyAlert::failure('google'))
    <div class="rounded bg-red-50 p-4 text-red-800">
        The Google Maps key has been rejected since {{ $failure->since }}: {{ $failure->reason }}
    </div>
@endif

A deploy that stops on a dead key

api-key-alert:status exits with a failure when a key is down, so a deploy script can refuse to continue, and api-key-alert:check tests the keys against the live APIs first:

php artisan api-key-alert:check && php artisan api-key-alert:status

For a monitoring tool that wants data, --json gives it:

php artisan api-key-alert:status --json