Webhook Event Registry
Contract for registering outbound webhook event names, grouped discovery metadata, and metering weights.
Overview
App\Services\Api\WebhookEventRegistry is the runtime registry for outbound webhook event discovery metadata and metering weights.
Core binds it as a singleton in AppServiceProvider and seeds it from the canonical event catalogue — EventRegistry::names('webhook'), i.e. every entry in config/events.php with webhook: true that is not system_protected. The event names are therefore derived, not hand-maintained here: add a webhook event by adding it to config/events.php. The catalogue is intentionally large, so this page documents the event-definition contract and discovery helpers rather than duplicating the entire event reference. For the exhaustive shipped list, use the Webhooks API reference.
EventRegistry is the generic name authority: its consumer-specific discovery methods decide which events are visible to REST subscriptions, audit bridges, and delivery, and this registry only decorates that webhook-visible slice.
WebhookEventRegistry::CORE_EVENT_NAMES is a generated mirror of that derived list, kept only for static contexts that cannot reach the container; an architecture ratchet asserts the two are identical, so the mirror cannot drift.
WebhookEventRegistry remains the backwards-compatible webhook metadata adapter. It derives labels, groups, and metering weights from the shared registry, preserves custom metadata registered through its legacy helpers, and discovers direct EventRegistry plugin registrations dynamically. Direct plugin registration uses the shared EventDefinition contract; legacy WebhookEventRegistry::register() is also a complete registration path that creates a webhook-visible plugin definition before retaining its metadata override. Neither path grants API, workflow, or general plugin visibility.
Public surface
| Method | Purpose |
|---|---|
register() |
Register one event definition |
registerMany() |
Register multiple event definitions atomically |
decorate() |
Attach metadata to a name the catalogue already carries |
get() |
Return one event definition or null |
all() |
Return the full keyed event map |
groups() |
Return grouped discovery metadata |
names() |
Return the flat list of currently registered event names |
has() |
Check whether an event name is registered |
weight() |
Return the event's outbound metering weight |
This metadata registry does not publish a separate registered-value interface. Its current framework contract is the typed public surface on this page plus the documented registration shape and runtime behavior below.
Accepted registration shape
Each event is identified by its registration name. Core uses dotted names such as account.created, settings.updated, and document.pdf_generated, but the registry does not require event names to be non-empty or dotted. It only rejects a duplicate name; callers are responsible for choosing a meaningful name.
The accepted definition shape is:
[
'label' => 'Created', // optional
'group' => 'account', // optional
'weight' => 1, // optional
]
Defaults are derived from the event name:
groupdefaults to the part before the first.labeldefaults to a headline version of the suffix after the first.weightdefaults to1
The definition shape is enforced by PHPDoc and static analysis, not by runtime registry validation. At runtime only duplicate event names are rejected. Empty label and group values are stored unchanged, and integer weights may be zero or negative. Callers remain responsible for supplying the documented types; for example, storing a non-integer weight can later violate the declared weight(): int return type.
Identity, collisions, and grouping
Event identity is the event name itself.
register()throwsInvalidArgumentExceptionwhen the name is already presentregisterMany()performs duplicate checks before mutating state, so collisions leave the registry unchanged
groups() sorts event groups alphabetically by group key. Each group exposes a human-readable label plus a flat list of event names.
names() preserves the registry's registration order. The webhook adapter and Livewire subscription picker use the current webhook-visible names, while REST request DTOs resolve the same list when their validation rules are evaluated. The wildcard (*) is an additional valid subscription value and expands at dispatch time to every currently visible core or plugin event.
Current core registrations and discovery guidance
The shipped catalogue is seeded from the configured EventDefinition catalogue and surfaced to users through the API console, webhook forms, and delivery jobs.
Representative shipped events include:
| Event | Group | Current meaning |
|---|---|---|
account.created |
account |
Account lifecycle creation |
settings.updated |
settings |
Settings API update completed |
document.pdf_generated |
document |
Document pipeline rendered a PDF |
vehicle.assigned |
vehicle |
Fleet assignment event |
Use the registry helpers instead of copying the full list:
names()supports runtime discovery and Livewire webhook-form validationgroups()powers grouped selection UI inresources/views/livewire/api/webhooks.blade.phpall()exposes the normalized metadata when you need labels, groups, and weights together
Lookup and failure behavior
get($name)returns the normalized event definition ornullhas($name)is the non-throwing existence checkweight($name)returns the stored weight, or1when the name is unknown
The registry itself does not dispatch events. It publishes the event catalogue that delivery and management flows consume.
Ordering and runtime consumption
The registry's weight value is about outbound metering, not delivery order.
App\Jobs\DeliverWebhook resolves get() and weight() on the first delivery attempt so it can:
- write
price_weightandweighted_unitsto thewebhook_logsrow - increment outbound daily usage on the
webhook_sentchannel using the event group as the metering resource
The management surfaces use the live EventRegistry boundary:
- the Livewire webhook form validates against the runtime registry's
names()output - REST
CreateWebhookDataandUpdateWebhookDatavalidate subscriptions againstEventRegistry::names('webhook')plus*
WebhookService::dispatch() is the single outbound authorization boundary: it enforces EventRegistry::names('webhook') before querying subscriptions or enqueueing jobs. DispatchWebhookForAuditableEvent and the document bridge retain their ownership, payload, and replay responsibilities, then delegate authorization and delivery to that service. Unknown, system-protected, and consumer-denied events are therefore ignored even for wildcard subscriptions. Existing wildcard subscriptions automatically receive newly visible core events and runtime plugin events. WebhookService::EVENTS remains available as a deprecated 245-event compatibility snapshot only; it is not a live allow-list.
This makes every current webhook-visible event available as an explicit named REST subscription as well as through the wildcard.
Worked example
Plugins can register a webhook-visible event directly with the shared registry:
$registry = new WebhookEventRegistry;
foreach (app(EventRegistry::class)->names('webhook') as $name) {
$registry->register($name);
}
With that registration in place:
names()includessettings.updatedanddocument.pdf_generatednames()also includes the 25 P6A-02 core events marked webhook-visible, plus later runtime plugin registrationsgroups()['document']['events']exposes the document events for the picker UIweight('settings.updated')returns the default metering weight used by outbound usage recording