Download the PHP package goldnead/statamic-webhook-manager without Composer

On this page you can find all versions of the php package goldnead/statamic-webhook-manager. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.

FAQ

After the download, you have to make one include require_once('vendor/autoload.php');. After that you have to import the classes with use statements.

Example:
If you use only one package a project is not needed. But if you use more then one package, without a project it is not possible to import the classes with use statements.

In general, it is recommended to use always a project to download your libraries. In an application normally there is more than one library needed.
Some PHP packages are not free to download and because of that hosted in private repositories. In this case some credentials are needed to access such packages. Please use the auth.json textarea to insert credentials, if a package is coming from a private repository. You can look here for more information.

  • Some hosting areas are not accessible by a terminal or SSH. Then it is not possible to use Composer.
  • To use Composer is sometimes complicated. Especially for beginners.
  • Composer needs much resources. Sometimes they are not available on a simple webspace.
  • If you are using private repositories you don't need to share your credentials. You can set up everything on our site and then you provide a simple download link to your team member.
  • Simplify your Composer build process. Use our own command line tool to download the vendor folder as binary. This makes your build process faster and you don't need to expose your credentials for private repositories.
Please rate this library. Is it a good library?

Informations about the package statamic-webhook-manager

Statamic Webhook Manager

A central, CP-native integration layer for Statamic 6. Manage outbound webhooks, inbound endpoints, deliveries, retries, replays, rules and templates — all from one place inside the Control Panel.

Status: Stable on Statamic 6 (Laravel 12/13). Outbound webhooks, the delivery engine with retries & replay, inbound endpoints, the rule engine, payload templates and the full Vue + Inertia Control Panel are implemented and covered by the test suite.


Features

Screenshots


Outbound — which events fire which requests, and whether they are healthy

Deliveries — status, error classification and attempt count

Delivery snapshot — full request and response, replayable with one click

Insights — volume, success rate and failures over time

Requirements

Installation

The Webhook Manager appears in the CP sidebar as Webhooks.

Note: The pre-built CP bundle (resources/dist/build/) ships with the package. If you cloned the repo directly (e.g. via path repository) you'll need to build it yourself:

Configuration

See config/webhook-manager.php after publishing — feature toggles, retry policy, logging mode, masking rules, route prefixes, alerting/circuit-breaker, storage driver, etc.

Settings in the Control Panel

Most of that file can also be changed under Settings in the Control Panel (permission: manage webhook settings): modules, retry policy, HTTP defaults, inbound limits, signature headers, logging and retention.

The screen is the suite's shared one, from goldnead/statamic-brand-context, and every addon of the suite that offers settings appears on it as its own section — one place to look instead of sixteen. Webhooks → Settings in the sidebar leads there. The values are per brand, which the addon's own screen could not do: on a multi-brand install two brands no longer share one setting.

Only the difference to the config file is stored, one row per changed key in brand_settings. A value set back to what the file says deletes its row again, so the config file stays the default and a later release can still move it. An install that never opens the screen behaves exactly as before.

Upgrading. Settings saved with an earlier release live in webhook_settings. They are carried over to brand_settings on the default brand by php artisan migrate. The old table is left in place for one minor version so a rollback does not lose them.

Not editable there, on purpose:

The Debug screen also prints the resolved config tree with its secrets masked, which is the quickest answer to "what is this install actually running". It is reachable with either use webhook debug tools or manage webhook settings.

Storage driver

Webhook configuration (outbound webhooks, inbound endpoints, rules, templates) can be stored two ways. Delivery records and logs are runtime telemetry and always live in the database.

You can switch the active driver in the Control Panel (Debug → Storage) — it migrates the existing config to the target store and activates it, no .env access needed. A Control-Panel choice is persisted under storage/ and takes precedence over the config/env default.

Or do it from the CLI (records are copied id-for-id either way):

Retries

A delivery that fails on a retryable status (or a network error) gets a next_retry_at from the retry policy — none, linear or exponential, capped at max_delay_seconds, up to max_attempts.

Those retries are executed by webhook-manager:dispatch-retries, which the addon puts on the scheduler itself, once a minute. Your site needs the standard Laravel scheduler cron — the one every Laravel install already has:

Without that cron, deliveries will sit at "next retry in …" forever. If you would rather drive the command yourself, set webhook-manager.retry.schedule to false.

A retry is claimed before it runs, so two overlapping scheduler runs cannot turn one planned attempt into two. Once a delivery is out of attempts it stops being scheduled, the circuit breaker records the failure and the failure alert goes out.

The inbound endpoint URL

The brand segment is part of the URL on every install, single-brand ones included. It is what the Control Panel prints on the endpoint's page, and it is what you paste into the sender's webhook field.

