Skip to content

Reference

Configuration reference

php artisan vendor:publish --tag=webhook-signature-config

endpoints

A map of endpoint name to its configuration. Empty by default — until you declare one, every lookup throws, because deny-by-default applies to configuration as much as to signatures.

'endpoints' => [
    'github' => [
        'scheme'    => 'github',
        'secrets'   => ['current' => env('GITHUB_WEBHOOK_SECRET')],
        'tolerance' => 300,
        'replay'    => true,
    ],
],
Key Type Default Meaning
scheme string required A registered scheme name
secrets string | list | map The live secrets. Also accepted as secret
tolerance int | null scheme default Window in seconds. null disables it. Omit to inherit
replay bool false opts this endpoint out of single-use enforcement

secrets

'secrets' => 'one-secret',
'secrets' => ['secret-a', 'secret-b'],
'secrets' => ['new' => 'secret-b', 'old' => 'secret-a'],   // preferred

For an asymmetric scheme (postal) these hold PEM public keys rather than shared secrets. They are not confidential and cannot be used to sign, but they rotate the same way, so the shape is the same.

A list because rotation is not atomic. Labels are reported back as VerifiedWebhook::$secretId, which is how you know when the old secret can safely be removed.

The first secret signs outbound messages.

Empty and null entries are dropped. An endpoint that ends up with none refuses everything with no_secret_configured — an unset environment variable must not read as "verification off".

tolerance

Two levels, not three: the endpoint's value if it set one, otherwise the scheme's own default. Omitting the key inherits; setting it to null explicitly disables the window.

A scheme that binds no timestamp cannot gain a window from configuration, whatever you write here. There is nothing signed to check against.

schemes

Extra schemes built by the generic HMAC driver. Empty by default. See custom schemes for the key list.

Registered after the bundled drivers, so an entry named github replaces the bundled GitHub scheme rather than colliding with it.

replay

'replay' => [
    'enabled' => env('WEBHOOK_SIGNATURE_REPLAY', false),
    'store'   => env('WEBHOOK_SIGNATURE_REPLAY_STORE'),
    'ttl'     => 3600,
],
Key Type Default Meaning
enabled bool false Master switch for single-use enforcement
store string | null null Cache store name; null uses the application default
ttl int 3600 Seconds a delivery is remembered as seen

Off by default because it is only correct against a store shared by every node — see replay protection. Set store to Redis, Memcached, or a shared database store before enabling.

ttl must be at least as long as your widest tolerance window, or a delivery could age out of the guard while still passing the timestamp check.

Environment variables

The package reads only what its own config file references:

Variable Used for
WEBHOOK_SIGNATURE_REPLAY replay.enabled
WEBHOOK_SIGNATURE_REPLAY_STORE replay.store

Secrets are referenced by whatever names you choose in your own endpoints block.

What the package registers

Route middleware

Aliased as webhook.signature, taking the endpoint name as its parameter:

->middleware('webhook.signature:github')

Responses on refusal: 401 for anything the caller got wrong, 500 when the failure is our own configuration — so a provider retries a misconfigured receiver instead of discarding events it delivered correctly.

HTTP client macro

Http::webhookSignature($endpoint, ?int $timestamp = null) returns a PendingRequest that signs whatever it finally sends:

Http::webhookSignature('outbound')->post($subscriber->url, $payload);

Registered only if nothing else has claimed the name, so an application macro of the same name keeps precedence.

Console commands

php artisan webhook:sign {endpoint} prints the headers — or, with --curl, a runnable command — for a payload signed with the endpoint's real secret.

php artisan webhook:verify {endpoint} takes a delivery you actually received and explains the outcome, distinguishing a stale delivery from a forged one. See debugging a failed verification.

Facade

Webhooks (Cbox\WebhookSignature\Facades\Webhooks), aliased by package discovery. Use it for the cases the macro and the middleware do not cover: verifying something other than the current request, or signing with a per-subscriber secret.