laravel-notification-channels/webhook
The Webhook notification channel lets you deliver Laravel notifications as HTTP POST requests to any URL. Define payloads with a simple toWebhook() method and send JSON to external services or custom endpoints using Laravel's standard notification API.
Architecture fit
The package is a thin adapter, not a framework. It slots into Laravel's native notification system as one additional channel: you add WebhookChannel::class to a notification's $via array, define toWebhook() returning a WebhookMessage, and resolve the target URL via routeNotificationForWebhook() on the notifiable model. There is no new abstraction layer, no persistence, no service provider magic beyond auto-discovery, and no schema. That makes it architecturally cheap to adopt and cheap to remove.
The important framing for stakeholders: this package answers "how do I shape and POST a JSON payload from a notification," not "how do I run a reliable outbound webhook program." Delivery guarantees, retries, signing, idempotency, and endpoint lifecycle are ours to build (or to accept as absent). Treat it as the transport primitive, and budget the reliability layer as separate work.
It fits best where we already have Laravel notifications as the event fan-out mechanism and want webhooks as one more consumer alongside mail, Slack, and database channels. It fits poorly if we intend to build a customer-facing "webhooks" product with per-tenant endpoint management, delivery logs, and replay — in that case it is one small component of a system we mostly have to build ourselves.
Integration feasibility
Low effort, high certainty. composer require laravel-notification-channels/webhook, optional config publish, one channel entry, one method per notification, one routing method (or trait) per notifiable. A mid-level Laravel developer gets an end-to-end POST into a request bin in well under a day.
The integration surface is genuinely small and inspectable — a handful of classes. That is a feature for review purposes: security and architecture review can read the entire dependency in an afternoon, which is rare and worth stating explicitly to a review board.
Technical risk
$tries/$backoff/retryUntil and Http::retry() cover part of it, but the composition is ours to design and test. Teams routinely underestimate this; it is the single largest source of post-launch incidents in webhook features.169.254.169.254. Also confirm current signing support — do not assume HMAC signing and timestamp headers are provided; plan to add a signing decorator or wrapping job.laravel-notification-channels org. Mitigant: MIT license, tiny code surface, trivially forkable — this is a low-severity risk, but it should be on the dependency register rather than assumed away.Key questions
Stack fit
Native fit for any Laravel 9+ application, and version-agnostic across the rest of the stack. 2.7.0 explicitly covers Laravel 12, so the framework-compatibility question is settled at the top of the range rather than a spike-day unknown. Recent releases build on Laravel's HTTP client, so it inherits Http::fake() testability, middleware, and macro support — meaning our existing HTTP test conventions and observability middleware (logging, tracing headers, timing) can be applied without special-casing this package. That is the strongest argument for adopting it over hand-rolled Guzzle calls.
There is no PHP runtime, extension, or service dependency beyond what a standard Laravel app already ships.
Migration path
WebhookNotification base class that centralizes toWebhook(), signing, and payload envelope/versioning so individual notifications stay declarative.Compatibility
Laravel 12 support is now confirmed upstream in 2.7.0 — this closes the item previously flagged as a spike dependency. 2.7.0 is a patch-level release containing only the compatibility change (laravel-shift PR #57); no breaking changes, no deprecations, no API surface changes, and no code changes required on our side beyond widening the version constraint. There is no re-certification burden and no need to re-review the dependency for this bump.
Still confirm on the spike: PHP version floor for the 2.7.0 line, and whether the HTTP client used honors Http::retry, timeout, and connectTimeout as expected. Also confirm whether verify() (TLS verification toggle) exists on WebhookMessage — we want TLS verification on everywhere and should not expose a per-notification escape hatch unless there is a documented legacy-receiver reason.
Sequencing (roughly two to three weeks of calendar time for one developer, most of it in steps 3–5)
ShouldQueue), explicit timeout/connectTimeout, and Http::fake() test patterns codified as a team convention.routeNotificationForWebhook(); static config first, tenant-scoped table only if the product requires it. Add URL allow-listing and private-IP blocking here if endpoints are dynamic.Maintenance
Near-zero code maintenance for the package itself — it is small, MIT, and now demonstrably tracks current Laravel releases (2.7.0 for Laravel 12). The real maintenance burden is the wrapper code we author around it (registry, signing, retry policy), which becomes a small internal library and needs an owner. Assign that ownership up front; "it's just a composer package" is how the reliability layer quietly becomes unowned code with no runbook. Add it to the dependency register with a quarterly review of upstream releases — the 2.7.0 bump is a good reminder that these arrive with no action required beyond a constraint check.
Support
Support load is concentrated in receiver-side debugging: "we didn't get the webhook" incidents will be the plurality of tickets. Mitigate with a delivery log (attempt, status, response snippet, latency) from day one — this is the single highest-ROI investment in the feature and is not provided by the package. Also publish a receiver-facing contract: payload envelope, version field, signature verification snippet, retry schedule, and source IP ranges.
First-line support needs a runbook covering: how to identify an attempt, how to re-drive a failed delivery, and how to disable a misbehaving endpoint without a deploy. Without that, every incident escalates to engineering.
Scaling
Outbound HTTP fan-out scales as well as the queue behind it — this is a queue-throughput problem, not a package problem. Watch three things:
connectTimeout (1–2s) and timeout (5s) values; a hung receiver should never pin a worker.Failure modes
failed() is implemented. Must be designed, not assumed.Ramp-up
For a Laravel developer: half a day to productive, one day to fluent. The framework knowledge they already have (notifications, queues, HTTP client) transfers directly; there is almost no package-specific knowledge to acquire. The 2.7.0 release adds nothing new to learn.
For the surrounding system — signing, retry semantics, endpoint lifecycle, on-call runbook — budget the time of a small feature, not a library install. Frame it that way in planning. The package earns its place by removing the boring transport work and letting us spend the effort on the reliability and security properties we actually need to own; presenting it as a complete webhook solution to stakeholders is the failure mode to avoid.
How can I help you explore Laravel packages today?