Webhooks
The Content API fires outbound signed POST notifications when a discovered resource is created, updated, or deleted. Receivers verify an HMAC signature over the body and use the event to drive downstream caches, search indexes, or previews. Nothing is pulled; the platform pushes.
Endpoints
Two sources of targets, both signed the same way:
- Named endpoints — managed on the panel's API settings page (
ManageWebhooks). Each row stores a name, URL, the event list it subscribes to, and anis_activetoggle. On creation a signing secret is minted (whsec_…) and shown once; it is stored encrypted and never displayed again. - Env default — set
WEBHOOK_URL(and optionallyWEBHOOK_SECRET) to fan every event out to a single default target, independent of the named-endpoint table.
WEBHOOK_URL=https://example.com/hooks/edge
WEBHOOK_SECRET=whsec_shared_rotation_key
Signing
Each delivery is a JSON POST with two custom headers:
X-Webhook-Event— the event name (e.g.post.created).X-Webhook-Signature—sha256=<hex>where the hex ishash_hmac('sha256', $rawBody, $secret).
Verify by recomputing the HMAC over the raw request body (not a re-serialized copy) and comparing in constant time. Empty secret skips the signature header, so always set one in production.
Events
Events are derived from discovered panel resources — the same discovery that drives REST and API keys. Adding a Filament resource automatically produces <singular>.created, <singular>.updated, and <singular>.deleted; publishable resources also emit <singular>.published. A webhook.test event is available from each endpoint's "Send test" action.
The payload is resource-shaped: type, id, title, slug, path, published, url, and actor_id (the panel user who triggered the change).
Delivery log
Every attempt — success or failure — writes one row to the deliveries table via WebhookDelivery:
| Column | Meaning |
|---|---|
event |
Event name dispatched |
url |
Target URL hit |
success |
Boolean, HTTP 2xx |
status_code |
Response status, null on connection error |
error |
Truncated response body or exception message |
duration_ms |
Round-trip time |
Inspect the log on the API settings page to debug 4xx/5xx responses, timeouts, or DNS failures. A failed POST still logs a red row, so the log is the source of truth for "did it arrive".
sequenceDiagram
participant Panel as Panel CRUD
participant Obs as Model Observer
participant Disp as Webhooks::dispatch
participant Job as DeliverWebhookJob
participant Recv as Receiver
participant Log as WebhookDelivery
Panel->>Obs: created/updated/deleted
Obs->>Disp: event + payload
Disp->>Job: per active endpoint + env URL
Job->>Recv: POST JSON + X-Webhook-Signature
Recv-->>Job: HTTP status
Job->>Log: insert (success, status_code, error, duration_ms)