Вебхуки¶
Регистрируйте исходящие HTTP-вебхуки, которые срабатывают при событиях записи в API (изменения страниц, медиафайлов и пользователей, обновления конфигурации, установки через GPM, обновления Grav). У каждого вебхука есть URL, список фильтров событий, необязательные собственные заголовки, сгенерированный секрет для подписи HMAC и журнал доставок.
Разрешения: api.webhooks.read / api.webhooks.write. Секреты генерируются сервером и всегда маскируются в ответах API, кроме одного раза — в ответе, создающем вебхук.
Допустимые имена событий¶
* (все), 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.
Доставки¶
Каждая доставка — это JSON POST вида {"event": "...", "timestamp": "<ISO 8601>", "data": {...}, "webhook_id": "wh_..."}. Grav добавляет к каждой доставке следующие заголовки:
| Заголовок | Значение |
|---|---|
X-Grav-Signature |
Hex HMAC-SHA256 необработанного тела запроса с ключом-секретом вебхука |
X-Grav-Event |
Имя события вебхука, например page.updated |
X-Grav-Delivery |
Уникальный идентификатор доставки (dlv_...) |
Собственные headers вебхука могут задавать другие заголовки, включая Content-Type и User-Agent, но не могут заменить эти три: создание или обновление вебхука с одним из них возвращает 422. Принимаются только URL http и https, а URL, который разрешается в приватный, loopback-, link-local или зарезервированный адрес, отклоняется — как при сохранении, так и повторно во время доставки. Перенаправления не выполняются.
Вебхук, у которого 5 доставок подряд завершились неудачей, автоматически отключается с указанием disabled_reason; успешная доставка сбрасывает счётчик. Хранятся последние 50 доставок каждого вебхука.
Список вебхуков¶
GET /webhooks
Выводит все настроенные вебхуки. Секреты маскируются (первые 6 и последние 4 символа сохраняются, середина скрыта).
Ответ
{"data": [{"id": "wh_abc123", "url": "https://example.com/hooks/grav", "secret": "whsec_************************************************a1b2", "events": ["page.created", "page.updated"], "enabled": true, "headers": {}, "created": 1774008000, "failure_count": 0}]}
Коды ответов
| Код | Описание |
|---|---|
200 |
Вебхуки возвращены. |
401 |
Не авторизован. |
403 |
Нет разрешения api.webhooks.read. |
Создание вебхука¶
POST /webhooks
Регистрирует новый вебхук. URL должен быть абсолютным URL http или https, который не разрешается в приватный или зарезервированный адрес. События (если переданы) должны быть из разрешённого списка (см. вводную главу). Сервер генерирует идентификатор вебхука и секрет для подписи; ответ — единственный раз, когда возвращается полный секрет.
Параметры
| Имя | Тип | Обязательный | Описание |
|---|---|---|---|
url |
string | Да | Абсолютный URL HTTP(S), на который отправляются POST-запросы с данными. |
events |
array | Нет | Фильтр событий в виде JSON-массива имён событий. Используйте ["*"], чтобы получать все события, или перечислите конкретные события. По умолчанию ["*"]. Всё, кроме массива допустимых имён, даёт 422. |
headers |
object | Нет | Собственные заголовки, отправляемые с каждой доставкой, в виде объекта «имя заголовка → значение». X-Grav-Signature, X-Grav-Event и X-Grav-Delivery (в любом регистре) отклоняются с 422, как и имена или значения, содержащие переводы строк. |
enabled |
boolean | Нет | По умолчанию true. |
Запрос
{"url": "https://example.com/hooks/grav", "events": ["page.created", "page.updated"], "enabled": true}
Ответ
{"data": {"id": "wh_abc123", "url": "https://example.com/hooks/grav", "secret": "whsec_0123456789abcdef0123456789abcdef0123456789abcdef", "events": ["page.created", "page.updated"], "enabled": true, "headers": {}, "created": 1774526400, "failure_count": 0}}
Коды ответов
| Код | Описание |
|---|---|
201 |
Вебхук создан; заголовок Location указывает на новый вебхук. |
401 |
Не авторизован. |
403 |
Нет разрешения api.webhooks.write. |
422 |
Отсутствует или неверен url (неверный синтаксис, не http/https либо приватный или зарезервированный адрес), events не является массивом допустимых имён событий, либо headers неверны или зарезервированы. |
Получение вебхука¶
GET /webhooks/{id}
Возвращает один вебхук по id. Секрет маскируется. Ответ содержит заголовок ETag.
Параметры
| Имя | Тип | Обязательный | Описание |
|---|---|---|---|
id |
string | Да | Идентификатор вебхука (параметр пути). |
Ответ
{"data": {"id": "wh_abc123", "url": "https://example.com/hooks/grav", "secret": "whsec_************************************************a1b2", "events": ["page.created", "page.updated"], "enabled": true, "headers": {}, "created": 1774008000, "failure_count": 0}}
Коды ответов
| Код | Описание |
|---|---|
200 |
Вебхук возвращён. |
401 |
Не авторизован. |
403 |
Нет разрешения api.webhooks.read. |
404 |
Вебхук не найден. |
Обновление вебхука¶
PATCH /webhooks/{id}
Частичное обновление: меняются только переданные поля. url, events и headers проверяются так же, как при создании. Секрет изменить нельзя. Повторное включение автоматически отключённого вебхука не сбрасывает его failure_count; это делает следующая успешная доставка.
Параметры
| Имя | Тип | Обязательный | Описание |
|---|---|---|---|
id |
string | Да | Идентификатор вебхука (параметр пути). |
url |
string | Нет | Новый URL. |
events |
array | Нет | Заменяющий фильтр событий (JSON-массив имён событий). |
headers |
object | Нет | Заменяющие собственные заголовки. Три заголовка X-Grav-*, которые задаёт Grav, отклоняются с 422. |
enabled |
boolean | Нет | Включить или отключить вебхук, не удаляя его. |
Запрос
{"url": "https://example.com/hooks/grav-v2", "events": ["page.created", "page.updated", "page.deleted"]}
Ответ
{"data": {"id": "wh_abc123", "url": "https://example.com/hooks/grav-v2", "secret": "whsec_************************************************a1b2", "events": ["page.created", "page.updated", "page.deleted"], "enabled": true, "headers": {}, "created": 1774008000, "failure_count": 0}}
Коды ответов
| Код | Описание |
|---|---|
200 |
Вебхук обновлён. |
401 |
Не авторизован. |
403 |
Нет разрешения api.webhooks.write. |
404 |
Вебхук не найден. |
422 |
Неверный url, events не является массивом допустимых имён событий, либо headers неверны или зарезервированы. |
Удаление вебхука¶
DELETE /webhooks/{id}
Удаляет вебхук. История доставок также удаляется.
Параметры
| Имя | Тип | Обязательный | Описание |
|---|---|---|---|
id |
string | Да | Идентификатор вебхука (параметр пути). |
Коды ответов
| Код | Описание |
|---|---|
204 |
Вебхук удалён. |
401 |
Не авторизован. |
403 |
Нет разрешения api.webhooks.write. |
404 |
Вебхук не найден. |
Журнал доставок¶
GET /webhooks/{id}/deliveries
Журнал доставок одного вебхука с постраничной разбивкой, сначала новые. Каждая запись содержит идентификатор доставки, событие, URL, отправленные заголовки и тело запроса, статус ответа и первые 1000 символов тела ответа, длительность, флаг успеха, error для транспортных сбоев и метку времени Unix created. Хранятся только последние 50 доставок.
Параметры
| Имя | Тип | Обязательный | Описание |
|---|---|---|---|
id |
string | Да | Идентификатор вебхука (параметр пути). |
page |
integer | Нет | Номер страницы (по умолчанию 1). |
per_page |
integer | Нет | Элементов на странице (по умолчанию plugins.api.pagination.default_per_page, 20). |
Ответ
{"data": [{"id": "dlv_9f2c4e1a7b3d5f60", "event": "page.updated", "url": "https://example.com/hooks/grav", "request_headers": {"Content-Type": "application/json", "User-Agent": "Grav-Webhook/1.0", "X-Grav-Signature": "5d41402abc4b2a76b9719d911017c592...", "X-Grav-Event": "page.updated", "X-Grav-Delivery": "dlv_9f2c4e1a7b3d5f60"}, "request_body": {"event": "page.updated", "timestamp": "2026-09-21T12:00:00+00:00", "data": {"page": {"route": "/blog/hello", "title": "Hello", "slug": "hello"}}, "webhook_id": "wh_abc123"}, "created": 1790000000, "status_code": 200, "response_body": "ok", "duration_ms": 142, "success": true}], "meta": {"pagination": {"page": 1, "per_page": 20, "total": 1, "total_pages": 1}}, "links": {"self": "/api/v1/webhooks/wh_abc123/deliveries?page=1&per_page=20"}}
Коды ответов
| Код | Описание |
|---|---|
200 |
Доставки возвращены. |
401 |
Не авторизован. |
403 |
Нет разрешения api.webhooks.read. |
404 |
Вебхук не найден. |
Тестовая доставка вебхука¶
POST /webhooks/{id}/test
Отправляет тестовую доставку (событие test с data.message, равным This is a test webhook delivery.) на URL вебхука и сразу возвращает запись доставки. Она подписывается и журналируется как настоящая доставка, а неудачный тест учитывается при автоматическом отключении после 5 сбоев как одна доставка. Используйте его, чтобы проверить, что эндпоинт доступен и проверяет подпись, не дожидаясь реального события. Возвращает 502 с той же записью доставки в теле, если получатель отвечает статусом вне 2xx или недоступен.
Параметры
| Имя | Тип | Обязательный | Описание |
|---|---|---|---|
id |
string | Да | Идентификатор вебхука (параметр пути). |
Ответ
{"data": {"id": "dlv_1a2b3c4d5e6f7a8b", "event": "test", "url": "https://example.com/hooks/grav", "request_headers": {"Content-Type": "application/json", "User-Agent": "Grav-Webhook/1.0", "X-Grav-Signature": "5d41402abc4b2a76b9719d911017c592...", "X-Grav-Event": "test", "X-Grav-Delivery": "dlv_1a2b3c4d5e6f7a8b"}, "request_body": {"event": "test", "timestamp": "2026-09-21T12:00:00+00:00", "data": {"message": "This is a test webhook delivery."}, "webhook_id": "wh_abc123"}, "created": 1790000000, "status_code": 200, "response_body": "ok", "duration_ms": 118, "success": true}}
Коды ответов
| Код | Описание |
|---|---|
200 |
Тест доставлен успешно. |
401 |
Не авторизован. |
403 |
Нет разрешения api.webhooks.write. |
404 |
Вебхук не найден. |
502 |
Тестовая доставка не удалась; тело содержит запись доставки с success: false. |