---
title: "DI and actions — Webhooks for Laravel | Roundly"
description: "Skip the facade: inject WebhooksManager or call a single-purpose action — every action with its execute() signature and facade method."
url: https://roundly-consulting.com/open-source/docs/webhooks-for-laravel/without-facade
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)

# DI and actions

The facade is the recommended default, not the only way in. RoundlyConsulting\\Webhooks\\WebhooksManager is the facade’s root — a scoped binding you can inject — and every facade call ends in one action. All three styles run the same code:

```php
// Send an event
Webhooks::send($endpoint, 'invoice.paid', ['invoice' => 42]);
$webhooks->send($endpoint, 'invoice.paid', ['invoice' => 42]);                       // WebhooksManager $webhooks, injected
app(SendWebhookAction::class)->execute($endpoint, 'invoice.paid', ['invoice' => 42]);

// Create an inbound endpoint
$created = Webhooks::inbound()->create(handler: 'billing');
$created = $webhooks->inbound()->create(handler: 'billing');
$created = app(CreateInboundEndpointAction::class)->execute(null, 'billing');

// Run the inbound pipeline (what the route does)
$response = Webhooks::inbound()->receive($key, $request);
$response = $webhooks->inbound()->receive($key, $request);
$response = app(ReceiveWebhookAction::class)->execute($key, $request);

// Redeliver
Webhooks::outbound()->redeliver($delivery);
$webhooks->outbound()->redeliver($delivery);
app(RedeliverWebhookAction::class)->execute($delivery);
```

## Inject the manager

```php
use RoundlyConsulting\Webhooks\Models\OutboundDelivery;
use RoundlyConsulting\Webhooks\Models\OutboundEndpoint;
use RoundlyConsulting\Webhooks\WebhooksManager;

final readonly class InvoiceWebhooks
{
    public function __construct(private WebhooksManager $webhooks) {}

    public function paid(OutboundEndpoint $endpoint, int $invoice): OutboundDelivery
    {
        return $this->webhooks->send($endpoint, 'invoice.paid', ['invoice' => $invoice], id: "invoice-{$invoice}-paid");
    }
}
```

The fake extends the manager, so an injected WebhooksManager is the fake too under Webhooks::fake(). The manager’s other public methods (createInboundEndpoint(), rotateOutboundSecret(), redeliverFailed() and the rest) are internal — the sub-accessors call them so the fake sees every call. Use the sub-accessors.

## Call an action

```php
use RoundlyConsulting\Webhooks\Actions\Inbound\CreateInboundEndpointAction;
use RoundlyConsulting\Webhooks\Actions\Outbound\SendWebhookAction;

app(SendWebhookAction::class)->execute($endpoint, 'invoice.paid', ['invoice' => 42]);
app(CreateInboundEndpointAction::class)->execute($team, 'billing');   // the owner comes first
```

Every host-facing action is final readonly, resolved from the container, with one execute(). The actions skip the fake and the owner scope — they take what you hand them — so use them to compose your own use cases, and the facade or the manager everywhere else.

## Facade method → action

Namespaces are relative to RoundlyConsulting\\Webhooks\\Actions:

| Facade method | Action | execute() |
| --- | --- | --- |
| `inbound()->create()` | `Inbound\CreateInboundEndpointAction` | `?Model $owner = null, ?string $handler = null, string $scheme = 'default', ?string $name = null, ?string $secret = null, ?InboundEndpointOptions $options = null` |
| `inbound()->update()` | `Inbound\UpdateInboundEndpointAction` | `InboundEndpoint $endpoint, InboundEndpointChanges $changes` |
| `inbound()->enable() · disable()` | `Inbound\SetInboundEndpointStateAction` | `InboundEndpoint $endpoint, bool $enabled` |
| `inbound()->delete()` | `Inbound\DeleteInboundEndpointAction` | `InboundEndpoint $endpoint` |
| `inbound()->rotateSecret()` | `Inbound\RotateInboundSecretAction` | `InboundEndpoint $endpoint, ?CarbonInterface $grace = null, ?string $secret = null` |
| `inbound()->receive()` | `Inbound\ReceiveWebhookAction` | `string $key, Request $request` |
| `inbound()->verify()` | `Inbound\VerifyInboundRequestAction` | `InboundEndpoint $endpoint, Request $request, ?string $body = null, bool $deferCommit = false` |
| `inbound()->replay()` | `Inbound\ReplayInboundDeliveryAction` | `InboundDelivery $delivery` |
| `outbound()->create()` | `Outbound\CreateOutboundEndpointAction` | `string $url, array $events = ['*'], ?string $name = null, array $headers = [], string $scheme = 'default', ?OutboundEndpointOptions $options = null, ?Model $owner = null` |
| `outbound()->update()` | `Outbound\UpdateOutboundEndpointAction` | `OutboundEndpoint $endpoint, OutboundEndpointChanges $changes` |
| `outbound()->enable() · disable()` | `Outbound\SetOutboundEndpointStateAction` | `OutboundEndpoint $endpoint, bool $enabled` |
| `outbound()->delete()` | `Outbound\DeleteOutboundEndpointAction` | `OutboundEndpoint $endpoint` |
| `outbound()->rotateSecret()` | `Outbound\RotateOutboundSecretAction` | `OutboundEndpoint $endpoint, ?CarbonInterface $grace = null` |
| `send()` | `Outbound\SendWebhookAction` | `OutboundEndpoint $endpoint, string $eventType, array\|Arrayable\|JsonSerializable $payload, ?string $id = null` |
| `broadcast() · for($owner)->broadcast()` | `Outbound\BroadcastWebhookAction` | `string $eventType, array\|Arrayable\|JsonSerializable $payload, ?Model $owner = null` |
| `outbound()->ping()` | `Outbound\PingEndpointAction` | `OutboundEndpoint $endpoint` |
| `outbound()->redeliver()` | `Outbound\RedeliverWebhookAction` | `OutboundDelivery $delivery` |
| `outbound()->redeliverFailed()` | `Outbound\RedeliverFailedDeliveriesAction` | `OutboundEndpoint $endpoint, ?CarbonInterface $since = null, bool $includeCancelled = false` |
| `outbound()->dispatchDue()` | `Outbound\DispatchDueDeliveriesAction` | — |
| `prune()` | `PruneAction` | `?PruneOptions $options = null` |

Two notes: CreateInboundEndpointAction takes the owner first, and VerifyInboundRequestAction also accepts the raw body when it was already read and $deferCommit — true hands you the verdict’s commit closure to run before you act. AcceptInboundDeliveryAction, ProcessInboundDeliveryAction, AttemptDeliveryAction, ScheduleRetryAction, RecordEndpointFailureAction, DisableOutboundEndpointAction and CancelPendingDeliveriesAction are internal building blocks.

## 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)
