Настройте вебхуки в панели управления, принимайте доставки и проверяйте HMAC-подпись — с примерами для curl, Node.js и PHP.
Вебхук отправляет HTTP-запрос на ваш сервер, как только в вашем рабочем пространстве что-то происходит, — например, когда страница опубликована. Так вам не нужно постоянно спрашивать, изменилось ли что-нибудь. Каждая доставка подписана, чтобы ваш сервер мог отличить настоящие сообщения от поддельных.
Требования
- Роль владельца или админа в рабочем пространстве — пункт меню «Вебхуки» виден только этим ролям
- Начиная с Creator Pro; в Creator Free написано «Вебхуки доступны начиная с Creator Pro.»
- До 5 вебхуков в Creator Pro, 25 в Creator Agency, 100 в Enterprise (настраивается)
- Сервер, доступный по публичному https-адресу
Вебхуками вы управляете только в панели управления. С API-ключами их нельзя создать или изменить.
Создание вебхука
- Откройте в навигации «Вебхуки»
- Нажмите «+ Новый вебхук»
- Введите целевой URL и выберите события
- Необязательно: введите описание и выберите статус
- Нажмите «Создать вебхук»
- На шаге «Готово» скопируйте секрет подписи («Секрет подписи — виден только сейчас») и сохраните его на своём сервере — он показывается только сейчас
- Проверьте вебхук кнопкой «Тест». Результат остаётся на карточке до следующего теста: «Успешно доставлено» или «Не доставлено» с HTTP-кодом (или «нет ответа»), длительностью и началом ответа
- «Целевой URL (https://…)»
- До 2048 символов. Только https, без учётных данных в URL, без адресов localhost и без внутренних имён вроде .local, .internal или .lan. Имя должно указывать на публичные IP-адреса; LinkHarbor проверяет это заново перед каждой доставкой.
- «События»
- Хотя бы одно событие. В каждой группе есть «Выбрать все» и «Снять выделение».
- «Описание»
- Необязательно, до 255 символов — например, для чего нужен вебхук.
- «Статус»
- «Активен» или «Отключён». Отключённые вебхуки не получают доставок.
События
| Событие | В панели управления | Когда | Содержимое data |
|---|---|---|---|
| page.published | Страница опубликована | Страница опубликована — в панели управления, через API или AI-ассистентом | page_id, version, checksum, published_by_user_id (null для API-ключей и AI-ассистентов) |
| form.submitted | Форма отправлена | Посетитель отправил форму на одной из ваших страниц | form_id, form_name, submission_id, page_id, block_id, submitted_at, values (ключ поля → значение), consent (ключ согласия → true/false) |
| domain.verified | Домен подтверждён | Собственный домен успешно подтверждён | domain_id, hostname, verification_method, status |
| domain.disabled | Домен отключён | Собственный домен отключён в панели управления (не при понижении тарифа) | domain_id, hostname, status |
| webhook.test | – (кнопка «Тест») | Вы нажимаете «Тест» | message (текст на языке аккаунта того, кто запустил тест; не опирайтесь на него) |
form.submitted содержит значения ровно в том виде, в каком их отправил посетитель, но никогда не содержит его IP-адрес или браузер. Доставка выполняется в фоне: посетитель её не ждёт, а неудачная доставка не мешает отправке формы.
Структура доставки
LinkHarbor отправляет каждую доставку как POST с телом в JSON и такими заголовками:
| Заголовок | Содержимое |
|---|---|
| Content-Type | application/json |
| X-LinkHarbor-Event | Название события, например page.published |
| X-LinkHarbor-Delivery-Id | Уникальный ID этой доставки (UUID), совпадает с id в теле |
| X-LinkHarbor-Timestamp | Время отправки в Unix-секундах |
| X-LinkHarbor-Signature | sha256=, за которым следует подпись HMAC-SHA256 в hex |
| X-LinkHarbor-Signature-Version | v1 |
{
"id": "3f1c2a9e-8d4b-4a51-9f0e-6b7c1d2e3f40",
"event": "page.published",
"created_at": "2026-11-01T09:30:12+00:00",
"data": {
"page_id": 42,
"version": 8,
"checksum": "9b1e5c…",
"published_by_user_id": 7
}
} Повторные доставки получают новый id, а также поля redelivery_of (ID повторяемой доставки) и delivery_root (ID первой доставки в этой цепочке).
Проверка подписи
У каждого вебхука свой секрет подписи (32 hex-символа). Вы видите его ровно один раз: на последнем шаге при создании вебхука и сразу после «Секрет ↺». Сразу скопируйте его на свой сервер — LinkHarbor больше его не покажет. С его помощью LinkHarbor вычисляет подпись так:
- Составляет сообщение: временная метка, точка, ID доставки, точка, исходное тело — то есть timestamp.delivery_id.body
- Вычисляет HMAC-SHA256 от этого сообщения с секретом подписи в качестве ключа
- Отправляет результат как hex-строку с префиксом sha256=
Ваш сервер должен проверять подпись сравнением за постоянное время, отклонять доставки с временной меткой старше 300 секунд и хранить ID доставок как минимум столько же, чтобы распознавать повторы. Важно: подписывается исходное тело ровно в том виде, в каком оно пришло, — а не JSON, который вы закодировали заново.
import crypto from 'node:crypto';
import express from 'express';
const app = express();
const SECRET = process.env.LH_WEBHOOK_SECRET;
const seen = new Set(); // в продакшене: база данных или Redis со сроком хранения
app.post('/webhooks/linkharbor', express.raw({ type: 'application/json' }), (req, res) => {
const ts = req.get('X-LinkHarbor-Timestamp') || '';
const id = req.get('X-LinkHarbor-Delivery-Id') || '';
const sig = req.get('X-LinkHarbor-Signature') || '';
if (!/^\d+$/.test(ts) || Math.abs(Date.now() / 1000 - Number(ts)) > 300) {
return res.status(400).send('timestamp');
}
const expected = 'sha256=' + crypto
.createHmac('sha256', SECRET)
.update(ts + '.' + id + '.')
.update(req.body) // исходное тело в виде Buffer
.digest('hex');
const a = Buffer.from(expected);
const b = Buffer.from(sig);
if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) {
return res.status(401).send('signature');
}
const event = JSON.parse(req.body.toString('utf8'));
const key = event.delivery_root || event.id; // распознать повторные доставки одной цепочки
if (!seen.has(key)) {
seen.add(key);
// обработать здесь, лучше асинхронно в очереди
}
res.sendStatus(200);
});
app.listen(3000); <?php
$secret = getenv('LH_WEBHOOK_SECRET');
$body = file_get_contents('php://input');
$ts = $_SERVER['HTTP_X_LINKHARBOR_TIMESTAMP'] ?? '';
$id = $_SERVER['HTTP_X_LINKHARBOR_DELIVERY_ID'] ?? '';
$sig = $_SERVER['HTTP_X_LINKHARBOR_SIGNATURE'] ?? '';
if (!ctype_digit($ts) || abs(time() - (int)$ts) > 300) {
http_response_code(400);
exit('timestamp');
}
$expected = 'sha256=' . hash_hmac('sha256', $ts . '.' . $id . '.' . $body, $secret);
if (!hash_equals($expected, $sig)) {
http_response_code(401);
exit('signature');
}
$event = json_decode($body, true);
// Распознать повторные доставки по $event['delivery_root'] ?? $event['id'],
// затем обработать.
http_response_code(200); Проверка эндпоинта с помощью curl
Этот скрипт отправляет на ваш собственный сервер правильно подписанную тестовую доставку — удобно до того, как вы создадите вебхук в LinkHarbor:
SECRET="your-signing-secret"
URL="https://example.com/webhooks/linkharbor"
TS=$(date +%s)
ID=$(uuidgen | tr 'A-Z' 'a-z')
BODY='{"id":"'"$ID"'","event":"webhook.test","created_at":"2026-11-01T09:30:12+00:00","data":{"message":"Это тестовое событие для этого вебхука."}}'
SIG="sha256=$(printf '%s' "$TS.$ID.$BODY" | openssl dgst -sha256 -hmac "$SECRET" -hex | sed 's/^.* //')"
curl -X POST "$URL" \
-H "Content-Type: application/json" \
-H "X-LinkHarbor-Event: webhook.test" \
-H "X-LinkHarbor-Delivery-Id: $ID" \
-H "X-LinkHarbor-Timestamp: $TS" \
-H "X-LinkHarbor-Signature: $SIG" \
-H "X-LinkHarbor-Signature-Version: v1" \
-d "$BODY" Ответ, тайм-аут и повторная доставка
- Любой ответ со статусом 2xx считается успешным; перенаправлениям (3xx) LinkHarbor не следует
- У вашего сервера есть 10 секунд на ответ и не больше 5 секунд на установку соединения
- Все вебхуки одного события делят в сумме 25 секунд; те, до которых к этому времени не дошла очередь, помечаются как неудачные и могут быть доставлены повторно
- LinkHarbor не повторяет неудачные доставки автоматически
- Неудачные доставки можно отправить повторно вручную, но не ту, которая с тех пор прошла успешно. У повторной доставки новый id и новый created_at
- На одно событие допускается не больше 3 попыток, включая первую доставку; после этого оно попадает в «Недоставленные» и повторно отправить его уже нельзя
Доставки в панели управления
«Доставки» открывает журнал вебхука. В нём три вкладки:
- «Все доставки»
- Все доставки с фильтрами по событию, статусу («Успешно (2xx)», «Ошибка клиента (4xx)», «Ошибка сервера (5xx)», «Неудачно») и «Лимит». «↺ Повторить» снова отправляет неудачную доставку, если после неё не было успешной. По клику на строку видны запрос, ответ, тело и исходный JSON.
- «Повторы»
- Цепочки повторных доставок с результатом «Восстановлено» или «Всё ещё не работает» и числом попыток.
- «Недоставленные»
- Доставки, у которых все разрешённые попытки не удались, — первая доставка и повторные вместе. Вкладка только для просмотра: повторно доставить их уже нельзя. Проверьте свой эндпоинт; после этого новые события доставляются как обычно. Если данные нужны, получите их через API.
Если повторная доставка не проходит, панель управления называет причину:
- «Не доставлено повторно: лимит попыток для этого события исчерпан (неудачных попыток: …). Оно находится в разделе “Недоставленные”.»
- Все 3 попытки израсходованы.
- «Не доставлено повторно: это событие уже успешно доставлено.»
- Более поздняя доставка в той же цепочке уже прошла успешно.
Проверка в панели управления
В разделе «Проверка» есть два вспомогательных инструмента:
- «Набор для проверки»
- «Загрузить набор» показывает алгоритм, имена заголовков, формат подписываемого сообщения и рекомендуемый допуск в 300 секунд. Самого секрета в наборе нет.
- «Проверить подпись»
- Введите ID доставки, временную метку, подпись и исходное тело запроса полученной доставки. LinkHarbor ответит «Подпись верна» или «Подпись неверна». Временную метку и подпись скопируйте из заголовков X-LinkHarbor-Timestamp и X-LinkHarbor-Signature. Проверка работает только для доставок за последние 5 минут; для более старых она сообщает «Подпись неверна», даже если подпись правильная.
Перевыпуск секрета, отключение, удаление
- «Секрет ↺» создаёт новый секрет подписи и показывает его один раз; старый сразу перестаёт действовать, и вашему серверу нужно использовать новый
- Нажмите «Изменить» и установите статус «Отключён», чтобы приостановить доставки
- Значок корзины удаляет вебхук; пока вы не закрыли сообщение, удаление можно отменить кнопкой «Отменить»
При переходе на тариф ниже вебхуки сверх нового лимита отключаются, а в Creator Free — все. Они сохраняются; после повышения тарифа LinkHarbor автоматически снова включает отключённые вебхуки в пределах лимита нового тарифа — в том числе те, которые вы отключили сами.
Вам также может пригодиться
Ничего не нашли?
Напишите, что вам нужно, и наша служба поддержки поможет вам лично.