Webhooki
Otrzymuj podpisane powiadomienia o zdarzeniach, gdy dokumenty są indeksowane, dostawy nie powiodą się lub punkty końcowe zmieniają stan. Weryfikacja, ponawianie prób i testowanie.
Webhooki wysyłają powiadomienia o zdarzeniach do twoich systemów jako żądania HTTPS POST, dzięki czemu możesz reagować na zmiany bez konieczności odpytywania. Zarejestruj punkt końcowy w ustawieniach przestrzeni roboczej: adres URL musi używać HTTPS, a każda przestrzeń robocza może zarejestrować do 10 punktów końcowych. Przy tworzeniu otrzymujesz tajny klucz podpisywania (z prefiksem whsec_, zgodny ze Standard Webhooks) dokładnie raz; nie można go później odzyskać, więc od razu skopiuj go do swojego magazynu sekretów.
Każde dostarczenie to treść JSON z tą samą kopertą:
{
"event": "documents.indexed",
"timestamp": "2026-07-23T09:14:07.000Z",
"tenantId": "0198f2a4-6c1e-7d30-b6a1-2f9d54c08a11",
"sequence": 42,
"data": { "count": 12 }
}Przesyłki są celowo lekkie: tylko identyfikatory, liczniki i znaczniki czasu. Traktuj webhook jako sygnał, że coś się zmieniło, a następnie pobierz aktualny stan przez uwierzytelnione API. Nazwy dokumentów i treść nigdy nie pojawiają się w przesyłce webhooka.
sequence to licznik per punkt końcowy, gęsty dla strumienia tego punktu końcowego: kolejne zdarzenia dostarczane do tego punktu końcowego mają kolejne numery, więc brakujący numer oznacza dostarczenie, którego twoja usługa nigdy nie otrzymała. Kolejność dostarczania NIE jest gwarantowana, więc używaj sekwencji (a nie kolejności przybycia) do sortowania zdarzeń. Dwa zastrzeżenia: przesyłki powtórzone docierają celowo poza kolejnością (zdarzenie nadrobione zachowuje swój oryginalny, starszy numer sekwencji), więc deduplikuj na podstawie webhook-id i nigdy nie odrzucaj dostarczenia tylko dlatego, że jego sekwencja jest poniżej twojego wysokiego poziomu wodnego; a przerwa może również oznaczać, że brakujące zdarzenie wciąż próbuje ponownie lub czeka w stanie możliwym do powtórzenia, więc traktuj przerwy jako "sprawdź dziennik dostarczania", a nie dowód utraty.
Katalog zdarzeń
Zasubskrybuj punkt końcowy do dowolnej kombinacji typów zdarzeń poniżej. Typy oznaczone jako planowane można już wybierać, ale nic dla nich jeszcze nie jest wysyłane, a kształt ich przesyłki nie jest ostateczny; zostanie opublikowany tutaj, gdy zdarzenie wejdzie w życie.
| Zdarzenie | Status | Przesyłka (data) |
|---|---|---|
documents.indexed | Na żywo | Dokumenty stały się przeszukiwalne. Zagregowane na przestrzeń roboczą: maksymalnie jedno zdarzenie na okno agregacji, zawierające licznik. |
documents.failed | Na żywo | Dokumenty ostatecznie nie powiodły się w przetwarzaniu. Zagregowane na przestrzeń roboczą: maksymalnie jedno zdarzenie na okno agregacji, zawierające licznik. |
sync.completed | Planowane | Ukończono synchronizację konektora. Kształt przesyłki nie jest ostateczny. |
sync.failed | Planowane | Synchronizacja konektora nie powiodła się. Kształt przesyłki nie jest ostateczny. |
ingestion.completed | Planowane | Ukończono uruchomienie ingestii. Kształt przesyłki nie jest ostateczny. |
ingestion.failed | Planowane | Uruchomienie ingestii nie powiodło się. Kształt przesyłki nie jest ostateczny. |
audit.recorded | Na żywo | Ślad audytu przestrzeni roboczej zyskał nowe wpisy. Zagregowane na przestrzeń roboczą w oknie: licznik i okno do pobrania, nigdy same wpisy. |
endpoint.disabled | Na żywo | Punkt końcowy webhooka został wyłączony: automatycznie po długotrwałych niepowodzeniach dostarczania lub 410 Gone, lub przez administratora. Dostarczane do innych punktów końcowych przestrzeni roboczej. |
delivery.failing | Na żywo | Dostarczanie do punktu końcowego webhooka nie powiodło się: zgłaszane raz na okno niepowodzeń, 30 minut po pierwszej nieudanej próbie, i dostarczane do innych punktów końcowych przestrzeni roboczej. |
endpoint.enabled | Na żywo | Wyłączony punkt końcowy webhooka został ponownie włączony. Zamyka incydent otwarty przez endpoint.disabled; dostarczane do innych punktów końcowych przestrzeni roboczej. |
delivery.recovered | Na żywo | Dostarczenie powiodło się na punkcie końcowym, dla którego zgłoszono delivery.failing. Zamyka ten incydent; dostarczane do innych punktów końcowych przestrzeni roboczej. |
webhook.ping | Na żywo | Ukierunkowane zdarzenie testowe wysłane przez akcję wyślij-test pulpitu na dokładnie jeden punkt końcowy. Nigdy nie można go zasubskrybować; data jest zawsze pusty. |
Przykładowe obiekty data dla zdarzeń na żywo:
// documents.indexed
{ "count": 12 }
// documents.failed
{ "count": 2 }
// audit.recorded
{
"count": 34,
"windowStart": "2026-07-23T09:05:00.000Z",
"windowEnd": "2026-07-23T09:10:00.000Z"
}
// endpoint.disabled
{
"endpointId": "0198f2a4-6c1e-7d30-b6a1-2f9d54c08a11",
"reason": "sustained_failure",
"disabledAt": "2026-07-23T09:14:07.000Z"
}
// delivery.failing
{
"endpointId": "0198f2a4-6c1e-7d30-b6a1-2f9d54c08a11",
"failingSince": "2026-07-23T09:14:07.000Z"
}
// endpoint.enabled
{
"endpointId": "0198f2a4-6c1e-7d30-b6a1-2f9d54c08a11",
"enabledAt": "2026-07-24T08:02:51.000Z"
}
// delivery.recovered
{
"endpointId": "0198f2a4-6c1e-7d30-b6a1-2f9d54c08a11",
"failingSince": "2026-07-23T09:14:07.000Z",
"recoveredAt": "2026-07-23T11:40:12.000Z"
}
// webhook.ping
{}Zdarzenia operacyjne (delivery.failing, delivery.recovered,
endpoint.disabled, endpoint.enabled) raportują stan innych punktów końcowych: punkt końcowy, którego dotyczy zdarzenie, jest zawsze wykluczony z dostarczania tego zdarzenia, ponieważ dostarczanie tam byłoby gwarantowanym szumem.
Każde zdarzenie na żywo jest również opisane w dokumencie OpenAPI pod adresem
/api/openapi.json, w sekcji webhooks: pełny schemat JSON ciała i trzech nagłówków podpisu, dzięki czemu możesz generować typy odbiorcy zamiast kopiować powyższe przykłady. Schemat jest również egzekwowany po naszej stronie: przesyłka, która do niego nie pasuje, nigdy nie jest wysyłana.
Dostarczanie śladu audytu do SIEM
audit.recorded to część push kanału SIEM i celowo nie są to same wpisy: wpis audytu zawiera nazwę osoby, jej adres i to, czego dotknęła, a przesyłka webhooka jest przechowywana w dzienniku dostarczania i wysyłana na adres URL, który kontrolujesz, co może rozwiązywać się gdziekolwiek. Zdarzenie informuje cię, że zamknięto okno z wpisami; pobierasz wpisy przez uwierzytelnione API za pomocą klucza z zakresem audit:read.
Na każde zdarzenie przeszukaj ślad za pomocą windowStart jako startDate i
windowEnd jako endDate, stronicując po kursorze, aż zwróci null. Dolna granica wyszukiwania jest włączająca, a okna nie, więc strona może powtórzyć wpis dokładnie z tego momentu; deduplikuj na podstawie identyfikatora wpisu, którego i tak potrzebujesz, ponieważ dostarczanie jest co najmniej jednokrotne.
Punkt końcowy katalogu wymienia każdą akcję, jaką ślad może zapisać, z kategorią, do której należy, oraz wersją, która zmienia się, gdy zmienia się słownictwo. Przeczytaj go raz, porównaj wersję przy kolejnych odczytach i traktuj akcję nieobecną w nim jako nieznaną, a nie nieprawidłową: ślad jest tylko do dopisywania i zachowuje pisownię, z jaką każdy wpis został zapisany.
Okna są ciągłe, dopóki pozostajesz zasubskrybowany: każde z nich zaczyna się tam, gdzie skończyło się ostatnie zdarzenie, które ci wysłano, więc opóźnione przejście poszerza następne okno zamiast tracić to, co wydarzyło się w międzyczasie. Okno bez wpisów nic nie wysyła.
Weryfikacja dostarczania
Każde dostarczenie jest podpisywane zgodnie ze specyfikacją Standard Webhooks. Zawsze weryfikuj podpis przed zaufaniem żądaniu: twój adres URL punktu końcowego jest dostępny dla każdego w internecie.
Każde żądanie zawiera trzy nagłówki:
| Nagłówek | Wartość |
|---|---|
webhook-id | Identyfikator wiadomości. Identyczny przy każdej ponownej próbie, ponownym wysłaniu i powtórzeniu tego samego zdarzenia; deduplikuj na jego podstawie. |
webhook-timestamp | Znacznik czasu Unix w sekundach w momencie wysłania. |
webhook-signature | Jedna lub więcej sygnatur, oddzielonych spacjami, każda w formie v1,{base64}. |
Aby zweryfikować:
- Odtwórz podpisaną treść jako
{webhook-id}.{webhook-timestamp}.{raw body}. Użyj surowych bajtów treści żądania dokładnie tak, jak zostały odebrane, przed jakimkolwiek parsowaniem JSON. - Oblicz HMAC-SHA256 dla tego ciągu. Kluczem jest część tajnego klucza po prefiksie
whsec_zdekodowana z base64 (wyprowadzanie klucza zgodne ze Standard Webhooks, które implementują biblioteki referencyjne). - Zakoduj HMAC do base64 i porównaj go z każdą sygnaturą
v1,w nagłówku za pomocą porównania o stałym czasie. Dostarczenie jest autentyczne, jeśli któraś z nich pasuje. - Odrzuć dostarczania, których
webhook-timestampróżni się o więcej niż 5 minut od twojego aktualnego czasu, w obu kierunkach. To ogranicza powtarzanie przechwyconych żądań.
Nagłówek może zawierać więcej niż jedną sygnaturę: podczas gdy okres karencji rotacji tajnego klucza jest otwarty, dostarczania są podpisywane zarówno nowym, jak i poprzednim tajnym kluczem (v1,NEW_SIG v1,OLD_SIG), więc weryfikacja nadal się powiedzie, niezależnie od tego, do którego tajnego klucza twoja usługa przeszła.
import { createHmac, timingSafeEqual } from "node:crypto";
const TOLERANCE_SECONDS = 5 * 60;
/**
* @param {string} secret - The whsec_ signing secret from endpoint creation.
* @param {Record<string, string>} headers - Lowercased request headers.
* @param {string} rawBody - The raw request body, unparsed.
*/
export function verifyWebhook(secret, headers, rawBody) {
const id = headers["webhook-id"];
const timestamp = headers["webhook-timestamp"];
const signatureHeader = headers["webhook-signature"];
if (!id || !timestamp || !signatureHeader) return false;
const skew = Math.abs(Math.floor(Date.now() / 1000) - Number(timestamp));
if (!Number.isFinite(skew) || skew > TOLERANCE_SECONDS) return false;
const key = Buffer.from(secret.slice("whsec_".length), "base64");
const expected = createHmac("sha256", key)
.update(`${id}.${timestamp}.${rawBody}`)
.digest();
return signatureHeader.split(" ").some((entry) => {
const comma = entry.indexOf(",");
if (comma === -1 || entry.slice(0, comma) !== "v1") return false;
const received = Buffer.from(entry.slice(comma + 1), "base64");
return received.length === expected.length && timingSafeEqual(received, expected);
});
}Ponieważ format jest zgodny ze specyfikacją Standard Webhooks, otwarte biblioteki weryfikacyjne dla innych języków działają bez zmian; przekaż im tajny klucz i trzy nagłówki.
Ponowne próby i niepowodzenia
Twój punkt końcowy ma 10 sekund na odpowiedź, wliczając w to nawiązanie połączenia. Odpowiedź ze statusem 2xx liczy się jako dostarczona; wszystko inne, w tym timeout lub przekierowanie (dostarczania nigdy nie podążają za przekierowaniami), liczy się jako niepowodzenie i jest ponawiane. Jedynym wyjątkiem jest 410 Gone: informuje nas, że trasa została celowo usunięta, więc to dostarczenie nie jest ponawiane, a punkt końcowy jest natychmiast wyłączany, z wysłaniem zdarzenia endpoint.disabled (powód gone) do innych punktów końcowych przestrzeni roboczej oraz e-mailem do każdego właściciela i administratora. 410 w odpowiedzi na Wyślij test lub ręczne ponowne wysłanie kończy się niepowodzeniem tylko tego dostarczenia.
Odpowiadaj 2xx, jak tylko trwale zaakceptujesz zdarzenie, a ciężkie przetwarzanie wykonuj asynchronicznie.
Nieudane dostarczenie jest ponawiane według tego harmonogramu:
| Próba | Opóźnienie po poprzednim niepowodzeniu |
|---|---|
| 1 | natychmiast |
| 2 | 5 sekund |
| 3 | 5 minut |
| 4 | 30 minut |
| 5 | 2 godziny |
| 6 | 5 godzin |
| 7 | 10 godzin |
| 8 | 10 godzin |
To 8 prób w ciągu około 27,5 godziny, więc całodzienna awaria po twojej stronie nie powoduje utraty zdarzeń. Każde opóźnienie ma do 20% losowego jittera w obu kierunkach, co zapobiega zsynchronizowanym falom ponownych prób przeciwko odzyskującemu punktowi końcowemu. Jeśli twój punkt końcowy zwraca nagłówek Retry-After, żądane opóźnienie jest honorowane w granicach: jest ograniczane między zaplanowanym opóźnieniem a dwukrotnością zaplanowanego opóźnienia. Odpowiedzi ze statusem 429 i próby zakończone timeoutem są ponawiane nie wcześniej niż po 60 sekundach, niezależnie od tego, jak wcześnie wypada slot harmonogramu.
Automatyczne wyłączanie
Punkt końcowy, który ciągle zawodzi, jest w końcu wyłączany, zamiast być bombardowany w nieskończoność:
- Gdy dostarczanie nie powodzi się przez 30 minut, raz uruchamia się zdarzenie
delivery.failing(dostarczane do innych punktów końcowych przestrzeni roboczej), a każdy właściciel i administrator przestrzeni roboczej otrzymuje powiadomienie w aplikacji. To wczesne ostrzeżenie; krótkotrwały problem, który zostanie rozwiązany sukcesem w ciągu tych 30 minut, nic nie wywołuje. - W miarę trwania okna niepowodzeń ostrzeżenie eskaluje: po 3 dniach i ponownie po 4,5 dniach każdy właściciel i administrator otrzymuje powiadomienie w aplikacji oraz e-mail z informacją o czasie, po którym punkt końcowy zostanie wyłączony, jeśli dostarczanie nadal będzie zawodzić. Każde ostrzeżenie dociera do każdej osoby tylko raz na okno niepowodzeń.
- Pomyślnie dostarczone zdarzenie resetuje okno niepowodzeń. Zdarzenie testowe nie: dowodzi, że punkt końcowy odpowiada, a nie że przetwarza zdarzenia. Jeśli ostrzeżenie zostało już wysłane, uruchamia się zdarzenie
delivery.recovered, a właściciele i administratorzy otrzymują powiadomienie w aplikacji, że punkt końcowy odzyskał sprawność. - Po 5 dniach ciągłych niepowodzeń punkt końcowy jest wyłączany: uruchamia się zdarzenie
endpoint.disabled(ponownie do innych punktów końcowych), a każdy właściciel i administrator przestrzeni roboczej otrzymuje e-mail.
Wyłączenie wymaga nieudanego dostarczenia po ostatnim ostrzeżeniu. Jeśli nic nie zostało wysłane do punktu końcowego do czasu, gdy ostatnie ostrzeżenie wskazało, następne niepowodzenie wywołuje świeże ostatnie ostrzeżenie, a punkt końcowy jest wyłączany tylko wtedy, gdy dostarczanie nadal zawodzi 12 godzin później.
Samodzielne wyłączenie punktu końcowego uruchamia endpoint.disabled z powodem
manual do innych punktów końcowych przestrzeni roboczej. Wyłączony punkt końcowy nie otrzymuje dalszych dostarczania, a akcje wyślij-test i ponów są zablokowane. Gdy twój odbiornik znów będzie sprawny, ponownie włącz punkt końcowy w ustawieniach przestrzeni roboczej (kliknij jego odznakę statusu): ponowne włączenie czyści historię niepowodzeń, więc punkt końcowy rozpoczyna nowe 5-dniowe okno, a zdarzenie endpoint.enabled jest wysyłane do innych punktów końcowych przestrzeni roboczej. Zdarzenia, które wystąpią, gdy punkt końcowy jest wyłączony, są zapisywane w jego dzienniku dostarczania ze statusem odrzucone, zamiast być wysyłane. Po ponownym włączeniu użyj Wyślij test, aby potwierdzić, że punkt końcowy jest osiągalny, a następnie Powtórz nieudane + odrzucone, aby nadrobić wszystko, co straciłeś podczas awarii, w jednej akcji.
Ręczne ponowne dostarczanie i testowanie
Dziennik dostarczania w ustawieniach przestrzeni roboczej pokazuje każdą próbę dostarczenia wraz z jej kodem statusu i odpowiedzią. Stamtąd:
- Ponów ponownie dostarcza oryginalne zdarzenie z przeszłego dostarczenia (ten sam
webhook-id, ta sama treść JSON) jako pojedynczą próbę. Nie wchodzi w harmonogram ponawiania i nigdy nie liczy się do automatycznego wyłączania, więc jest bezpieczne w użyciu podczas debugowania niestabilnego odbiornika. Ponowne wysłanie zawiera oryginalnywebhook-id, więc odbiornik, który już przetworzył zdarzenie przed odpowiedzią z błędem, traktuje je jako tę samą wiadomość. - Powtórz nieudane + odrzucone masowo powtarza każde ostatecznie nieudane i odrzucone dostarczenie punktu końcowego, od najstarszego (dostarczania wciąż znajdujące się w harmonogramie automatycznego ponawiania są wykluczone, więc powtórzenie nigdy nie zduplikuje ponownej próby, która i tak by się powiodła). Ponownie dostarczone wiadomości używają pełnego harmonogramu ponawiania i zawierają oryginalny
webhook-id: dla twojej usługi powtórzenie to ta sama wiadomość ponownie, więc obsługa idempotencji na podstawiewebhook-idsprawia, że nadrobienie jest bezpieczne, niezależnie od tego, czy oryginał kiedykolwiek dotarł. Zdarzenie, które zostało już dostarczone przez ponowne wysłanie lub wcześniejsze powtórzenie, jest pomijane, podobnie jak to, które wciąż jest dostarczane. Wybierz, jak daleko wstecz sięgnąć (ostatnie 24 godziny, ostatnie 7 dni lub wszystko, co zostało zachowane), a okno dialogowe podglądu pokazuje, ile zdarzeń zostanie wysłanych w tym zakresie przed rozpoczęciem; duże zakresy są automatycznie stronicowane, a postęp jest pokazywany w miarę ich przetwarzania. Powtórzenia docierają poza kolejnością; uporządkuj nadrobione zdarzenia wedługsequencew przesyłce. - Wyślij test dostarcza podpisane zdarzenie
webhook.pingz pustym obiektemdatadokładnie do tego punktu końcowego, niezależnie od jego subskrypcji zdarzeń, i pokazuje wynik (kod statusu lub powód niepowodzenia) zaraz po zakończeniu próby. Użyj tego, aby potwierdzić osiągalność i przetestować weryfikację podpisu od początku do końca. Zdarzenie testowe nigdy nie otwiera, nie przesuwa ani nie resetuje okna niepowodzeń.
Najlepsze praktyki
- Deduplikuj na podstawie
webhook-id. Automatyczne ponowne próby, ponowne wysyłanie i powtórzenia używają tego samego identyfikatora, więc przechowywanie przetworzonych identyfikatorów daje ci dokładnie jednokrotne przetwarzanie na podstawie co najmniej jednokrotnego dostarczania. - Nigdy nie traktuj przesyłki jako aktualnego stanu. Przesyłki są lekkie i mogą docierać z opóźnieniem; używaj zdarzenia jako wyzwalacza i odczytuj aktualny stan z API.
- Nie polegaj na kolejności. Ponowne próby i równoległe dostarczanie oznaczają, że zdarzenia mogą docierać poza kolejnością.
- Odpowiadaj 2xx przed ciężką pracą. Umieść zdarzenie w kolejce i przetwarzaj je asynchronicznie; handler, który wykonuje swoją pracę w linii, ryzykuje przekroczenie 10-sekundowego limitu czasu i zostanie ponowiony, co zamienia jedno zdarzenie w kilka zduplikowanych prób.
- Rotuj tajne klucze z okresem karencji. Rotacja z ustawień przestrzeni roboczej zachowuje poprzedni tajny klucz ważny przez okres, który wybierzesz, a dostarczania są podpisywane oboma tajnymi kluczami w tym czasie, więc stopniowe wdrażanie twoich usług nigdy nie gubi dostarczenia. Punkt końcowy przechowuje jeden poprzedni tajny klucz, więc druga rotacja z okresem karencji jest odrzucana, dopóki pierwszy okres nie dobiegnie końca; rotacja bez okresu karencji jest zawsze akceptowana i natychmiast unieważnia wszystkie stare tajne klucze, co jest ścieżką dla wycieku tajnego klucza.