Why the brand is in the path. Inbound endpoints are brand-scoped like everything else in this addon, and the brand scope fails closed: with no current brand a query returns no rows. Every other route resolves the brand from something the caller carries — a CP session, a bearer token, a link token. A webhook sender carries none of those. It is Scaleway or Stripe or n8n, and it holds a URL. So the URL has to say it. Until 2.1.0 it did not, and a multi-brand install answered every inbound delivery with 404 Endpoint not found or disabled while the endpoint sat there, enabled.

The short form stays routable:

It resolves to the default brand, which is where the brand-scoping migration put every endpoint that existed before brands did — so a site that switches brand-context.multi_brand on keeps the senders that worked before it switched. It deliberately does not search the other brands: handle is unique per brand, not globally, so that search would have to guess as soon as two brands pick the same name, and a webhook config is a destination plus the credential that authenticates it. An endpoint belonging to any other brand is reachable only through its brand-qualified URL.

A brand segment naming a brand that does not exist gets the same 404 as an unknown endpoint handle, so the URL cannot be used to find out which brands an installation has.

Endpoint handles are a different matter and always have been: a known handle with a bad signature answers 401, an unknown one answers 404, so someone who already knows a brand can tell one from the other. Nothing behind the handle is reachable without the endpoint's own credential; treat a handle as a name, not as a secret.

Signature and replay

An inbound endpoint is a public URL. Two things keep it from being an open door, and both are worth setting deliberately:

One thing the defaults cannot do for you: require_timestamp in an HMAC endpoint's auth_config. Without it the signature covers a body and nothing else, and a signature that says nothing about when it was made never expires — anyone who has ever seen one valid delivery can send it again next year. The replay cache closes its own window (ten minutes by default) and not a second more. It is not switched on for you because a sender that does not send the timestamp header would start failing on upgrade; instead such an endpoint is logged as inbound_signature_without_timestamp, at most once an hour. Turn it on as soon as the sender is known to send the header:

Inbound rate limiting

An inbound endpoint accepts a fixed number of requests per minute. The limit is checked first, before the method allowlist and before authentication, so a flood cannot be used to make the site do work.

IP allowlist

ip_allowlist is one of the inbound auth schemes. It accepts single addresses and CIDR ranges, IPv4 and IPv6:

It fails closed: an endpoint with an empty or missing allowlist rejects every request. Behind a proxy or load balancer, configure Laravel's TrustProxies — otherwise $request->ip() is your proxy's address and no allowlist will ever match.

Failure alerting

Set recipients (and an optional Slack webhook) so an admin is notified when a delivery fails after all retries; alerts are throttled per hook. A hook is auto-disabled after circuit_breaker.threshold consecutive terminal failures.

Concepts

Integration presets

Rather than hand-writing a Slack or Discord payload, pick the destination and fill in a URL. Presets ship for Slack, Discord, Microsoft Teams, Zapier, Make, n8n and generic JSON; each one creates a normal outbound webhook with a working payload template you can then edit like any other. CP → Webhooks → Integrations.

Rules

A rule is a When → If → Then flow: an incoming trigger (a Statamic event or an inbound webhook), an optional condition tree with AND/OR groups, and an ordered list of actions — send an outbound webhook, create or update an entry, create a form submission, dispatch an event, send an email or a Slack message, set a field, write a log note. Conditions and actions are registry-driven, so a site can add its own (see Extending).

Rules are the layer between "something happened" and "these requests go out", without a listener class.

Templates

A template is a reusable payload body, referenced by handle from any number of outbound webhooks. The body is rendered with token variables ({{ entry:title }}, {{ system:timestamp_iso }}, …) that are resolved from the trigger payload at delivery time. Attach one to a webhook so several webhooks can share a single payload shape; deleting a template detaches the webhooks using it and they fall back to their inline body.

Usage example

  1. CP → Webhooks → Outbound → Create.
  2. Pick trigger entry.published, scope to a collection.
  3. Set destination URL, method and HMAC secret.
  4. Use the JSON template editor:

  5. Save, publish a test entry, watch it appear under Deliveries.

Extending

The addon is intentionally registry-driven. Register your own from any service provider:

Each registry has its own contract under Goldnead\WebhookManager\Contracts.

Custom event triggers (any event class)

Out of the box the addon reacts to a fixed set of Statamic events (entry saved/published/…, form submitted, user saved, asset saved). If you want any other Laravel or Statamic event — your own domain events or a third-party addon's — to fire webhooks, register it as a custom event trigger. No listener class required: the addon attaches one generic listener that normalises the event into the standard dispatch pipeline, and the trigger shows up in the CP trigger picker (Outbound + Rules) automatically.

Config-driven — add entries to the event_triggers map in config/webhook-manager.php. The array key is the trigger handle (unless you set handle explicitly):

A payload class is just an invokable that maps the event to an array:

