PHPackages                             bambamboole/laravel-webhooks - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. bambamboole/laravel-webhooks

ActiveLibrary

bambamboole/laravel-webhooks
============================

Attribute-driven outgoing webhooks for Laravel: discover webhook events, manage subscriptions, deliver signed payloads.

0.1.0(today)00[1 PRs](https://github.com/bambamboole/laravel-webhooks/pulls)MITPHPPHP ^8.4CI passing

Since Aug 1Pushed todayCompare

[ Source](https://github.com/bambamboole/laravel-webhooks)[ Packagist](https://packagist.org/packages/bambamboole/laravel-webhooks)[ RSS](/packages/bambamboole-laravel-webhooks/feed)WikiDiscussions main Synced today

READMEChangelog (1)Dependencies (12)Versions (2)Used By (0)

Laravel Webhooks
================

[](#laravel-webhooks)

Attribute-driven outgoing webhooks for Laravel: annotate event classes, manage subscriptions in the database, and deliver signed payloads through [spatie/laravel-webhook-server](https://github.com/spatie/laravel-webhook-server).

Installation
------------

[](#installation)

```
composer require bambamboole/laravel-webhooks
```

Delivery goes through [spatie/laravel-webhook-server](https://github.com/spatie/laravel-webhook-server), which is installed as a dependency. The `webhook_subscriptions` and `webhook_deliveries` migrations run automatically. Publish the config if you need to change defaults:

```
php artisan vendor:publish --tag=laravel-webhooks-config
```

Defining webhook events
-----------------------

[](#defining-webhook-events)

Annotate any class with `#[WebhookEvent]` and give it a payload method. By default the package scans `app/Events`; adjust `webhooks.scan_paths` in the config to change that.

```
use Bambamboole\LaravelWebhooks\Attributes\WebhookEvent;

#[WebhookEvent(
    name: 'invoice.paid',
    title: 'Invoice paid',
    summary: 'Sent when an invoice is paid.',
    tags: ['billing'],
)]
final class InvoicePaid
{
    public function __construct(public int $invoiceId, public int $amount) {}

    /**
     * @return array{invoiceId:int, amount:int}
     */
    public function webhookPayload(): array
    {
        return [
            'invoiceId' => $this->invoiceId,
            'amount' => $this->amount,
        ];
    }
}
```

Every delivery wraps the payload in a stable envelope:

```
{
    "id": "9d3c…",
    "event": "invoice.paid",
    "createdAt": "2026-07-03T12:34:56.000000Z",
    "data": {
        "invoiceId": 987,
        "amount": 6500
    }
}
```

`WebhookEventRegistry::all()` returns the discovered definitions, which an app can use to power its own webhook setup UI:

```
use Bambamboole\LaravelWebhooks\WebhookEventRegistry;

$events = collect(app(WebhookEventRegistry::class)->all())
    ->map(fn ($event) => [
        'name' => $event->name,
        'title' => $event->title,
        'summary' => $event->summary,
    ]);
```

Managing subscriptions
----------------------

[](#managing-subscriptions)

Subscriptions live in the `webhook_subscriptions` table via the shipped Eloquent model. `events` holds the subscribed event names; `'*'` subscribes to everything. Secrets are encrypted at rest and used to sign deliveries.

```
use Bambamboole\LaravelWebhooks\Models\WebhookSubscription;

WebhookSubscription::create([
    'name' => 'Billing system',
    'url' => 'https://example.com/webhooks',
    'secret' => 'signing-secret',
    'headers' => ['X-Tenant' => 'acme'],
    'events' => ['invoice.paid', 'invoice.refunded'],
]);
```

To source subscriptions from somewhere else, bind your own repository — the database repository is only a default (`bindIf`):

```
use Bambamboole\LaravelWebhooks\WebhookSubscription;
use Bambamboole\LaravelWebhooks\WebhookSubscriptionRepository;

final class CustomWebhookSubscriptionRepository implements WebhookSubscriptionRepository
{
    public function forEvent(string $eventName, object $event): iterable
    {
        yield new WebhookSubscription(
            url: 'https://example.com/webhooks',
            secret: 'signing-secret',
            headers: ['X-Webhook-Source' => 'app'],
            id: 'subscription-123',
        );
    }
}
```

Dispatching
-----------

[](#dispatching)

Webhook event classes are regular Laravel events — fire them and the package delivers them. Listeners for every discovered `#[WebhookEvent]` class are registered automatically:

```
event(new InvoicePaid(invoiceId: 987, amount: 6500));
```

Discovery scans the configured paths at every boot. If that cost matters to you, set `webhooks.auto_listen` to `false` and wire the dispatcher yourself:

```
use Bambamboole\LaravelWebhooks\DispatchWebhookEvent;
use Illuminate\Support\Facades\Event;

Event::listen(InvoicePaid::class, DispatchWebhookEvent::class);
```

Each active subscription for the event receives one signed call through spatie's queue-backed webhook server. Calls are signed with the subscription secret (unsigned when no secret is set) and include a timestamp against replay attacks (`webhooks.dispatcher.use_timestamp`).

Delivery log
------------

[](#delivery-log)

Every call attempt is recorded in the `webhook_deliveries` table (UUIDv7 keys): event name, url, payload, attempt, status, HTTP response status, and error details. Retries of the same call share a `call_uuid`. The status is `succeeded`, `failed` (a retry follows), or `final_failed` (retries exhausted).

```
use Bambamboole\LaravelWebhooks\Models\WebhookDelivery;

WebhookDelivery::where('status', WebhookDelivery::STATUS_FAILED)->latest()->get();

$delivery->subscription; // the WebhookSubscription it was delivered to, if any
```

Calls dispatched through spatie's webhook server directly (without this package's dispatcher) are not recorded.

The log is prunable: rows older than `webhooks.deliveries.prune_after_days` (default 30, `null` keeps them forever) are removed when your app schedules pruning:

```
Schedule::command('model:prune', ['--model' => [WebhookDelivery::class]])->daily();
```

Development
-----------

[](#development)

```
composer test    # pest
composer check   # pint + phpstan + rector + pest
composer serve   # workbench app
```

License
-------

[](#license)

[MIT](LICENSE.md)

###  Health Score

38

—

LowBetter than 83% of packages

Maintenance100

Actively maintained with recent releases

Popularity0

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity40

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 94.7% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

0d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/547137a6d80cad01ed1dd065b1c6af329d9a23a4134a895cff01e078cc155500?d=identicon)[bambamboole](/maintainers/bambamboole)

---

Top Contributors

[![bambamboole](https://avatars.githubusercontent.com/u/8823695?v=4)](https://github.com/bambamboole "bambamboole (18 commits)")[![github-actions[bot]](https://avatars.githubusercontent.com/in/15368?v=4)](https://github.com/github-actions[bot] "github-actions[bot] (1 commits)")

---

Tags

laravelwebhooks

###  Code Quality

TestsPest

Static AnalysisPHPStan, Rector

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/bambamboole-laravel-webhooks/health.svg)

```
[![Health](https://phpackages.com/badges/bambamboole-laravel-webhooks/health.svg)](https://phpackages.com/packages/bambamboole-laravel-webhooks)
```

###  Alternatives

[spatie/laravel-medialibrary

Associate files with Eloquent models

6.2k45.4M679](/packages/spatie-laravel-medialibrary)[spatie/laravel-health

Monitor the health of a Laravel application

88212.7M180](/packages/spatie-laravel-health)[psalm/plugin-laravel

Psalm plugin for Laravel

3355.4M352](/packages/psalm-plugin-laravel)[simplestats-io/laravel-client

Server-side analytics for Laravel that follows the full funnel from visit to registration to payment, attributed to the channel that drove it. Revenue, MRR, churn and ad-spend profit (ROAS/CAC) per channel. GDPR compliant, ad-blocker proof.

5022.6k](/packages/simplestats-io-laravel-client)[harris21/laravel-fuse

Circuit breaker for Laravel queue jobs. Protect your workers from cascading failures.

24773.9k](/packages/harris21-laravel-fuse)[api-platform/laravel

API Platform support for Laravel

58174.6k18](/packages/api-platform-laravel)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
