---
title: "Installation — Webhooks for Laravel | Roundly"
description: "Install via Composer, publish config and migrations with webhooks:install, migrate, then run a queue worker and the scheduler."
url: https://roundly-consulting.com/open-source/docs/webhooks-for-laravel/installation
language: en
---

[All packages](https://roundly-consulting.com/open-source.md)

[Webhooks for Laravel](https://roundly-consulting.com/open-source/docs/webhooks-for-laravel.md)

# Installation

Require the package and run the installer. The service provider and the Webhooks facade alias are auto-discovered:

```bash
composer require roundly-consulting/webhooks-for-laravel
php artisan webhooks:install      # config + webhooks migrations + sentinel migrations
php artisan migrate
```

webhooks:install publishes the config, the five webhooks migrations and Sentinel’s migrations — the inbound dedupe and the jobs’ idempotent middleware use Sentinel’s sentinel\_idempotency\_keys table. The migrations are not loaded from the package: publish them (the installer does) before migrating. Set webhooks.key\_type and webhooks.primary\_key\_type first if your owners or the package tables should use uuid or ulid keys (see Configuration).

## Publish tags

| Tag | What |
| --- | --- |
| `webhooks-config` | config/webhooks.php |
| `webhooks-migrations` | The five migrations, timestamped on publish. |
| `webhooks-translations` | lang/vendor/webhooks/{en,sk}: commands, enums, exceptions, problems (the refusal titles). |
| `webhooks-routes` | routes/webhooks.php — the inbound route file; a published copy is the one loaded. |

```bash
php artisan vendor:publish --tag=webhooks-translations
```

## Receive your first webhook

One endpoint per sender. Give the sender the URL and the secret — the secret is shown once:

```php
use RoundlyConsulting\Webhooks\Facades\Webhooks;

$inbound = Webhooks::for($team)->inbound()->create(handler: 'billing');
$inbound->url;     // https://app.test/webhooks/q8Zr…  (an unguessable 32-character key)
$inbound->secret;  // whsec_…  — never readable again
```

Bind the handler alias in config/webhooks.php and write the handler:

```php
// config/webhooks.php
'handlers' => ['billing' => App\Webhooks\BillingWebhookHandler::class],
```

```php
use RoundlyConsulting\Webhooks\Contracts\InboundHandler;
use RoundlyConsulting\Webhooks\DataTransferObjects\InboundWebhook;

final class BillingWebhookHandler implements InboundHandler
{
    public function handle(InboundWebhook $webhook): void
    {
        if ($webhook->type === 'invoice.paid') {
            // runs on the queue, once per delivery
        }
    }
}
```

## Send your first webhook

The URL is your customer’s own receiver — signed, retried, throttled and SSRF-guarded. The guard resolves it when the endpoint is saved, so use a real https address that resolves to a public host; a made-up name is refused:

```php
// $url — the https address your customer entered for their own receiver.
// The SSRF guard resolves it on save: it must be a public host that resolves.
$outbound = Webhooks::for($team)->outbound()->create($url, events: ['invoice.*']);
$outbound->secret; // whsec_… — hand it to the receiver once

Webhooks::send($outbound->endpoint, 'invoice.paid', ['invoice' => 42]);
```

## A queue worker and the scheduler

Deliveries are processed only on the queue: the inbound route answers 202 and returns, the handler runs in ProcessInboundDelivery and every outbound attempt in DeliverWebhook. Run a worker for webhooks.queue.connection and its queues, and run Laravel’s scheduler — the provider schedules webhooks:retry every minute and webhooks:prune daily, both withoutOverlapping()->onOneServer(). Throttled and long-delayed deliveries move only through webhooks:retry.

- The connection’s retry\_after must be longer than DeliverWebhook::$timeout (the outbound total timeout + 20 s) and than inbound.handler.timeout — webhooks:doctor checks both.
- The http-client-rate-limits store must be shared (cache or redis) for the per-endpoint outbound throttle to hold across workers; the default in-memory store is per process.
- The inbound limiters’ cache store and Sentinel’s idempotency store must be shared — never array.
- A sync queue works for tests and small apps but runs processing inside the request; webhooks:doctor warns.

## Behind a proxy, and the body limit

Behind a load balancer or a CDN, configure Laravel’s trusted proxies: the inbound rejection guard counts per endpoint and client IP, and without them every sender shares the proxy’s IP.

```php
// bootstrap/app.php — behind a load balancer or a CDN
->withMiddleware(function (Middleware $middleware): void {
    $middleware->trustProxies(at: [/* your proxies */]);
})
```

Laravel’s Request::capture() reads and decodes a JSON request’s whole body before any middleware, route or package code runs — the package’s 256 KB check bounds signature work and storage, not that first read. Bound the body at the web server for the inbound prefix: nginx client\_max\_body\_size (or Apache LimitRequestBody) a little above inbound.max\_payload\_bytes\_ceiling (1 MB).

## Check the setup

```bash
php artisan webhooks:doctor
```

It flags processing inside the request, volatile limiter and idempotency stores, a stalled retry sweep and more — see Artisan commands.

## Show your open-source love

This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.

[More ways to support, including crypto](https://roundly-consulting.com/support-us.md)

By donating, you agree to our [donation terms](https://roundly-consulting.com/donation-terms.md).

[Support our open source work (opens in a new tab)](https://donate.stripe.com/dRmeVe8FX5PF1Qd9pXcEw00) [Join us on Patreon (opens in a new tab)](https://www.patreon.com/cw/roundly)

## Want this built into your product?

We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.

[Get a quote in 48 hours](https://roundly-consulting.com/contact.md) [Browse all packages](https://roundly-consulting.com/open-source/docs/webhooks-for-laravel.md)
