Webhooki skracacza URL Elido wysyłają metodą POST podpisaną kopertę JSON na Twój punkt końcowy HTTPS za każdym razem, gdy coś zmienia się w workspace: link zostaje utworzony, edytowany, usunięty, wygasa lub osiąga swój limit kliknięć, członek zostaje zaproszony, domena zostaje zweryfikowana. Każde żądanie niesie nagłówek X-Webhook-Signature: v1=<hex>, który jest HMAC-SHA256 liczonym po {timestamp}.{raw_body}, a nieudane dostarczenie otrzymuje trzy próby w ciągu około dwudziestu minut.
To, czego nie wysyła dzisiaj, to webhook kliknięcia linku. Kliknięcia wychodzą zamiast tego przez API analityczne i przekaźniki zdarzeń, a pokażę gdzie na końcu. Ten wpis to wychodząca połowa powierzchni API; szybki start API + SDK skracacza URL opisuje połowę przychodzącą, a smart links wyjaśnione to fundament funkcji, z którego pochodzą zdarzenia linków.
Które zdarzenia linków uruchamiają webhook dzisiaj
Każde poniższe zdarzenie trafia do punktu końcowego webhooka, który zasubskrybował je po nazwie. Formularz nowego punktu końcowego w panelu oferuje checkboxy dla ośmiu najczęstszych; API przyjmuje dowolną nazwę z listy.
| Zdarzenie | Kiedy się uruchamia | Checkbox w panelu |
|---|---|---|
link.created | Link zostaje utworzony, pojedynczo lub w imporcie zbiorczym | tak |
link.updated | Zmienia się cel, ustawienia lub status, edycje zbiorcze, przywrócenia | tak |
link.deleted | Link zostaje usunięty, pojedynczo lub zbiorczo | tak |
link.expired | Link mija swoją datę wygaśnięcia | tylko API |
link.cap_reached | Link osiąga maksymalną liczbę kliknięć | tylko API |
link.broken | Sprawdzenie uszkodzonych linków wykrywa niedziałający cel | tylko API |
workspace.created, workspace.updated | Workspace zostaje utworzony lub zmieniają się jego ustawienia | tak |
member.invited, member.removed | Członek zostaje dodany (bezpośrednio, przez SCIM lub zaakceptowane zaproszenie) lub usunięty | tak |
member.role_changed | Zmienia się rola członka | tylko API |
invitation.created, invitation.accepted | Zaproszenie zostaje wysłane lub zaakceptowane | tylko API |
domain.verified, domain.ssl_failed | Domena niestandardowa przechodzi kontrole DNS albo nadal ich nie przechodzi po 24 godzinach | tylko API |
audit.event | Dowolny wpis w dzienniku audytu | tak |
Zaszyfrowane linki dodają jeszcze dwa, link.encrypted_created i link.encryption_rotated. Jeśli wolisz nie utrzymywać tej listy ręcznie, utwórz punkt końcowy typu siem: otrzymuje on każde zdarzenie w workspace, wraz z wpisami audytu, bez żadnego filtra subskrypcji.
Na mapie drogowej, ale jeszcze nie na żywo: click.created (próbkowany strumień kliknięć), zdarzenia rozliczeniowe takie jak billing.subscription_upgraded oraz filtry per punkt końcowy w rodzaju "tylko linki w folderze X". Nie subskrybuj dziś tych nazw, nic ich jeszcze nie publikuje.
Tworzenie punktu końcowego webhooka przez API
Punkt końcowy należy do jednego workspace. Tworzysz go żądaniem POST do /v1/workspaces/{workspace_id}/webhooks:
curl -X POST "https://api.elido.app/v1/workspaces/$WORKSPACE_ID/webhooks" \
-H "Authorization: Bearer elido_..." \
-H "Content-Type: application/json" \
-d '{
"url": "https://hooks.example.com/elido",
"events": ["link.created", "link.updated", "link.deleted"],
"description": "CRM sync",
"kind": "event"
}'
Odpowiedzią jest 201 wraz z punktem końcowym oraz, dla rodzajów event i siem, jednorazowym sekretem:
{
"endpoint": {
"id": 7,
"workspace_id": 42,
"url": "https://hooks.example.com/elido",
"events": ["link.created", "link.updated", "link.deleted"],
"is_active": true,
"description": "CRM sync",
"kind": "event",
"config": {},
"created_at": "2026-09-21T09:12:44Z"
},
"secret": "whsec_9f2c..."
}
Elido generuje sekret; nie wysyłasz go samodzielnie. Skopiuj go teraz, bo żadne kolejne wywołanie go nie zwróci. kind przyjmuje pięć wartości. event i siem to podpisane dostarczenia JSON opisane w tym wpisie. discord, telegram i sentry przekształcają te same zdarzenia w wiadomość czatu lub zdarzenie Sentry, uwierzytelniają się przez URL lub zaszyfrowany token bota i nie niosą żadnych nagłówków HMAC.
Uprawnienia zmieniły się w tym miesiącu. Odczyt punktów końcowych i dziennika dostarczeń wymaga workspace.view. Tworzenie, edycja, usuwanie, rotacja sekretu lub ponowna wysyłka dostarczenia wymaga workspace.edit, czyli roli administratora lub właściciela. Klucz API działa wewnątrz workspace, dla którego został wydany, i nigdy powyżej roli wybranej w momencie jego utworzenia, więc klucz na poziomie podglądu otrzyma 403 przy powyższym POST. Pełne odniesienie: dokumentacja webhooków.
Koperta ładunku webhooka
Każde podpisane dostarczenie ma tę samą czteropolową kopertę. data zawiera rekord, który się zmienił, więc dla zdarzeń linków jest to wiersz linku:
{
"type": "link.created",
"workspace_id": 42,
"data": {
"id": 91834,
"workspace_id": 42,
"domain_id": 3,
"slug": "spring-sale",
"destination_url": "https://shop.example.com/spring",
"title": "Spring sale landing",
"tags": ["newsletter"],
"status": "active",
"expires_at": null,
"max_clicks": null,
"redirect_status": 302,
"created_by_user_id": 17,
"created_at": "2026-09-21T09:14:02.184311Z"
},
"timestamp": "2026-09-21T09:14:02Z"
}
Ten przykład jest przycięty; prawdziwe data niesie każdą kolumnę linku, wraz z regułami targetowania, folderem, kampanią i polami skanowania. Nie ma tu identyfikatora zdarzenia ani short_url w ciele, więc jeśli go potrzebujesz, zbuduj krótki URL z własnej domeny i slug. Zdarzenia zaplanowane wysyłają mniejszy obiekt: link.expired ma link_id, slug i destination_url, a link.cap_reached dodaje cap i clicks.
Jedna poprawka, o której warto wiedzieć, jeśli logowałeś ładunki przed tym tygodniem: pola z sekretami są teraz usuwane, zanim ładunek opuści Elido. password_hash linku chronionego hasłem i token zaproszenia kiedyś pojawiały się w data; teraz już nie, ani w nowych dostarczeniach, ani w dzienniku dostarczeń. Jeśli przechowujesz stare ładunki, warto je wyczyścić.
Weryfikacja nagłówka X-Webhook-Signature
Każde podpisane żądanie niesie te nagłówki:
X-Webhook-Signature: v1=5d8f0c3e...
X-Elido-Signature: v1=5d8f0c3e...
X-Webhook-Timestamp: 1790068442
X-Webhook-Event: link.created
X-Webhook-Delivery: 55120
User-Agent: Elido-Webhooks/1.0
Oba nagłówki sygnatury niosą tę samą wartość. Elido oblicza HMAC-SHA256, zgodnie z RFC 2104, z całym ciągiem whsec_... jako kluczem, po znaczniku czasu, kropce i surowych bajtach ciała. Skrót hex otrzymuje prefiks v1=. Wewnątrz nagłówka nie ma pola t=; znacznik czasu znajduje się we własnym nagłówku.
W Node podpisuj surowe bajty, nie ponownie zserializowany obiekt. Express z tego powodu potrzebuje na tej trasie express.raw({ type: "application/json" }):
import { createHmac, timingSafeEqual } from "node:crypto";
export function verifyElido(secret, headers, rawBody, toleranceSec = 300) {
const ts = headers["x-webhook-timestamp"] ?? "";
if (!/^\d+$/.test(ts)) return false;
if (Math.abs(Date.now() / 1000 - Number(ts)) > toleranceSec) return false;
const mac = createHmac("sha256", secret).update(`${ts}.`).update(rawBody);
const expected = Buffer.from("v1=" + mac.digest("hex"));
// During a rotation the old secret signs X-Elido-Signature-Previous.
return ["x-elido-signature", "x-elido-signature-previous"].some((name) => {
const got = Buffer.from(headers[name] ?? "");
return got.length === expected.length && timingSafeEqual(got, expected);
});
}
Sprawdzenie długości ma znaczenie: timingSafeEqual w Node rzuca wyjątek dla buforów o różnych rozmiarach zamiast zwracać false. Wersja w Pythonie ze standardowym modułem hmac:
import hashlib
import hmac
import time
def verify_elido(secret: str, headers, raw_body: bytes, tolerance: int = 300) -> bool:
ts = headers.get("X-Webhook-Timestamp", "")
if not ts.isdigit() or abs(time.time() - int(ts)) > tolerance:
return False
digest = hmac.new(secret.encode(), ts.encode() + b"." + raw_body, hashlib.sha256)
expected = "v1=" + digest.hexdigest()
for name in ("X-Elido-Signature", "X-Elido-Signature-Previous"):
got = headers.get(name)
if got and hmac.compare_digest(got, expected):
return True
return False
Jeśli weryfikacja nadal kończy się niepowodzeniem, poradnik weryfikacji sygnatur webhooków zawiera wersję w Go, węzeł Code w n8n oraz typowe przyczyny niezgodności. Pięciominutowe okno to Twoja kontrola, nie nasza. Elido stempluje świeży znacznik czasu przy każdej próbie, w tym ponowieniach, więc uzasadnione ponowienie nigdy nie wygląda na nieaktualne. Bez tego okna każdy, kto przechwycił jedno żądanie, mógłby powtórzyć je za tydzień, a sygnatura wciąż by pasowała.
Rotacja to POST /v1/workspaces/{workspace_id}/webhooks/{id}/rotate-secret albo przycisk Rotate na stronie punktu końcowego. Nowy sekret otrzymujesz raz. Przez kolejne siedem dni każde dostarczenie niesie też X-Elido-Signature-Previous, podpisaną starym sekretem, dlatego obie powyższe funkcje ją próbują. Wdróż nowy sekret w dowolnym momencie tego tygodnia i nic nie zawiedzie.
Polityka ponowień webhooka
Worker dostarczeń odbiera oczekujące dostarczenia co kilka sekund, więc zmiana linku zwykle dociera do Ciebie w ciągu sekund. Dowolna odpowiedź 2xx oznacza dostarczenie jako ukończone. Status inny niż 2xx, błąd sieci lub brak odpowiedzi w ciągu 10 sekund liczy się jako nieudana próba.
Trzy próby na dostarczenie, dwadzieścia minut od początku do końca. To celowo krótko i szczerze mówiąc krócej, niż wybrałbym dla odbiorcy za niestabilnym VPN-em. Dwugodzinna awaria po Twojej stronie nie zostanie pokryta automatycznymi ponowieniami. To, co ją pokrywa, to dziennik dostarczeń: GET /v1/workspaces/{workspace_id}/webhooks/{id}/deliveries wylicza każde dostarczenie ze statusem, kodem HTTP, opóźnieniem, liczbą prób i czasem następnej próby, a strona punktu końcowego pokazuje te same wiersze z przyciskiem Retry. Retry, albo POST .../deliveries/{delivery_id}/retry, uzbraja od nowa nieudane lub dostarczone dostarczenie ze świeżym budżetem trzech prób i zwraca 202. Dostarczenie wciąż oczekujące otrzymuje 409.
Niektóre niepowodzenia pomijają ponowienia. Punkt końcowy Telegram bez chat_id albo źle sformułowany DSN Sentry zostaje od razu oznaczony jako nieudany, bo powtórzenie żądania nie naprawi konfiguracji. Aby wyłączyć punkt końcowy bez jego usuwania, wyślij PUT z "is_active": false; wstrzymane punkty końcowe nie otrzymują nowych dostarczeń.
Jeśli Twój handler wykonuje ciężką pracę, zwróć najpierw 200 i zakolejkuj zadanie. Dziesięciosekundowy limit to miejsce, w którym wolny, ale udany handler zamienia się w duplikat, co prowadzi nas do deduplikacji.
Planujesz odbiornik dla swojego zespołu? Strona funkcji webhooków pokazuje to wszystko od strony panelu.
Idempotencja i kolejność webhooków
Elido dostarcza co najmniej raz. Sekcja o ponowieniach pokazała jeden sposób, w jaki powstaje duplikat: Twój handler zatwierdza pracę, potem nie mieści się w dziesięciosekundowym oknie, a Elido wysyła go ponownie. Ręczne Retry wysyła ponownie celowo.
Oba przypadki zachowują tę samą wartość X-Webhook-Delivery, bo jest to identyfikator jednego dostarczenia do jednego punktu końcowego, a nie jednej próby. Klucz się na niej:
CREATE TABLE elido_webhook_seen (
delivery_id BIGINT PRIMARY KEY,
received_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
-- in the handler, inside the same transaction as your work:
INSERT INTO elido_webhook_seen (delivery_id) VALUES ($1)
ON CONFLICT (delivery_id) DO NOTHING
RETURNING delivery_id;
-- no row back means you've already handled this delivery
Dwa punkty końcowe zasubskrybowane do tego samego zdarzenia otrzymują dwa różne identyfikatory dostarczenia, więc deduplikuj per punkt końcowy. Przy rzadkich restartach po naszej stronie zdarzenie może zostać zakolejkowane dwukrotnie jako osobne dostarczenia; jeśli podwójny zapis by zaszkodził, dodaj drugie zabezpieczenie na type plus data.id plus data.updated_at.
Nie ma tu żadnej gwarancji kolejności. Dostarczenia wychodzą od najstarszych zaległych, ale ponowienie wcześniejszego zdarzenia może dotrzeć po późniejszym. Porównaj data.updated_at z tym, co już zapisałeś, zanim nadpiszesz link, i nie polegaj na timestamp koperty przy ustalaniu kolejności: ma on precyzję tylko do jednej sekundy.
Gdzie znajdują się dane kliknięć zamiast webhooka kliknięcia
To jest część, którą starsza wersja tego wpisu podawała błędnie. Nie ma dziś webhooka click, a click.created jest zaplanowany, ale niewdrożony. Ścieżka przekierowania jest utrzymywana wolna od pracy synchronicznej, co wyjaśnia wpis pozyskiwanie kliknięć w trybie fire-and-forget, a kliknięcia trafiają do magazynu analitycznego, a nie do kolejki webhooków.
Na dane na poziomie kliknięcia masz dziś dwie drogi:
- Przekaźniki zdarzeń. Każde kliknięcie krótkiego linku staje się zdarzeniem po stronie serwera w narzędziu, z którego już korzystasz: zdarzenia kliknięć linków w Mixpanel, zdarzenia kliknięć Klaviyo w profilach albo metryki przekierowań Datadog dla dashboardów operacyjnych.
- API analityczne.
GET /v1/analytics/workspaces/{workspace_id}/clicks/recentzwraca ostatnie kliknięcia, aclicks.csvje eksportuje, więc zaplanowane zadanie może pobrać potrzebne wiersze.
To, co pasuje, zależy od opóźnienia i miejsca, w którym dane mają wylądować; webhooki kontra odpytywanie dla śledzenia kliknięć przeprowadza przez ten kompromis. A jeśli próbkowany strumień click.created zostanie wdrożony, ta strona powie o tym pierwsza.
Przeczytaj fundament: smart links wyjaśnione.
Powiązane na blogu
- Webhooki kontra odpytywanie dla śledzenia kliknięć - kiedy wypychać, a kiedy pobierać.
- Szybki start API + SDK skracacza URL - przychodząca powierzchnia API.
- Pozyskiwanie kliknięć w trybie fire-and-forget - dlaczego kliknięcia nigdy nie czekają na wywołania wychodzące.
- Zdarzenia kliknięć linków w Mixpanel - dane kliknięć jako zdarzenia po stronie serwera.
- Metryki przekierowań linków Datadog - kondycja przekierowań na dashboardzie operacyjnym.
- Weryfikacja sygnatur webhooków - sprawdzanie HMAC w Node, Pythonie, Go i n8n oraz diagnozowanie niezgodności.
Najczęściej zadawane pytania
Czy Elido wysyła webhook dla każdego kliknięcia linku?
Nie dzisiaj. Webhooki obejmują zmiany w workspace, takie jak link.created, link.updated, link.expired i member.invited. Zdarzenie click.created jest na mapie drogowej jako próbkowany strumień. Na dane na poziomie kliknięcia już teraz skorzystaj z API analitycznego lub przekaźnika zdarzeń, takiego jak Mixpanel, Klaviyo czy Datadog.
Jak zweryfikować sygnaturę webhooka Elido?
Oblicz HMAC-SHA256 na wartości X-Webhook-Timestamp, kropce i surowym ciele żądania, kluczowany Twoim sekretem whsec_. Zakoduj to w hex, dodaj prefiks v1= i porównaj w czasie stałym z nagłówkiem X-Webhook-Signature. Odrzucaj znaczniki czasu starsze niż pięć minut.
Ile razy Elido ponawia nieudany webhook?
Każde dostarczenie ma trzy próby: jedną od razu, jedną pięć minut po niepowodzeniu i jedną piętnaście minut po tamtej. Każdy status inny niż 2xx, błąd sieci lub odpowiedź wolniejsza niż dziesięć sekund liczy się jako niepowodzenie. Po trzeciej próbie dostarczenie zostaje oznaczone jako nieudane, dopóki nie naciśniesz Retry.
Jaka jest różnica między X-Webhook-Signature a X-Elido-Signature?
Żadna poza nazwą. Oba nagłówki niosą tę samą sygnaturę v1=, a X-Webhook-Signature pozostaje ze względu na starsze odbiorniki. Podczas rotacji sekretu Elido wysyła też X-Elido-Signature-Previous, podpisaną starym sekretem, przez siedem dni.
Jak przestać przetwarzać ten sam webhook dwa razy?
Zapisz wartość nagłówka X-Webhook-Delivery i pomiń każde żądanie, którego wartość już obsłużyłeś. Identyfikuje ona jedno dostarczenie do jednego punktu końcowego i pozostaje taka sama przy automatycznych ponowieniach i ręcznych wysyłkach, więc unikalny indeks na niej wystarczy.
Kto może tworzyć lub usuwać webhooki w workspace?
Administratorzy i właściciele workspace. Wyświetlanie punktów końcowych i odczyt dziennika dostarczeń wymaga dostępu do podglądu; tworzenie, edycja, usuwanie, rotacja sekretu lub ponowna wysyłka dostarczenia wymaga uprawnienia workspace.edit. Klucze API są ograniczone do roli wybranej w momencie utworzenia klucza.
Wypróbuj Elido
Wklej URL, otrzymaj krótki link
Bez rejestracji. Link działa 30 dni. Zarejestruj się, aby zachować go na zawsze.
Za darmo, bez rejestracji · 2 dziennie