Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Webhook Laravel Package

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.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

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

  • Reliability gap (highest). No built-in retry-with-backoff, dead-letter handling, circuit breaking, or idempotency keys. Laravel's queue $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.
  • Security / SSRF. If endpoints are ever user-configurable, we must validate and resolve URLs ourselves and block private/link-local ranges. The package will happily POST to 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.
  • Bus factor / maintenance. Small star count, effectively one active maintainer, part of the 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.
  • Version drift (downgraded in 2.7.0). 2.7.0 adds Laravel 12.x compatibility, confirming the maintainers are actively tracking current framework majors rather than stalled on an older line. The "will it support our framework version" question is now answered rather than assumed. Residual risk is future drift, not current drift: pin the constraint, and add a Dependabot/Renovate lane so a Laravel 13 major does not surprise us mid-quarter.
  • Zero dependents in the index means no transitive breakage and no ecosystem lock-in — good for reversibility, neutral for maturity signal.

Key questions

  1. Is the webhook feature internal plumbing or a customer-facing contract? That single answer determines whether this package is 90% of the solution or 15% of it.
  2. What are the delivery semantics we owe receivers — at-least-once with idempotency keys, or best-effort?
  3. Do we need request signing (HMAC + timestamp) on day one, and who owns key rotation?
  4. Are endpoints static config or tenant-managed data? Tenant-managed implies SSRF defense, endpoint health, and a delivery log we must build.
  5. Do we already have a bespoke Guzzle-based webhook notifier in the codebase that this replaces, and what behaviors does that code have that we would silently lose?
  6. What is the failure contract with receivers — do we retry on 4xx, on 5xx only, and for how long?

Integration Approach

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

  • Greenfield: adopt directly, wrap from the start. Define an abstract WebhookNotification base class that centralizes toWebhook(), signing, and payload envelope/versioning so individual notifications stay declarative.
  • Replacing bespoke Guzzle code: inventory existing webhook call sites first, capture their payload shapes, headers, retry behavior, and timeouts, then port one at a time behind a feature flag. Do not do a big-bang swap; the silent behavior differences (timeout defaults, TLS verification, redirect handling) are exactly what bites in cutover.
  • Coexistence: the package is additive. It can run alongside jobs, events, and listeners that fan out the same domain event; no data migration, no dual-write concern.

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)

  1. Spike (1 day, reduced from 1–2). Framework compatibility is now known, so the spike is about behavior rather than feasibility: install in a scratch branch, ship one notification to a request bin, and record timeout/retry/TLS defaults. Lower uncertainty than originally scoped.
  2. Transport baseline. Config entry, queue wiring (ShouldQueue), explicit timeout/connectTimeout, and Http::fake() test patterns codified as a team convention.
  3. Endpoint registry. Trait or model for 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.
  4. Reliability layer. Retry policy, backoff, max attempts, terminal-failure handling, and an idempotency key on every payload. Decide explicitly whether failures dead-letter or drop.
  5. Security layer. HMAC signing with timestamp and a documented verification recipe for receivers; secret storage and rotation.
  6. Observability. Delivery attempts, latency, status codes, and failure rate per endpoint, emitted as metrics through existing middleware.
  7. Rollout. One domain event in production, soak for a week, then migrate remaining call sites.

Operational Impact

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:

  • Slow receivers consume queue workers. Enforce aggressive connectTimeout (1–2s) and timeout (5s) values; a hung receiver should never pin a worker.
  • Fan-out amplification. One domain event notifying N endpoints multiplies queue volume; consider per-endpoint jobs so a single bad endpoint cannot block others.
  • Connection reuse. High-volume fan-out benefits from keeping the HTTP client's connection pooling behavior in mind; at our likely volumes this is a non-issue, but flag it before any 10x event bursts (imports, backfills) that could produce a thundering herd.

Failure modes

  • Silent drops. No built-in dead-letter; if a job exhausts retries, the notification is simply gone unless failed() is implemented. Must be designed, not assumed.
  • Duplicate delivery. At-least-once is the realistic guarantee; receivers must dedupe on the idempotency key we send. If we do not send one, receivers will dedupe badly or not at all.
  • Non-2xx ambiguity. Default behavior on 4xx vs 5xx retry classification needs an explicit decision; retrying 400s wastes capacity and masks payload bugs.
  • Receiver-side outages. Without backoff these become a retry storm that outlasts the receiver's recovery. Backoff plus jitter is mandatory.
  • SSRF and key exposure. A malicious or compromised endpoint URL can be used to probe internal networks; secrets in payload headers must live in config/vault, never in code.
  • Upstream abandonment. Low severity given MIT and tiny surface, and further reduced by the 2.7.0 compatibility release. The mitigation remains a fork and a named internal owner, decided in advance rather than during an incident.

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.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource