PHPackages                             fucodo/webhook - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [API Development](/categories/api)
4. /
5. fucodo/webhook

ActiveNeos-package[API Development](/categories/api)

fucodo/webhook
==============

Configurable incoming/outgoing webhooks with pluggable actions for Neos Flow

084↓100%PHP

Since Nov 14Pushed 5mo agoCompare

[ Source](https://github.com/fucodo/webhook)[ Packagist](https://packagist.org/packages/fucodo/webhook)[ RSS](/packages/fucodo-webhook/feed)WikiDiscussions main Synced 1mo ago

READMEChangelogDependenciesVersions (1)Used By (0)

Fucodo.Webhook
==============

[](#fucodowebhook)

Configurable incoming/outgoing webhooks with pluggable actions for Neos Flow.

This package provides:

- Public incoming webhook endpoint(s) with HMAC verification.
- A small action system so you can plug behavior for each incoming webhook (built-ins: `log`, `http-forward`).
- An HTTP client with timeouts, proxy, retry support.
- Outgoing webhook entity + sender service to notify external systems.
- CLI tooling to list/create/delete incoming webhooks and print ready-to-use cURL/Guzzle examples.

Requirements
------------

[](#requirements)

- PHP &gt;= 8.1
- Neos Flow ^8.0 or ^9.0
- Doctrine ORM ^2.16
- guzzlehttp/guzzle ^7.9

Installation
------------

[](#installation)

```
composer require fucodo/webhook
```

Flow detects the package by its package key `Fucodo.Webhook`. Run migrations if prompted, then warm up caches:

```
./flow doctrine:migrate
./flow flow:cache:flush
```

Quick start
-----------

[](#quick-start)

1. Create a `WebhookActionConfiguration` (persist an entity that selects an action and its options).
    - For a quick demo, you can create one for the built-in HTTP forwarder (`http-forward`) pointing to a test URL.
2. Create an incoming hook (via CLI) and link it to the action configuration:

```
./flow incomingwebhook:create \
  --path-segment github \
  --action-config
```

3. Call your endpoint:

- URL: `https://your-host/webhook/in/github`
- Default allowed methods: `POST` (configurable)
- Optional HMAC header if you set a secret: `X-Signature: sha256=`

The controller will verify the signature (if secret configured), dispatch the selected action with the JSON payload, and return a JSON result.

Incoming endpoint
-----------------

[](#incoming-endpoint)

- Route: `/webhook/in/{pathSegment}`
- Controller/action: `Fucodo.Webhook -> IncomingWebhookController::receiveAction()`
- Returns JSON body like:

```
{"success":true,"message":"...","data":{}}
```

- Status codes:
    - 200 on success
    - 400 on action failure
    - 401 on signature verification failure
    - 404 if webhook not found or disabled
    - 405 if HTTP method not allowed

The `pathSegment` is the unique slug of your `IncomingWebhook` entity.

### Signature verification (HMAC)

[](#signature-verification-hmac)

If your `IncomingWebhook` has a `secret` set, requests must include a header:

```
X-Signature: sha256=

```

where `` is `hash_hmac('sha256', , )`.

Notes:

- Header name is case-insensitive; `X-Signature` or `x-signature` both work.
- If no secret is configured, verification is skipped (see setting below).

Configuration (Settings.yaml)
-----------------------------

[](#configuration-settingsyaml)

Default settings live in `Configuration/Settings.yaml` of this package. You can override them in your application settings.

```
Fucodo:
  Webhook:
    incoming:
      # Allowed HTTP methods if not set on entity level
      defaultAllowedMethods: ['POST']
      # If true, allow requests without signature when secret is missing
      allowUnsignedWhenSecretMissing: true
    http:
      # total timeout (seconds)
      timeout: 5
      # connection timeout (seconds)
      connectTimeout: 2
      # TLS certificate verification
      verify: true
      # proxy string in Guzzle format, e.g. "http://user:pass@proxy:8080"
      proxy: null
      # number of retries on network / transient errors
      retries: 2
      # delay between retries (milliseconds)
      retryDelayMs: 200
```

The HTTP settings are used by the internal `HttpClient` (Guzzle wrapper) for `http-forward` and outgoing webhooks.

Routing
-------

[](#routing)

This package registers the following route by default:

```
-
  name: 'Webhook public incoming'
  uriPattern: 'webhook/in/{pathSegment}'
  defaults:
    '@package': 'Fucodo.Webhook'
    '@controller': 'IncomingWebhook'
    '@action': 'receive'
    '@format': 'html'
  appendExceedingArguments: true
```

You can prepend/override in your global `Configuration/Routes.yaml` if desired.

Domain model overview
---------------------

[](#domain-model-overview)

- `IncomingWebhook`

    - `pathSegment` (unique slug used in the URL)
    - `enabled` (boolean)
    - `secret` (optional, for HMAC verification)
    - `allowedMethods` (array, defaults to `['POST']`)
    - `staticHeaders` (optional, reserved for future enhancements)
    - `actionConfiguration` (ManyToOne to `WebhookActionConfiguration`)
- `WebhookActionConfiguration`

    - Holds the selected action type and an `options` array consumed by the action.
- `OutgoingWebhook`

    - `targetUrl`, `httpMethod`, `headers` (array)
    - `payloadTemplate` (optional string template)
    - `enabled` (boolean)
    - Optional `actionConfiguration` to transform/build payload before sending
    - Optional `triggerEvent` (free-form domain event name you can use in your app)

Action dispatching
------------------

[](#action-dispatching)

Incoming requests are dispatched to the `WebhookActionDispatcher` with:

- `payload` — JSON-decoded body (falls back to `[]` on invalid JSON)
- `context` — includes `headers` and raw URL `query`
- `options` — taken from `WebhookActionConfiguration`

Actions implement:

```
interface ActionInterface {
    public function execute(array $payload, array $context = [], array $options = []): ActionResultInterface;
    public static function identifier(): string; // machine name
    public static function label(): string;      // human-readable label
}
```

Return an `ActionResult` using the helpers:

```
ActionResult::ok(array $data = [], ?string $message = null)
ActionResult::fail(?string $message = null, array $data = [])
```

### Built-in actions

[](#built-in-actions)

- `log` (`LogAction`)
    - Options: `level` (e.g. `info`, `warning`, `error`)
    - Logs the payload/context/options to the PSR logger.
- `http-forward` (`HttpForwardAction`)
    - Options:
        - `url` (required)
        - `method` (default: `POST`)
        - `headers` (array, merged with `Content-Type: application/json`)
    - Sends the incoming JSON payload to the target URL using the configured HTTP client.

### Writing a custom action

[](#writing-a-custom-action)

Create a class implementing `Fucodo\Webhook\WebhookAction\ActionInterface` and register it as a Flow bean/service. Example skeleton:

```
