jeffersongoncalves/filament-webhooks
A Filament plugin that adds a webhook management UI to your Laravel admin panel: list endpoints, fire test calls, and inspect delivery logs and responses without leaving Filament, making third-party integrations easier to debug and maintain.
Install
composer require jeffersongoncalves/filament-webhooks
Register the plugin in your panel provider (app/Providers/Filament/AdminPanelProvider.php):
use JeffersonGoncalves\FilamentWebhooks\WebhooksPlugin;
public function panel(Panel $panel): Panel
{
return $panel
// ...
->plugin(WebhooksPlugin::make());
}
Publish and migrate. Check the package's service provider for tags before running blind — the usual Filament layout is:
php artisan vendor:publish --tag="filament-webhooks-config"
php artisan vendor:publish --tag="filament-webhooks-migrations"
php artisan migrate
php artisan filament:optimize-clear # or: php artisan optimize:clear
Where to look first, in order
config/filament-webhooks.php — table names, queue connection/name, retry counts, timeouts, signing toggle. This is the file that answers 80% of "why didn't it fire?" questions.resources/lang/ — as of 3.1.0 this ships en plus ar, az, de, es, fa, fr, hi, it, ja, nl, pl, pt, ru, tr, uk, uz, zh_CN.First use case (5 minutes)
https://webhook.site/<your-uuid>.order.created).Order::first()->update(['status' => 'paid']); — or dispatch whatever event your endpoint subscribes to.php artisan queue:work) and watch the delivery row appear in the panel with status, response code, and body.If step 3 succeeds but step 4 shows nothing, it is almost always the queue, not the plugin.
Let the UI own the endpoint list, let code own the event names. Endpoints and their subscribed events are runtime data — they belong in the database and the admin panel. The set of available event names is code. Expose them from a single source (a config array or an enum) and have your dispatch site validate against it so a typo in the panel fails loudly instead of silently never matching.
Dispatch through one seam. Rather than calling Http::post() from a dozen observers, funnel every outbound hook through a single helper/job:
Webhook::dispatch('order.created', $order->toWebhookPayload());
This gives you one place to add queueing, signing, retries, tenant scoping, and observability later.
Queue everything, always. Webhook delivery is network I/O against a third party. Do it in a job on a dedicated queue (webhooks) so a slow customer endpoint can never starve your default queue. Configure the connection and queue name in the config rather than relying on defaults.
Verify on the receiving side with HMAC, and design for rotation. If the package signs payloads, compute the signature over the raw request body — not over json_decode/json_encode, which reorders keys. Store two active secrets per endpoint so you can rotate without dropping in-flight deliveries:
foreach ($endpoint->secrets as $secret) {
if (hash_equals(hash_hmac('sha256', $raw, $secret), $header)) {
return true;
}
}
Fire after commit. If your events are dispatched inside a DB transaction, the receiver can be told about an order that then rolls back. Add $afterCommit = true on the event class or dispatch from an DB::afterCommit() callback.
Model the payload explicitly. Don't serialize whole models. Build a small toWebhookPayload(): array (or a dedicated Resource class) per event type so adding a column never accidentally leaks a password or remember_token to a customer's server.
Tenancy/authorization. In a multi-tenant panel, scope the webhook resource query by tenant_id and register a policy on the endpoint model. Outbound URL creation is a privilege — treat create permission on webhooks as equivalent to manage integrations, not as a normal editor right.
Testing without the network. Two patterns, both cheap:
Queue::fake(); // assert the job was queued with the right event
Http::fake(); // or let it run and assert the outbound request shape
Prefer Http::fake() at least once per event type — it catches payload keys you renamed and forgot to update.
Localization (new in 3.1.0). Set app.locale to one of the 18 supported locales and the resource labels, form fields, and status strings follow automatically. To override a string without forking the package, publish the translation files using the package's lang tag and edit only the keys you care about; missing keys fall back to en, so partial overrides are safe. If you maintain custom locales, keep your keys aligned with the shipped en file when you upgrade.
Extension points worth checking before you patch vendor code. Filament plugin ecosystems usually expose the resource/navigation registration on the plugin object (navigation group, icon, slug, ->authorize()), and the config file typically lets you swap the endpoint model or the job class. Subclass and re-register rather than editing published files.
queue:work --queue=default,webhooks or give webhooks their own supervisor process.php artisan optimize:clear. Filament plugins that appear "missing" in production are usually a stale cached panel config.filament:optimize order matters in deploy scripts — clear cached components before the new config is cached, or the resource list can drift from the code.127.0.0.0/8, 10/8, 172.16/12, 192.168/16, 169.254/16, ::1) at validation time. Otherwise a low-privileged panel user can probe your internal network from your app server.encrypted casts for the secret column, and grep your payload/response logs — the panel stores response bodies, so anything sensitive the receiver echoes back is now in your DB and visible to panel users with the right policy.model:prune on deliveries older than N days) from day one.attempts = 0? no → job never ran, check failed_jobs and worker logs; (3) got a status code? → the problem is on the receiver. The delivery log is the whole debugger; don't reach for Log::info before reading it.expose, ngrok) or Http::fake() in tests — don't disable TLS verification on the endpoint to "make it work"; the package's HTTP client config should not be a place to relax verify.php artisan optimize:clear after composer install; otherwise upgrade cost is zero. If you forked or overrode the package's English lang files, re-check for key additions before deploying so your overrides stay complete.composer.json constraints before adopting, since Filament v4 moved several plugin registration APIs.How can I help you explore Laravel packages today?