Programmatic — register the same thing in code from your service provider's boot() method (e.g. to ship a preconfigured trigger with your own addon). It funnels into the exact same generic listener + registry registration as the config path:

When no payload mapper is given, the listener builds the payload from the event's toArray() if present, otherwise its public properties (and passes through an event that is already an array).

Deliveries on the object (subject)

Every delivery row records which object it was about, in two columns on webhook_deliveries: subject_type (payment, offer, entry, …) and subject_id, indexed together. They are filled once, when the snapshot is written, by SubjectResolver, in this order: an explicit subject_type / subject_id pair in the payload; a key configured for a type (payment_id, payment.id); a trigger handle matching a configured pattern (payments.*) plus the event's source reference; and finally the event's own source type and reference, which is how the built-in entry, user, asset and form-submission triggers get a subject without configuration. The map lives in config/webhook-manager.php under subjects; add your own types there.

Read the log from the object's side with the facade:

In the Control Panel the delivery listing has a subject filter above the table and a Subject column, and the detail screen shows the subject next to the trigger.

To show the same log on your own addon's Inertia page, use the globally registered component. It fetches through the CP's deliveries/for-subject endpoint, so the viewer's view webhook deliveries permission and brand scope apply unchanged, and replay goes through the same route as the listing:

Guarding with Statamic.$components.has() keeps your page working when Webhook Manager is not installed. There is no Blade partial: the Statamic 6 Control Panel has no Blade pages left, so the component is the only embed.

Load order & overwriting

Register from the boot() method of your own service provider. Statamic boots addon providers before application providers, so by the time your boot() runs the Webhook Manager registries exist and are seeded with the built-in defaults. Registering from register() (or before this addon boots) is not supported.

Registries are keyed by handle: registering a trigger, condition, action, auth scheme, resolver, evaluator, preset or inbound action handler whose handle() matches an existing one replaces it. That is the supported way to override a built-in — but pick unique handles for genuinely new registrations to avoid clobbering defaults.

Boundary vs. goldnead/statamic-automations

Both addons can react to the same Statamic events (e.g. EntrySaved). Pick one place per concern: if an automation already fires a webhook for an event, don't also configure a Webhook Manager trigger for that same event and destination — you will double-fire.

Architecture

Roadmap

Forward-looking design questions that may evolve in future releases:

  1. Antlers/Tokens vs. a dedicated mini-template language.
  2. Whether outbound hooks are modeled as specialised rules or kept separate.
  3. How editable replay snapshots should be.
  4. Whether inbound directly writes content or always goes through the action layer.
  5. Final extensibility API surface.

Console commands

Testing

Feature tests cover the outbound delivery flow, failure logging, replay, inbound dispatch & signature verification, rule execution, template CRUD and permission masking; unit tests cover the renderer, mapper, condition/rule engines, retry planner and HMAC verifier.

Component tests (Vitest)

The Control Panel is a Vue SPA, and until 1.6.0 nothing in this package could execute a line of it. PHPUnit reaches the controller and the props it hands over; the QA harness clicks through the finished screen. Between the two sat the component logic — and that is where a Content-Type header that arrives as a PSR-7 array instead of a string took down an entire panel without anything reporting an error.

Vitest closes that gap. It is deliberately narrow:

Setup notes, in case something fails at an import rather than at an assertion:

Structural guards in PHPUnit (e.g. DeliveryShowHandlesArrayHeadersTest) stay alongside these: they catch a newly added component that reintroduces a known-bad pattern, which a component test — testing only components that exist — cannot. The Vitest test catches the logic being wrong.

This addon is the reference implementation for the other addons in this family. When rolling the layer out, copy vite.config.js's test block, tests/js/setup.js and the test script verbatim, then port the tests.

Local playground

Spin up a full Statamic 6 site with the addon wired in as a path repository (SQLite, CP user, seeded sample records) so you can click through the Control Panel:

End-to-end smoke test

./scripts/smoke-test.sh installs a throwaway Statamic project, wires the addon, then renders a payload template and delivers it to a local receiver through the real DeliveryEngine, asserting the Delivery is recorded as a success.

License

Commercial license. See LICENSE.


All versions of statamic-webhook-manager with dependencies

PHP Build Version
Package Version
Requires php Version ^8.2
goldnead/statamic-brand-context Version ^1.13
illuminate/database Version ^12.0|^13.0
illuminate/http Version ^12.0|^13.0
illuminate/queue Version ^12.0|^13.0
illuminate/support Version ^12.0|^13.0
inertiajs/inertia-laravel Version ^2.0 || ^3.0
statamic/cms Version ^6.0
Composer command for our command line client (download client) This client runs in each environment. You don't need a specific PHP version etc. The first 20 API calls are free. Standard composer command

The package goldnead/statamic-webhook-manager contains the following files

Loading the files please wait ...