SIGNALS Documentation
API Reference

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:

  • group defaults to the part before the first .
  • label defaults to a headline version of the suffix after the first .
  • weight defaults to 1

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() throws InvalidArgumentException when the name is already present
  • registerMany() 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 validation
  • groups() powers grouped selection UI in resources/views/livewire/api/webhooks.blade.php
  • all() exposes the normalized metadata when you need labels, groups, and weights together

Lookup and failure behavior

  • get($name) returns the normalized event definition or null
  • has($name) is the non-throwing existence check
  • weight($name) returns the stored weight, or 1 when 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:

  1. write price_weight and weighted_units to the webhook_logs row
  2. increment outbound daily usage on the webhook_sent channel 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 CreateWebhookData and UpdateWebhookData validate subscriptions against EventRegistry::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() includes settings.updated and document.pdf_generated
  • names() also includes the 25 P6A-02 core events marked webhook-visible, plus later runtime plugin registrations
  • groups()['document']['events'] exposes the document events for the picker UI
  • weight('settings.updated') returns the default metering weight used by outbound usage recording