Skip to content

Powered by Grav + Helios

Webhooks

Webhooks

Register outgoing HTTP webhooks that fire when API write events happen (page/media/user mutations, config updates, GPM installs, Grav upgrades). Each webhook has a URL, an event filter list, an optional shared secret for HMAC signing, and a delivery log. Permissions: api.webhooks.read / api.web...

Register outgoing HTTP webhooks that fire when API write events happen (page/media/user mutations, config updates, GPM installs, Grav upgrades). Each webhook has a URL, an event filter list, an optional shared secret for HMAC signing, and a delivery log.

Permissions: api.webhooks.read / api.webhooks.write. Secrets are always redacted in API responses — the full value is shown once on creation and never again.

Valid event names

* (all), page.created, page.updated, page.deleted, page.moved, page.translated, pages.reordered, media.uploaded, media.deleted, user.created, user.updated, user.deleted, config.updated, gpm.installed, gpm.removed, grav.upgraded.

List Webhooks

GET /webhooks
List all configured webhooks. Secrets are redacted (first 6 + last 4 chars, middle masked).
JSON
{"data": [{"id": "wh_abc123", "url": "https://example.com/hook", "events": ["page.created", "page.updated"], "secret": "grav_a****************_xyz", "enabled": true}]}

Response Codes

200 Webhooks returned.
401 Unauthorized.
403 Missing `api.webhooks.read` permission.

Create Webhook

POST /webhooks
Register a new webhook. URL is validated as a syntactically correct absolute URL. Events (if supplied) must come from the allowed list (see chapter intro). The response includes the generated webhook id and the full, un-redacted secret — this is the only time the secret is returned in full.

Parameters

Name Type Description
url required string Absolute HTTP(S) URL to POST payloads to.
events optional array Event filter. Use `["*"]` to receive every event, or list specific events. Defaults to all.
secret optional string Shared secret used to sign request bodies (HMAC-SHA256 sent as `X-Hub-Signature-256`). A random one is generated if omitted.
enabled optional boolean Defaults to true.
JSON
{"url": "https://example.com/hook", "events": ["page.updated", "page.deleted"]}
JSON
{"data": {"id": "wh_abc123", "url": "https://example.com/hook", "events": ["page.updated", "page.deleted"], "secret": "grav_abcdef1234567890", "enabled": true}}

Response Codes

201 Webhook created; Location header points to the new webhook.
400 Missing `url`, invalid URL, or invalid event name.
401 Unauthorized.
403 Missing `api.webhooks.write` permission.

Get Webhook

GET /webhooks/{id}
Get a single webhook by id. Secret is redacted. Returns an ETag; use `If-None-Match` for conditional fetching.

Parameters

Name Type Description
id required string Webhook id (path param).
JSON
{"data": {"id": "wh_abc123", "url": "https://example.com/hook", "events": ["page.updated"], "secret": "grav_a****************_xyz", "enabled": true}}

Response Codes

200 Webhook returned.
304 Not modified (ETag match).
401 Unauthorized.
403 Missing `api.webhooks.read` permission.
404 Webhook not found.

Update Webhook

PATCH /webhooks/{id}
Partial update — only supplied fields are changed. `url` is re-validated; `events` are re-validated against the allowed event list. Supports [optimistic concurrency control](/20/api/getting-started#concurrency-control) via the `If-Match` header.

Parameters

Name Type Description
id required string Webhook id (path param).
url optional string New URL.
events optional array Replacement event filter.
secret optional string New shared secret.
enabled optional boolean Enable/disable the webhook without deleting it.
JSON
{"enabled": false}
JSON
{"data": {"id": "wh_abc123", "url": "https://example.com/hook", "events": ["page.updated"], "secret": "grav_a****************_xyz", "enabled": false}}

Response Codes

200 Webhook updated.
400 Invalid URL or invalid event name.
401 Unauthorized.
403 Missing `api.webhooks.write` permission.
404 Webhook not found.
412 ETag mismatch.

Delete Webhook

DELETE /webhooks/{id}
Delete a webhook. Delivery history is also removed.

Parameters

Name Type Description
id required string Webhook id (path param).

Response Codes

204 Webhook deleted.
401 Unauthorized.
403 Missing `api.webhooks.write` permission.
404 Webhook not found.

Delivery Log

GET /webhooks/{id}/deliveries
Paginated delivery log for a single webhook. Each record contains the event, outgoing URL, HTTP response status, duration, success flag, and timestamps. Useful for debugging webhook failures.

Parameters

Name Type Description
id required string Webhook id (path param).
page optional integer Page number (default 1).
per_page optional integer Items per page.
JSON
{"data": [{"event": "page.updated", "url": "https://example.com/hook", "status_code": 200, "success": true, "duration_ms": 142, "delivered_at": "2026-04-17T12:00:00+00:00"}], "meta": {"total": 57, "page": 1, "per_page": 50}}

Response Codes

200 Deliveries returned.
401 Unauthorized.
403 Missing `api.webhooks.read` permission.
404 Webhook not found.

Test Webhook

POST /webhooks/{id}/test
Send a synthetic test payload to the webhook URL and return the delivery result immediately. Use this to verify endpoint reachability + signature verification without waiting for a real event. Returns 502 (with the delivery object in the body) if the remote returns an error status or cannot be reached.

Parameters

Name Type Description
id required string Webhook id (path param).
JSON
{"data": {"event": "webhook.test", "url": "https://example.com/hook", "status_code": 200, "success": true, "duration_ms": 118, "delivered_at": "2026-04-17T12:00:00+00:00"}}

Response Codes

200 Test delivered successfully.
401 Unauthorized.
403 Missing `api.webhooks.write` permission.
404 Webhook not found.
502 Test delivery failed; see `data` for details.