| description | Receive HTTP callbacks from other services, verify them, and keep an audit of every delivery. |
|---|
The webhooks module is the way a Nails app receives an HTTP callback. A class under src/Webhooks is the endpoint. The module discovers it, checks the request, calls your handler, and records what happened.
composer require nails/module-webhooksA payment driver is the usual shape: Stripe calls back when a payment succeeds, the handler checks the signature, and handle() marks the payment complete. That handler lives in the driver, not in this module. Invoice is not a dependency.
- The provider calls
/webhooks/{slug}, or/webhooks/{slug}/{token}when the handler has instances. - An unknown, disabled, or badly addressed handler answers 404. Nothing is written down.
- A protected handler verifies the request. A bad signature is 401 and
handle()is not called. A handshake (Slackurl_verification, Metahub.challenge) is answered immediately. - A repeat of a delivery that already succeeded is recorded as ignored and
handle()is not called again. See Deliveries. handle()returns a result. The HTTP status comes from that result. An exception is 500, so the provider retries.- One audit row is stored, a line is written to the day's file log, and
delivery.completedis triggered.
GET and POST are accepted. Anything else is 405.
{% hint style="warning" %}
CSRF protection is off in Nails by default. If the app turns it on, exclude webhooks/.*. The provider cannot send a CSRF token.
{% endhint %}
Drop a class in src/Webhooks that implements Nails\Webhooks\Interfaces\Webhook. Nails\Webhooks\Traits\Defaults fills in an empty description and leaves the handler enabled.
namespace App\Webhooks;
use Nails\Webhooks\Delivery;
use Nails\Webhooks\Interfaces\Webhook;
use Nails\Webhooks\Result;
use Nails\Webhooks\Traits\Defaults;
class BuildFinished implements Webhook
{
use Defaults;
public function getLabel(): string
{
return 'Build finished';
}
public function handle(Delivery $oDelivery): Result
{
$oPayload = $oDelivery->json();
$oDelivery->log('Build ' . ($oPayload->id ?? ''));
return Result::accepted('Recorded build');
}
}That class is served at /webhooks/app/build-finished. You do not register it. Discovery, slugs, and return values are covered in Handlers.
Copy the URL from Admin → Webhooks → Definitions. Renaming or moving the class changes the URL, and there is no override that lets the two drift apart.
| Page | What it covers |
|---|---|
| Handlers | Discovery, the slug, Delivery, and Result |
| Protection | Shared secrets, HMAC signatures, challenges, and subscribing the URL with a provider |
| Instances | One class, many copies: each with its own channel, secret, and URL |
| Deliveries | Duplicate suppression, the file log, the audit table, and the admin screens |