Webhooks · Open source
Laravel Webhook Console
A dashboard and replay tool for the webhooks spatie/laravel-webhook-client stores in your Laravel app.
What it solves
spatie/laravel-webhook-client stores every webhook your app accepts in the webhook_calls table, but has no screen to look at them.
When your process job throws on a real queue worker, the call keeps no exception and the failure only exists in failed_jobs, with nothing that points back to the webhook.
An unofficial, independent dashboard for spatie/laravel-webhook-client (not made or endorsed by Spatie). It lists every stored call with its status, links failed_jobs rows back to their calls, shows payloads and headers with secrets masked, and replays one call or every failure from the dashboard or artisan.
- List and filter stored calls by config, status, date range, or a search inside the payload and URL
- Failures include process jobs that failed on a queue worker, linked from failed_jobs without unserializing them
- Secrets masked in payloads, headers and URLs, and inside exception messages, stack traces and failed_jobs text
- Replay one call, the selected calls, or every failure: the call's process_webhook_job is dispatched again
- Viewing opens in the local environment only; replay and prune stay off until you define their gates for signed-in users
- webhooks:failed, webhooks:replay and webhooks:prune with documented exit codes for CI and schedules
Install
Requires PHP 8.3+ and Laravel 12 and 13.
composer require amjadiqbal/laravel-webhook-console
Usage
// app/Providers/AppServiceProvider.php: who may use /webhook-console outside local
use App\Models\User;
use Illuminate\Support\Facades\Gate;
Gate::define('viewWebhookConsole', fn (User $user) => $user->is_admin);
Gate::define('replayWebhookCalls', fn (User $user) => $user->is_admin);
Gate::define('pruneWebhookCalls', fn (User $user) => false); // view and replay only
Setup
This is an independent package, not made by Spatie. It requires spatie/laravel-webhook-client 3.4+ (installed for you) that is already receiving webhooks. After installing, open /webhook-console in your local environment. Queue failures are read from the database or database-uuids failed job driver; with other drivers the dashboard says why they are missing.
- 1 composer require amjadiqbal/laravel-webhook-console
- 2 Open /webhook-console locally.
- 3 To replay or prune, or to open it outside local, define the gates.
- 4 Optional: php artisan vendor:publish --tag=webhook-console-config
Access: three gates
viewWebhookConsole allows the dashboard, payloads, headers and failures, and defaults to the local environment only. replayWebhookCalls (Replay, Replay selected, Retry failed) and pruneWebhookCalls (deleting old calls) are denied everywhere until you define them, and also need a signed-in user unless you set dashboard.actions_require_user to false. Out of the box you can look at your webhooks locally and change nothing. Someone who can view but not replay sees the dashboard without replay buttons, and the replay and prune routes return 403. The view gate is always added after dashboard.middleware.
use App\Models\User;
use Illuminate\Support\Facades\Gate;
Gate::define('viewWebhookConsole', fn (User $user) => $user->is_admin);
Gate::define('replayWebhookCalls', fn (User $user) => $user->is_admin);
Failure statuses
- Failed: webhook-client stored an exception on the call.
- Failed in queue: the process job failed on a queue worker and is in failed_jobs. The console links the row to its call from the serialized job's model id, read with a regex and never unserialized.
- No error: nothing failed that the console can see.
- After a replay, an older failed_jobs row no longer counts against the call; the failed_jobs rows themselves are never changed or deleted.
Replay and bulk retry
Replay dispatches the call's configured process_webhook_job again, the way webhook-client does for a new webhook. Nothing is sent over the network and the signature is not checked again, so your job should be safe to run twice for the same event. Retry failed replays every failure matching the current filters, oldest first, up to bulk_limit (100). From artisan, webhooks:replay asks before replaying, needs --force when not interactive, and exits 1 when a call failed to dispatch or was skipped, or you answered no (2 for missing or bad options).
php artisan webhooks:replay 41 42
php artisan webhooks:replay --failed --dry-run
php artisan webhooks:replay --failed --since=2h --force
Monitoring, pruning and masking
Schedule webhooks:failed --strict to be told about failures, and webhooks:prune to delete calls older than N days (default: webhook-client's delete_after_days), for all configs or one; --pretend only counts. Pruning from the dashboard or artisan is a mass delete, so no model events fire. When failed_jobs holds more rows than failed_jobs.scan_limit, the dashboard and webhooks:failed say that older queue failures were not checked. Masking applies to what is shown, copied, searched and printed; stored rows are not changed, and search only matches the masked text.
// routes/console.php
Schedule::command('webhooks:failed --since=1h --strict')->hourly()->emailOutputOnFailure('[email protected]');
Schedule::command('webhooks:prune --days=30 --force')->daily();
Hire me
Need this built into your app, or something custom?
I wrote Laravel Webhook Console. I can wire it into your Laravel app, extend it for your case, or build the feature around it. Tell me what you need and you'll get a plan and a quote, free.
Support
Found a bug or want a feature? Open an issue on GitHub. For help adding it to your own project, use the quote form above.
More Laravel packages
All packages
Laravel Driver.js
Build Driver.js product tours in PHP and render them with one Blade component.
Laravel Sonner
Sonner toast notifications for Laravel, sent from PHP and shown without React.
Laravel Ninja Keys
A Cmd+K / Ctrl+K command palette for Laravel, with the actions defined in PHP.
Laravel SweetAlert2
SweetAlert2 popups, toasts and confirm dialogs from Laravel controllers, Livewire and Alpine.
Laravel Grid.js
Turn any Eloquent query into a searchable, sortable, paginated Grid.js data table with one fluent PHP call.
Laralink
Work on a Composer package inside the Laravel app that uses it, then switch back to the Packagist release.
Refresher
A drop-in replacement for Laravel's RefreshDatabase that restores a cached snapshot of your test database instead of running migrate:fresh.
Kiln
Artisan commands and an optional signed HTTP route to clear, warm and inspect OPcache when you deploy.
Laravel Media Viewer
Show private PDFs, video and audio from any Laravel disk without making the files public.
Laravel Swiper
Swiper sliders, carousels and image galleries for Laravel as Blade components.
Laravel MySQL Advisor
Read-only health checks for the MySQL or MariaDB database behind your Laravel app, with the SQL and the Laravel migration to fix a problem wherever a fix applies.
Laravel Pennant Dashboard
A browser dashboard for laravel/pennant feature flags: per-scope toggles, sticky percentage rollouts, purge and an audit log.
Laravel Notification Inbox
Catches every non-mail Laravel notification in local and staging instead of sending it, and shows each one as it would look on the other end.
Laravel WebPush Kit
The browser side of Laravel web push: a subscribe button with a two-step permission prompt, a service worker, and routes that save each user's subscription. Tested in Chrome so far.
Laravel Redis Advisor
Read-only health checks for the Redis or Valkey behind your Laravel cache, queues, sessions and Horizon, with the redis.conf line or Laravel config that fixes each problem.