Skonfiguruj webhooki w panelu, odbieraj dostarczenia i weryfikuj podpis HMAC – z przykładami dla curl, Node.js i PHP.
Webhook wysyła żądanie HTTP do Twojego serwera, gdy tylko coś się wydarzy w Twoim workspace – na przykład gdy strona zostanie opublikowana. Dzięki temu nie musisz ciągle sprawdzać, czy coś się zmieniło. Każde dostarczenie jest podpisane, aby Twój serwer mógł odróżnić prawdziwe wiadomości od podrobionych.
Wymagania
- Rola właściciela lub admina w workspace – pozycja menu „Webhooki” pojawia się tylko dla tych ról
- Od planu Creator Pro; w Creator Free widać komunikat „Webhooki są dostępne od planu Creator Pro.”
- Do 5 webhooków w Creator Pro, 25 w Creator Agency, 100 w Enterprise (można dostosować)
- Serwer dostępny pod publicznym adresem https
Webhookami zarządzasz tylko w panelu. Nie można ich tworzyć ani zmieniać za pomocą kluczy API.
Tworzenie webhooka
- Otwórz „Webhooki” w nawigacji
- Kliknij „+ Nowy webhook”
- Wpisz docelowy URL i wybierz zdarzenia
- Opcjonalnie: wpisz opis i wybierz status
- Kliknij „Utwórz webhook”
- W kroku „Gotowe” skopiuj sekret podpisu („Sekret podpisu – widoczny tylko teraz”) i zapisz go na swoim serwerze – jest pokazywany tylko w tym momencie
- Przetestuj webhook przyciskiem „Test”. Wynik zostaje na karcie do następnego testu: „Dostarczono” albo „Nie dostarczono” z kodem HTTP (lub „brak odpowiedzi”), czasem trwania i początkiem odpowiedzi
- „Docelowy URL (https://…)”
- Do 2048 znaków. Tylko https, bez danych logowania w URL, bez adresów localhost i bez nazw wewnętrznych, takich jak .local, .internal czy .lan. Nazwa musi wskazywać na publiczne adresy IP; LinkHarbor sprawdza to ponownie przed każdym dostarczeniem.
- „Zdarzenia”
- Co najmniej jedno zdarzenie. Każda grupa ma opcje „Zaznacz wszystko” i „Odznacz wszystko”.
- „Opis”
- Opcjonalny, do 255 znaków – na przykład do czego służy webhook.
- „Status”
- „Aktywny” lub „Wyłączony”. Wyłączone webhooki nie otrzymują dostarczeń.
Zdarzenia
| Zdarzenie | W panelu | Kiedy | Zawartość data |
|---|---|---|---|
| page.published | Strona opublikowana | Strona została opublikowana – w panelu, przez API lub przez asystenta AI | page_id, version, checksum, published_by_user_id (null dla kluczy API i asystentów AI) |
| form.submitted | Formularz wysłany | Odwiedzający wysłał formularz na jednej z Twoich stron | form_id, form_name, submission_id, page_id, block_id, submitted_at, values (klucz pola → wartość), consent (klucz zgody → true/false) |
| domain.verified | Domena zweryfikowana | Własna domena została pomyślnie zweryfikowana | domain_id, hostname, verification_method, status |
| domain.disabled | Domena wyłączona | Własna domena została wyłączona w panelu (nie przy przejściu na niższy plan) | domain_id, hostname, status |
| webhook.test | – (przycisk „Test”) | Klikasz „Test” | message (informacja w języku konta osoby, która uruchamia test; nie porównuj z nią w kodzie) |
form.submitted zawiera wartości dokładnie tak, jak wysłał je odwiedzający, ale nigdy jego adresu IP ani przeglądarki. Dostarczenie odbywa się w tle: odwiedzający na nie nie czeka, a nieudane dostarczenie nie blokuje zgłoszenia.
Budowa dostarczenia
LinkHarbor wysyła każde dostarczenie jako POST z treścią JSON i tymi nagłówkami:
| Nagłówek | Zawartość |
|---|---|
| Content-Type | application/json |
| X-LinkHarbor-Event | Nazwa zdarzenia, na przykład page.published |
| X-LinkHarbor-Delivery-Id | Unikalne ID tego dostarczenia (UUID), identyczne z id w treści |
| X-LinkHarbor-Timestamp | Czas wysłania w sekundach Unix |
| X-LinkHarbor-Signature | sha256=, a po nim podpis HMAC-SHA256 w 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
}
} Ponowne dostarczenia dostają nowe id oraz pola redelivery_of (ID powtarzanego dostarczenia) i delivery_root (ID pierwszego dostarczenia w tym łańcuchu).
Weryfikacja podpisu
Każdy webhook ma własny sekret podpisu (32 znaki hex). Widzisz go dokładnie raz: w ostatnim kroku tworzenia webhooka i zaraz po „Sekret ↺”. Od razu skopiuj go na swój serwer – LinkHarbor nie pokaże go później ponownie. LinkHarbor oblicza z jego pomocą podpis w ten sposób:
- Budowanie wiadomości: znacznik czasu, kropka, ID dostarczenia, kropka, surowa treść – czyli timestamp.delivery_id.body
- Obliczenie HMAC-SHA256 tej wiadomości z sekretem podpisu jako kluczem
- Wysłanie wyniku jako ciągu hex z prefiksem sha256=
Twój serwer powinien sprawdzać podpis porównaniem w stałym czasie, odrzucać dostarczenia ze znacznikiem czasu starszym niż 300 sekund i przechowywać ID dostarczeń co najmniej przez ten czas, aby rozpoznawać powtórzenia. Ważne: podpisywana jest surowa treść, dokładnie taka, jaka przychodzi – a nie JSON zakodowany przez Ciebie ponownie.
import crypto from 'node:crypto';
import express from 'express';
const app = express();
const SECRET = process.env.LH_WEBHOOK_SECRET;
const seen = new Set(); // w produkcji: baza danych lub Redis z wygasaniem
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) // surowa treść jako 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; // rozpoznawanie ponownych dostarczeń z tego samego łańcucha
if (!seen.has(key)) {
seen.add(key);
// tutaj przetwarzanie, najlepiej asynchronicznie w kolejce
}
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);
// Rozpoznaj zduplikowane dostarczenia po $event['delivery_root'] ?? $event['id'],
// a potem przetwórz.
http_response_code(200); Testowanie endpointu przez curl
Ten skrypt wysyła poprawnie podpisane dostarczenie testowe do Twojego serwera – przydatne, zanim utworzysz webhook w 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":"To jest zdarzenie testowe dla tego webhooka."}}'
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" Odpowiedź, limit czasu i ponowne dostarczenie
- Każda odpowiedź ze statusem 2xx liczy się jako sukces; przekierowania (3xx) nie są wykonywane
- Twój serwer ma 10 sekund na odpowiedź i najwyżej 5 sekund na nawiązanie połączenia
- Wszystkie webhooki dla jednego zdarzenia mają łącznie 25 sekund; te, na które do tego czasu nie przyszła kolej, są oznaczane jako nieudane i można je dostarczyć ponownie
- LinkHarbor nie ponawia nieudanych dostarczeń automatycznie
- Nieudane dostarczenia możesz wysłać ponownie ręcznie, ale nie takie, które w międzyczasie się powiodło. Ponowne dostarczenie ma nowe id i nowe created_at
- Na jedno zdarzenie dozwolone są najwyżej 3 próby, wliczając pierwsze dostarczenie; potem trafia ono do zakładki „Dead letters” i nie można go już wysłać ponownie
Dostarczenia w panelu
„Dostarczenia” otwiera dziennik webhooka. Ma on trzy zakładki:
- „Wszystkie dostarczenia”
- Wszystkie dostarczenia z filtrami zdarzenia, statusu („Sukces (2xx)”, „Błąd klienta (4xx)”, „Błąd serwera (5xx)”, „Nieudane”) i „Limit”. „↺ Ponów” dostarcza nieudane dostarczenie ponownie, o ile nie ma późniejszego udanego. Kliknięcie wiersza pokazuje żądanie, odpowiedź, treść i surowy JSON.
- „Powtórki”
- Łańcuchy ponownych dostarczeń z wynikiem „Naprawione” lub „Nadal nie działa” i liczbą prób.
- „Dead letters”
- Dostarczenia, w których nie powiodły się wszystkie dozwolone próby – pierwsze dostarczenie i ponowne razem. Zakładka służy tylko do podglądu: tych dostarczeń nie można już wysłać ponownie. Sprawdź swój endpoint; potem nowe zdarzenia są dostarczane normalnie. Jeśli potrzebujesz tych danych, pobierz je przez API.
Jeśli ponowne dostarczenie się nie uda, panel podaje powód:
- „Nie dostarczono ponownie: to zdarzenie nie powiodło się już … razy, kolejne próby nie są możliwe. Znajdziesz je w zakładce «Dead letters».”
- Wykorzystano wszystkie 3 próby.
- „Nie dostarczono ponownie: to zdarzenie zostało w międzyczasie dostarczone.”
- Późniejsze dostarczenie w tym samym łańcuchu już się powiodło.
Weryfikacja w panelu
W sekcji „Weryfikacja” znajdziesz dwie pomoce:
- „Zestaw weryfikacyjny”
- „Załaduj zestaw” pokazuje algorytm, nazwy nagłówków, format podpisywanej wiadomości i zalecaną tolerancję 300 sekund. Sam sekret nie jest w nim zawarty.
- „Zweryfikuj podpis”
- Wpisz ID dostarczenia, znacznik czasu, podpis i surową treść żądania otrzymanego dostarczenia. LinkHarbor odpowie wtedy „Podpis prawidłowy” lub „Podpis nieprawidłowy”. Znacznik czasu i podpis skopiuj z nagłówków X-LinkHarbor-Timestamp i X-LinkHarbor-Signature. Sprawdzenie działa tylko dla dostarczeń z ostatnich 5 minut; dla starszych zgłasza „Podpis nieprawidłowy”, nawet jeśli podpis jest poprawny.
Zmiana sekretu, wyłączanie, usuwanie
- „Sekret ↺” generuje nowy sekret podpisu i pokazuje go raz; stary od razu przestaje działać, a Twój serwer musi używać nowego
- W „Edytuj” ustaw status „Wyłączony”, aby wstrzymać dostarczenia
- Ikona kosza usuwa webhook; dopóki nie zamkniesz komunikatu, możesz to cofnąć przyciskiem „Cofnij”
Gdy przechodzisz na mniejszy plan, webhooki ponad nowy limit zostają wyłączone, a w Creator Free wszystkie. Pozostają jednak zachowane; po zmianie planu na wyższy LinkHarbor automatycznie włącza wyłączone webhooki do limitu nowego planu – także te, które wyłączono ręcznie.
Może Ci się też przydać
Nic nie znaleziono?
Napisz, czego potrzebujesz, a nasz zespół pomocy technicznej pomoże Ci osobiście.