Webhooks
locco dostarcza podpisane powiadomienia o zdarzeniach JSON do zarejestrowanych przez partnera endpointów HTTPS, gdy w organizacji wystąpią zdarzenia domenowe (utworzenie wpisu, zatwierdzenie wpisu, wypłata wpisu, aktualizacja wpisu, utworzenie lub aktualizacja pracownika). Ta strona stanowi dokumentację referencyjną dla partnerów: opisuje format sygnatury, mechanizm ochrony przed ponownym wysłaniem (replay protection), wymagania dotyczące idempotentności, politykę ponawiania prób oraz kanoniczny katalog typów zdarzeń.
Model push jest odpowiednikiem modelu pull dla API /api/v1/*. Payload zawiera te same obiekty DTO v1, które GET /api/v1/{resource}/{id} zwraca w odpowiedzi na zapytanie z kluczem API, dzięki czemu partnerzy korzystają z jednego kontraktu dla obu rozwiązań.
W skrócie
- Partnerzy rejestrują endpoint HTTPS za pomocą
POST /api/v1/webhooksi otrzymują jednorazowy klucz tajny do podpisywania (whsec_...). - locco wysyła żądania POST z formatem JSON na ten endpoint, gdy zostaną wywołane pasujące zdarzenia. Treści żądań (bodies) są podpisywane za pomocą HMAC-SHA256.
- Nieudane doręczenia (błędy 5xx, błędy sieciowe, przekroczenia limitu czasu) są ponawiane z wykładniczym opóźnieniem (exponential back-off); błędy 4xx przerywają ponawianie. Po około 40 godzinach nieudanych prób (8 prób oprócz próby początkowej) doręczenie jest oznaczane jako wyczerpane (exhausted).
- Po 10 kolejnych wyczerpanych doręczeniach subskrypcja jest automatycznie wyłączana. Aby ją reaktywować, należy zarejestrować ją ponownie.
- Zagnieżdżony dziennik doręczeń (
GET /api/v1/webhooks/{id}/deliveries) oraz strona w aplikacji Ustawienia → Integracje → Webhooks pokazują, co zostało wysłane i jaka odpowiedź została zwrócona.
Format sygnatury
Każde żądanie zawiera nagłówek Locco-Signature:
Locco-Signature: t=<unix-timestamp>,v1=<hex-hmac-sha256>Dane wejściowe dla HMAC to kanoniczny ciąg znaków {timestamp}.{payload}:
{timestamp}: liczba całkowita w parametrzet=(sekundy Unix epoch, UTC)..: dosłowny znak kropki.{payload}: surowe, niezmodyfikowane bajty treści żądania. Nie należy ponownie serializować JSON przed obliczeniem. Białe znaki i kolejność kluczy mają znaczenie.
Format ten dokładnie odzwierciedla sygnaturę webhook Stripe (t=...,v1=...), więc partnerzy posiadający istniejący kod weryfikujący Stripe mogą go dostosować, zmieniając jedynie nazwę nagłówka.
Weryfikacja sygnatury
import { createHmac, timingSafeEqual } from 'node:crypto';
const TOLERANCE_SECONDS = 5 * 60; // 5 minut, zalecane przez Stripe
export function verifyLoccoSignature(rawBody, header, secret) { // header wygląda następująco: "t=1714050000,v1=abc123..." const parts = Object.fromEntries( header.split(',').map((part) => part.split('=', 2)), ); const timestamp = Number(parts.t); const signature = parts.v1;
if (!timestamp || !signature) { throw new Error('Malformed Locco-Signature header'); }
// 1. Ochrona przed ponownym wysłaniem: odrzuć wszystko, co jest starsze niż okno tolerancji. const ageSeconds = Math.abs(Math.floor(Date.now() / 1000) - timestamp); if (ageSeconds > TOLERANCE_SECONDS) { throw new Error(`Signature timestamp is ${ageSeconds}s old; rejecting`); }
// 2. Ponownie oblicz HMAC dla kanonicznych danych wejściowych. const signedPayload = `${timestamp}.${rawBody}`; const expected = createHmac('sha256', secret) .update(signedPayload, 'utf8') .digest('hex');
// 3. Porównanie w czasie stałym. const expectedBuf = Buffer.from(expected, 'hex'); const actualBuf = Buffer.from(signature, 'hex'); if ( expectedBuf.length !== actualBuf.length || !timingSafeEqual(expectedBuf, actualBuf) ) { throw new Error('Invalid signature'); }
return true;}import hmac, hashlib, time
TOLERANCE_SECONDS = 5 * 60
def verify_locco_signature(raw_body: bytes, header: str, secret: str) -> bool: parts = dict(p.split("=", 1) for p in header.split(",")) ts = int(parts["t"]) sig = parts["v1"] if abs(time.time() - ts) > TOLERANCE_SECONDS: raise ValueError("timestamp outside tolerance") signed = f"{ts}.".encode() + raw_body expected = hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest() if not hmac.compare_digest(expected, sig): raise ValueError("invalid signature") return Trueusing System.Collections.Generic;using System.Globalization;using System.Linq;using System.Security.Cryptography;using System.Text;
public static bool VerifyLoccoSignature(ReadOnlySpan<byte> rawBody, string header, string secret){ const int toleranceSeconds = 5 * 60; var parts = header.Split(',') .Select(p => p.Split('=', 2)) .ToDictionary(p => p[0], p => p[1]); var timestamp = long.Parse(parts["t"], CultureInfo.InvariantCulture); var signature = parts["v1"]; var ageSeconds = Math.Abs(DateTimeOffset.UtcNow.ToUnixTimeSeconds() - timestamp); if (ageSeconds > toleranceSeconds) throw new InvalidOperationException("timestamp outside tolerance");
var signed = Encoding.UTF8.GetBytes($"{timestamp}.").Concat(rawBody.ToArray()).ToArray(); using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secret)); var expected = Convert.ToHexString(hmac.ComputeHash(signed)).ToLowerInvariant(); return CryptographicOperations.FixedTimeEquals( Encoding.UTF8.GetBytes(expected), Encoding.UTF8.GetBytes(signature));}Ten sam schemat działa w każdym języku: HMAC-SHA256 z {timestamp}.{payload} przy użyciu klucza tajnego (secret) jako klucza, zakodowany w formacie hex i porównany w czasie stałym z parametrem v1=.
Ochrona przed ponownym wysłaniem (WYMAGANE)
Partnerzy MUSZĄ odrzucać żądania, których timestamp jest starszy niż określone krótkie okno czasowe. Zalecana tolerancja wynosi 5 minut (zgodnie z domyślnym ustawieniem Stripe). Bez tej weryfikacji atakujący, który przechwyci prawidłową treść żądania i sygnaturę, może ją powtarzać w nieskończoność, dopóki klucz tajny nie zostanie zmieniony.
Należy odrzucić:
- Brakujący lub nieprawidłowy nagłówek
Locco-Signature. - Timestamp starszy niż okno tolerancji.
- Timestamp znacząco wybiegający w przyszłość (należy zezwolić na niewielki margines przesunięcia zegara, np. 30 sekund).
- Obliczony HMAC, który nie pasuje do
v1=.
Do weryfikacji HMAC należy używać porównania w czasie stałym (timingSafeEqual w Node.js, hmac.compare_digest w Pythonie, CryptographicOperations.FixedTimeEquals w .NET). Zwykłe porównanie ciągów znaków (===) ujawnia informacje o czasie wykonania (timing leaks) i w teorii może pozwolić na odtworzenie sygnatury bajt po bajcie.
Idempotentność
Każde doręczenie zawiera nagłówek Locco-Event-Id, czyli stały identyfikator Guid identyfikujący zdarzenie logiczne. Ten sam identyfikator zdarzenia jest wysyłany ponownie w przypadku:
- Ręcznych powtórzeń: partner kliknął Retry w interfejsie dziennika doręczeń lub wysłał żądanie POST na adres
/api/v1/webhooks/{id}/deliveries/{deliveryId}/retry. - Ponownego doręczenia typu at-least-once: proces roboczy w tle, który ulegnie awarii między wysłaniem żądania a zarejestrowaniem sukcesu, ponownie zakolejuje ten sam wiersz przy następnym odpytywaniu.
Endpointy partnera MUSZĄ traktować powtarzające się identyfikatory Locco-Event-Id jako operacje bezskutkowe (no-ops). Standardowy wzorzec: utrzymywanie magazynu niedawnych doręczeń (Redis, mała tabela w bazie danych, pamięć podręczna w procesie) i odrzucanie każdego zdarzenia, którego identyfikator został już przetworzony w ciągu ostatniej godziny.
Bez tej deduplikacji efekt uboczny po stronie partnera (synchronizacja z ERP, wysłanie powiadomienia e-mail, modyfikacja rekordu) zostanie wykonany dwukrotnie dla tego samego zdarzenia logicznego przy każdej próbie ponowienia.
Semantyka ponawiania prób jest celowa:
- Ponowna próba wysyła ten sam
Locco-Event-Id, co pierwotne doręczenie. Partnerzy dokonują deduplikacji wyłącznie na podstawie nagłówka, bez konieczności analizowania payload. - Ponowna próba wysyła te same bajty treści, co pierwotne doręczenie. Sygnatura jest obliczana ponownie z nowym timestamp, ale dane wejściowe HMAC są identyczne, z wyjątkiem tego timestamp.
Polityka ponawiania prób
locco ponawia próby w przypadku przejściowych błędów z wykładniczym opóźnieniem (exponential back-off). Harmonogram obejmuje łącznie 9 prób HTTP (początkowe doręczenie plus 8 powtórzeń) rozłożonych w czasie na około 40 godzin.
| Próba | Opóźnienie od poprzedniej próby | Łączny czas od 1. próby |
|---|---|---|
| 1 (początkowa) | (brak) | 0 |
| 2 | 30 sekund | 30s |
| 3 | 1 minuta | 1m 30s |
| 4 | 5 minut | 6m 30s |
| 5 | 15 minut | 21m 30s |
| 6 | 1 godzina | ~1h 21m |
| 7 | 3 godziny | ~4h 21m |
| 8 | 12 godzin | ~16h 21m |
| 9 (ostatnia) | 24 godziny | ~40h 21m |
Ponowienie a zakończenie
| Odpowiedź partnera | Zachowanie |
|---|---|
2xx | Sukces. Wiersz doręczenia oznaczony jako Succeeded. Brak ponownej próby. |
4xx (z wyjątkiem 408, 429) | Błąd po stronie partnera. Wiersz doręczenia oznaczony jako Failed. Brak ponownej próby. |
408 (Request Timeout), 429 (Too Many) | Traktowane jako błąd przejściowy, ponowienie zgodnie z harmonogramem. |
5xx | Błąd serwera. Ponowienie zgodnie z harmonogramem. |
| Błąd sieciowy (DNS, reset TCP, TLS handshake) | Ponowienie zgodnie z harmonogramem. |
| Przekroczenie limitu czasu (locco rezygnuje po 10 sekundach) | Ponowienie zgodnie z harmonogramem. |
Po wyczerpaniu wszystkich 9 prób bez uzyskania statusu 2xx, wiersz doręczenia zostaje oznaczony jako Exhausted. Treść ostatniej odpowiedzi partnera (skrócona do 2 KB) jest zachowywana w dzienniku doręczeń, aby partnerzy mogli zdiagnozować, co zwracał ich endpoint.
Nagłówki Retry-After od partnerów nie są obsługiwane w wersji v1. Zawsze obowiązuje powyższy harmonogram.
Automatyczne wyłączenie
Jeśli subskrypcja zgromadzi 10 kolejnych doręczeń oznaczonych jako Exhausted, locco automatycznie ją wyłączy (IsActive = false, DisabledAt, DisabledReason). Chroni to locco przed ciągłym odpytywaniem trwale uszkodzonego endpointu.
Aby ponownie włączyć automatycznie wyłączoną subskrypcję, należy:
- Sprawdzić dziennik doręczeń, aby zidentyfikować, co zwracał endpoint partnera podczas awarii.
- Naprawić endpoint partnera.
- Usunąć subskrypcję i utworzyć nową (
POST /api/v1/webhooks).
Typy zdarzeń
Katalog wersji v1. Nowe typy są dodawane poprzez zmiany w kodzie równolegle z nowymi zdarzeniami domenowymi, dzięki czemu partnerzy mogą mieć pewność, że ta lista stanowi kompletny zestaw dla każdego wydania.
Ten sam zestaw jest udostępniony w dokumentacji API jako schemat typu wyliczeniowego WebhookEventType. Generatory SDK tworzą typowany enum na podstawie tej listy, co zapewnia partnerom bezpieczeństwo typów na etapie kompilacji dla subskrybowanych wartości.
| Typ zdarzenia | Kiedy występuje | Payload data |
|---|---|---|
entry.approved | Wpis zmienił status na końcowy Approved w ramach kroku przepływu pracy (workflow). Wpisy zatwierdzane automatycznie (w firmach bez skonfigurowanego przepływu zatwierdzania) nie wywołują tego zdarzenia. Patrz entry.updated. | Bezpieczne dla webhook podsumowanie wpisu (id + numer wpisu + id pracownika + daty + status + kwoty). |
entry.paidout | Wpis został oznaczony jako wypłacony w ramach paczki wypłat. | Takie samo bezpieczne dla webhook podsumowanie wpisu. |
entry.created | Utworzono nowy wpis. | Takie samo bezpieczne dla webhook podsumowanie wpisu. |
entry.updated | Dziennik audytu wpisu zarejestrował zdarzenie inne niż utworzenie, końcowe zatwierdzenie lub wypłata, tzn. wpis uległ zmianie w sposób, który platforma uznaje za warty odnotowania. Obejmuje to edycję, przesłanie, pośrednie zatwierdzenia w przepływie pracy, anulowanie, zwrot do poprawy oraz zmiany encji podrzędnych (wydatki, diety, miejsca powstawania kosztów, załączniki). | Takie samo bezpieczne dla webhook podsumowanie wpisu. |
employee.created | Utworzono nowy rekord pracownika. | Bezpieczne dla webhook podsumowanie pracownika (id + imię i nazwisko + stanowisko + status + znaczniki czasu audytu). |
employee.updated | Zaktualizowano istniejący rekord pracownika. | Takie samo bezpieczne dla webhook podsumowanie pracownika. |
Kontrakt payload webhook: zdarzenia zawierają identyfikatory, a nie dane osobowe (PII)
Treści zdarzeń webhook zawierają identyfikatory zasobów oraz minimalną migawkę prezentacyjną - nigdy dane osobowe (PII). W szczególności:
- Zdarzenia dotyczące pracowników zawierają
id,firstName,lastName,position,departmentId,status,isAccountOwner,isArchivedorazcreatedAt/editedAt. Nie zawierają one adresu e-mail, telefonu, adresu, identyfikatora podatkowego (OIB), konta bankowego (IBAN) ani kodu BIC. - Zdarzenia dotyczące wpisów zawierają identyfikator wpisu, numer wpisu, identyfikator pracownika, daty, status przepływu pracy, fazę przepływu pracy (
phase), flagę wniosku o podróż (isRequest), status wypłaty, kwotę zaliczki w walucie wniosku i walucie bazowej oraz znaczniki czasu audytu. Nie zawierają one szczegółów dotyczących powiązanych wydatków, diet, miejsc powstawania kosztów, tekstu docelowego ani opisów tekstowych, raportów czy uwag dotyczących wstępnego zatwierdzenia.
isRequest ma wartość true, gdy podróż wciąż oczekuje na autoryzację (nieautoryzowany wniosek o podróż, a nie wystawiona delegacja); phase to numeryczna wartość WorkflowPhase - 1 (PreApproval), gdy podróż jest autoryzowana, 0 (Standard) dla zatwierdzenia rozliczenia po podróży oraz null, gdy wpis nie znajduje się obecnie w przepływie zatwierdzania. Podobnie jak status, phase jest serializowany w przesyłanym webhooku jako liczba całkowita, a nie ciąg znaków. Wniosek o podróż zawiera pusty entryNumber - kolejny numer jest nadawany dopiero po autoryzacji podróży. Zobacz sekcję Wnioski o podróż i odroczona numeracja poniżej, aby dowiedzieć się, jak te przejścia są prezentowane jako zdarzenia.
Gdy integracja partnera wymaga pełnych danych osobowych (PII) lub pełnych szczegółów zagnieżdżonych, pobiera zasób za pośrednictwem uwierzytelnionego API:
GET /api/v1/employees/{id}w celu pobrania pełnego rekordu pracownika (e-mail, telefon, adres, identyfikator podatkowy, konto bankowe).GET /api/v1/travel-entries/{id}w celu pobrania pełnego wpisu (wydatki, diety, przypisania do miejsc powstawania kosztów, załączniki, pola tekstowe).
Jest to ten sam wzorzec, którego używa Stripe (treść zdarzenia = identyfikator; pełny zasób = uwierzytelnione żądanie GET). Żądania pull mogą być audytowane dla każdego wywołania pod kątem klucza API, który je wygenerował. Powiadomienia push webhook opuszczają locco w momencie ich wywołania i nie można ich przypisać w celach audytowych do konkretnego odbiorcy. Utrzymanie danych osobowych (PII) poza obszarem push ogranicza zakres RODO dla integracji partnera i pozwala uniknąć zmuszania endpointów partnera do obsługi danych osobowych na poziomie wejścia sieciowego, na co mogą nie być przygotowane.
Obiekty DTO webhooków w wersji v1 są typu append-only - mogą być dodawane nowe pola niezawierające danych osobowych (PII), natomiast istniejące pola nie będą usuwane ani zmieniane. Pola z danymi osobowymi (PII) nie zostaną dodane do payload webhooka - ten obszar stanowi kontrakt, a nie tylko domyślne zachowanie.
Wybór zdarzeń do subskrypcji
Dwa popularne modele integracji partnerskich:
- Synchronizacja z ERP / systemem zewnętrznym: subskrypcja zdarzeń
entry.created,entry.updated,entry.approvedorazentry.paidout. Razem zapewniają one pełny, jednokierunkowy podgląd cyklu życia każdego wpisu. W przypadku organizacji bez przepływu zatwierdzania zdarzenieentry.approvedpo prostu nigdy nie wystąpi (przypadek automatycznego zatwierdzenia przechodzi zamiast tego przezentry.updated). W przypadku organizacji z przepływem pracy zdarzenieentry.approvedjest wymagane do przechwycenia ostatecznego przejścia w stan zatwierdzenia. W połączeniu z kontraktem deduplikacji opartym naLocco-Event-Idpozwala to na utrzymanie wiarygodnego zewnętrznego rejestru bez konieczności odpytywania (polling). - Przepływ pracy oparty na zatwierdzaniu: subskrypcja zdarzenia
entry.approved, jeśli integracja dotyczy wyłącznie ostatecznego zatwierdzenia (np. uruchomienie procesu płatności w systemie zewnętrznym w momencie, gdy wpis zostanie w pełni zatwierdzony). Uwaga: zdarzenieentry.approvedwystępuje tylko w organizacjach, które skonfigurowały przepływ zatwierdzania z co najmniej jednym krokiem posiadającym uprawnione osoby zatwierdzające. W przypadku organizacji bez przepływu pracy wpis jest automatycznie zatwierdzany przy przesłaniu i zamiast tego wywoływane jest zdarzenieentry.updated.
Wnioski o podróż i odroczona numeracja
Gdy dany typ wpisu korzysta z przepływu zatwierdzania podróży (wstępnego zatwierdzenia), podróż musi zostać autoryzowana, zanim stanie się rzeczywistą delegacją. W tym oknie czasowym wpis jest wnioskiem o podróż: isRequest ma wartość true, phase wynosi 1 (wartość PreApproval), a entryNumber jest pusty. Nie ma dedykowanego typu zdarzenia dla wniosków - cały cykl życia wniosku jest przekazywany za pośrednictwem entry.updated:
- Przesłanie wniosku (podróż zostaje wysłana do autoryzacji): wywoływane jest zdarzenie
entry.updatedzisRequest: true,phase: 1oraz pustymentryNumber. - Autoryzacja podróży (wniosek zostaje zatwierdzony i staje się edytowalną delegacją): wywoływane jest zdarzenie
entry.updatedzisRequest: falseoraz uzupełnionym, nowo nadanymentryNumber. Jest to zdarzenie, które należy monitorować, jeśli rekordy w systemach zewnętrznych są powiązane z numerem wpisu - numer pojawia się tutaj, a nie wentry.created. Autoryzacja podróży nie wywołuje zdarzeniaentry.approved- to zdarzenie jest zarezerwowane dla ostatecznego zatwierdzenia rozliczenia. - Odrzucenie lub anulowanie wniosku: wywoływane jest zdarzenie
entry.updatedz końcowym statusem. Odrzucony lub anulowany wniosek nigdy nie otrzymuje numeru wpisu.
Partnerzy korzystający z synchronizacji przyrostowej (delta-sync), którzy odpytują GET /api/v1/travel-entries?isRequest=false (oraz oczekują na niepusty entryNumber), zobaczą, że wpis trafia do ich zestawu delegacji w momencie autoryzacji podróży wywołującej entry.updated, a nie wcześniej.
Struktura koperty (envelope)
{ "id": "evt_b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7", "type": "entry.approved", "createdAt": "2026-04-23T12:34:56Z", "data": { "id": "...", "entryNumber": "1/26", "employeeId": "...", "departureDate": "2026-04-21", "returnDate": "2026-04-23", "status": 4, "phase": null, "isRequest": false, "advancePayment": 500.00, "currencyCode": "EUR", "advancePaymentInBaseCurrency": 500.00, "createdAt": "2026-04-20T08:00:00Z", "editedAt": "2026-04-23T12:34:56Z" }}id:evt_z następującymi po nim 32 znakami szesnastkowymi (bez myślników), np.evt_b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7. Semantycznie jest to ta sama wartość Guid, co w nagłówkuLocco-Event-Id, ale w innym formacie ciągu znaków.type: jeden z kanonicznych ciągów znaków typu zdarzenia wymienionych powyżej.createdAt: znacznik czasu ISO 8601 UTC określający, kiedy zdarzenie powstało w locco (a nie kiedy zostało doręczone).data: bezpieczne dla webhook podsumowanie zasobu zdarzenia (patrz sekcja „Kontrakt payload webhook” powyżej).
Struktura przesyłu jest zarejestrowana w dokumentacji API jako schemat WebhookEnvelope, gdzie pole data jest rozróżniane na podstawie type: zdarzenia entry.* zawierają obiekt TravelEntryWebhookDto, a zdarzenia employee.* zawierają obiekt EmployeeWebhookDto. Generatory SDK tworzą typowaną unię dyskryminowaną na podstawie dokumentu OpenAPI, dzięki czemu partnerzy korzystający z generatora otrzymują silnie typowany deserializator zamiast obiektu o dowolnej strukturze.
Uwaga dotycząca formatu: dopasowanie id do Locco-Event-Id. Pole id w kopercie ma format evt_{guid:N} (32 małe znaki szesnastkowe bez myślników), podczas gdy nagłówek Locco-Event-Id ma kanoniczną postać szesnastkową Guid 8-4-4-4-12 z myślnikami (np. b2c3d4e5-f6a7-b8c9-d0e1-f2a3b4c5d6e7). Reprezentują one tę samą wartość Guid, ale zwykłe porównanie ciągów znaków pod kątem równości zakończy się niepowodzeniem. Aby je porównać, należy: (a) usunąć prefiks evt_ i usunąć myślniki z wartości nagłówka przed porównaniem ciągów znaków, lub (b) sparsować obie wartości jako obiekty Guid i porównać sparsowane wartości. Formaty przesyłu są stabilne i celowe. Koperta webhooka Stripe używa tego samego skrótu evt_..., a partnerzy posiadający istniejący kod weryfikujący Stripe mogą przenieść ten wzorzec parsowania bez zmian.
Nagłówki
Każde żądanie POST webhooka zawiera:
| Header | Opis |
|---|---|
Content-Type | Zawsze application/json; charset=utf-8. |
User-Agent | Locco-Webhook/1.0. W razie potrzeby można na tej podstawie skonfigurować białe listy (allowlists) partnera. |
Locco-Event | Ciąg znaków typu zdarzenia (np. entry.approved). Wygodne kierowanie (routing) bez konieczności analizowania treści żądania. |
Locco-Event-Id | Stały identyfikator Guid na potrzeby idempotentności. Partnerzy MUSZĄ dokonywać deduplikacji na jego podstawie. |
Locco-Signature | t={timestamp},v1={hex-hmac-sha256}. Patrz format sygnatury. |
Locco-Delivery-Id | Wewnętrzny identyfikator wiersza doręczenia, przydatny przy korelacji z dziennikiem doręczeń za pośrednictwem wsparcia technicznego locco. |
Rejestracja subskrypcji
Treść żądania POST /api/v1/webhooks:
{ "url": "https://partner.example.com/hooks/locco", "eventTypes": ["entry.created", "entry.updated", "entry.approved", "entry.paidout"]}Ograniczenia:
urlmusi korzystać z protokołu HTTPS. Niezabezpieczony protokół HTTP jest odrzucany na etapie tworzenia oraz ponownie na etapie wysyłki w ramach wielopoziomowej ochrony (defense in depth).urlnie może wskazywać na localhost, loopback ani prywatne adresy IP RFC1918 (ochrona przed SSRF).eventTypesnie może być pusty, a każda pozycja musi znajdować się w kanonicznym katalogu.
Treść odpowiedzi zwraca klucz tajny do podpisywania dokładnie raz jako signingSecret. Należy go natychmiast zapisać. Nie ma możliwości odzyskania utraconego klucza tajnego z kolejnego żądania. Klucz tajny nie jest zwracany przez GET /api/v1/webhooks ani GET /api/v1/webhooks/{id}.
Dziennik doręczeń
Można sprawdzić, co zostało wysłane i co zostało zwrócone, za pomocą:
- W aplikacji: Ustawienia → Integracje → Webhooks (rozwijany wiersz dla każdego doręczenia, zawierający pełny payload i skróconą treść odpowiedzi).
- API (zagnieżdżone pod subskrypcją nadrzędną):
GET /api/v1/webhooks/{id}/deliveries: lista doręczeń dla subskrypcji. Obsługuje parametry zapytaniapage,pageSize,status,eventType.GET /api/v1/webhooks/{id}/deliveries/{deliveryId}: pobranie pojedynczego doręczenia, w tym pełnych podpisanych bajtów payload oraz skróconej treści odpowiedzi partnera. Zwraca status 404, jeśli doręczenie istnieje, ale należy do innej subskrypcji (próby dostępu między subskrypcjami są niedozwolone).
Ręczne ponowienie: POST /api/v1/webhooks/{id}/deliveries/{deliveryId}/retry. Dostępne tylko dla doręczeń o statusie Failed lub Exhausted. Ponowna próba zachowuje oryginalny Locco-Event-Id oraz bajty payload, dzięki czemu mechanizmy deduplikacji partnerów nadal działają poprawnie.
Retencja danych. Wiersze doręczeń - w tym zarejestrowane bajty payload - są trwale usuwane po 30 dniach przez codzienne zadanie czyszczące. Partnerzy, którzy wymagają stałego dziennika audytu każdego zdarzenia, muszą zapisać własną kopię w momencie odbioru. Endpoint dziennika doręczeń służy do krótkoterminowego monitorowania i ponawiania prób, a nie do długoterminowej archiwizacji. W połączeniu z projekcją payloadu webhooka pozbawioną danych osobowych (bajty payloadu zawierają identyfikatory zasobów i minimalną migawkę prezentacyjną, nigdy dane osobowe PII), historyczna weryfikacja za pośrednictwem tego endpointu nie ujawni informacji wykraczających poza identyfikatory zasobów.