Webhooki
Webhooki wysyłają żądanie HTTP POST do Twojego systemu, gdy w kolejce zachodzi zdarzenie. Zdarzeniem jest na przykład dołączenie biletu do kolejki, jego wywołanie lub zakończenie.
Pierwsze kroki
Dział zatytułowany „Pierwsze kroki”- Otwórz admin.jonot.io/settings/integrations.
- Kliknij Dodaj punkt końcowy.
- Wprowadź publiczny adres URL HTTPS, pod którym Twój serwer przyjmuje żądania.
- Wybierz zdarzenia, które chcesz zasubskrybować.
- Kliknij Zapisz punkt końcowy.
Skopiuj i zapisz klucz podpisujący, gdy się pojawi. Jonot pokazuje go tylko raz. Aby sprawdzić, czy Jonot dociera do Twojego punktu końcowego, użyj przycisku Wyślij żądanie testowe na stronie edycji punktu końcowego.
Typy zdarzeń
Dział zatytułowany „Typy zdarzeń”| Nazwa zdarzenia | Kiedy Jonot je wysyła |
|---|---|
ticket.joined | Klient dołącza do kolejki |
ticket.called | Członek personelu wywołuje bilet do stanowiska obsługi |
ticket.completed | Bilet zostaje oznaczony jako zakończony |
ticket.cancelled | Klient lub członek personelu anuluje bilet |
ticket.skipped | Bilet zostaje pominięty (odroczenie z możliwością przywołania) |
ticket.no_show | Wywołany lub pominięty bilet zostaje potwierdzony jako nieobecność |
queue.status_changed | Zmienia się status kolejki (ACTIVE, PAUSED lub CLOSED) |
Format ładunku
Dział zatytułowany „Format ładunku”Każda wysyłka to żądanie HTTP POST z nagłówkami:
Content-Type: application/jsonX-Jonot-Event: ticket.calledX-Jonot-Delivery-Id: <uuid>X-Jonot-Timestamp: 2024-06-01T12:00:00.000ZX-Jonot-Signature: v1,<base64-hmac-sha256>X-Jonot-Payload-Version: v1Domyślne ciało żądania to obiekt JSON. Zawartość pola payload zależy od zdarzenia:
ticket.joined
{ "deliveryId": "<uuid>", "event": "ticket.joined", "timestamp": "2024-06-01T12:00:00.000Z", "org": { "id": "org_…", "name": "My Org" }, "payload": { "ticket": { "id": "tkt_…", "queueId": "q_…" }, "queue": { "id": "q_…", "name": "Main Queue" }, "location": { "id": "loc_…", "name": "Downtown" } }}ticket.called / ticket.completed / ticket.skipped / ticket.no_show
{ "deliveryId": "<uuid>", "event": "ticket.called", "timestamp": "2024-06-01T12:00:00.000Z", "org": { "id": "org_…", "name": "My Org" }, "payload": { "ticket": { "id": "tkt_…", "number": 42, "status": "CALLED", "queueId": "q_…", "createdAt": "2024-06-01T11:58:00.000Z", "updatedAt": "2024-06-01T12:00:00.000Z" } }}ticket.cancelled
{ "deliveryId": "<uuid>", "event": "ticket.cancelled", "timestamp": "2024-06-01T12:00:00.000Z", "org": { "id": "org_…", "name": "My Org" }, "payload": { "ticketId": "tkt_…", "queueId": "q_…" }}queue.status_changed
{ "deliveryId": "<uuid>", "event": "queue.status_changed", "timestamp": "2024-06-01T12:00:00.000Z", "org": { "id": "org_…", "name": "My Org" }, "payload": { "queueId": "q_…", "status": "PAUSED" }}Weryfikacja podpisu
Dział zatytułowany „Weryfikacja podpisu”Jonot podpisuje każdą wysyłkę algorytmem HMAC-SHA256 na ciągu:
<deliveryId>.<timestamp>.<body>Aby zweryfikować w Node.js (≥18):
import { createHmac, timingSafeEqual } from "node:crypto";
/** * Returns true when the signature header is valid and the timestamp is * within 5 minutes of now. Throws for malformed input. */function verifySignature(secret, deliveryId, timestamp, body, header) { // Replay-attack guard: reject deliveries older than 5 minutes. const ageMs = Date.now() - new Date(timestamp).getTime(); if (Math.abs(ageMs) > 5 * 60 * 1000) return false;
const expected = "v1," + createHmac("sha256", secret) .update(`${deliveryId}.${timestamp}.${body}`) .digest("base64");
// timingSafeEqual prevents timing-oracle attacks. // Buffers must be the same length — if lengths differ the signature is // invalid, but we still compare a dummy value to keep constant time. const expectedBuf = Buffer.from(expected); const headerBuf = Buffer.from(header); if (expectedBuf.length !== headerBuf.length) return false; return timingSafeEqual(expectedBuf, headerBuf);}Szablony ładunku
Dział zatytułowany „Szablony ładunku”Domyślne ciało JSON możesz zastąpić własnym szablonem. Szablony korzystają z uproszczonej składni Mustache:
{{ path.to.value }}— Jonot wstawia wartość i zabezpiecza ją odpowiednio do typu treści: jako ciąg JSON dlaapplication/json, w kodowaniu procentowym dlaapplication/x-www-form-urlencoded, bez zmian dlatext/plain.{{{ path.to.value }}}— Jonot wstawia wartość bez żadnego zabezpieczania.
Przykładowy szablon dla application/json (zasubskrybowane ticket.joined):
{ "type": "{{ event }}", "ticketId": "{{ payload.ticket.id }}", "queueName": "{{ payload.queue.name }}"}Edytor szablonu
Dział zatytułowany „Edytor szablonu”Pole szablonu ładunku to pełny edytor kodu z:
- Podświetlaniem składni i dopasowywaniem nawiasów dla szablonów JSON.
- Listą zmiennych — rzędem przycisków ze ścieżkami zmiennych dostępnymi dla wybranych zdarzeń. Kliknij przycisk, aby wstawić token
{{ path }}w miejscu kursora. - Walidacją na żywo — podczas pisania edytor sprawdza ścieżki zmiennych. Sprawdza też strukturę JSON, gdy typ treści to
application/json, a szablon nie zawiera surowych znaczników{{{ }}}. Edytor zaznacza problemy w kodzie i wypisuje je poniżej:- Błąd — ścieżka nieznana we wszystkich wybranych zdarzeniach.
- Ostrzeżenie — ścieżka istniejąca tylko dla części wybranych zdarzeń (dla wysyłek pozostałych zdarzeń będzie pusta).
Przy zapisie serwer stosuje te same reguły walidacji.
Ponawianie wysyłek
Dział zatytułowany „Ponawianie wysyłek”Cloudflare Queues ponawia nieudaną wysyłkę do 3 razy, za każdym razem z dłuższym opóźnieniem. Wysyłka jest nieudana, gdy punkt końcowy zwróci status spoza zakresu 2xx albo gdy wystąpi błąd połączenia. Jeśli pierwsza próba i wszystkie 3 ponowienia zawiodą, wysyłka trafia do kolejki dead-letter, a licznik kolejnych niepowodzeń punktu końcowego rośnie.
Po 20 kolejnych niepowodzeniach Jonot automatycznie wyłącza punkt końcowy. Możesz włączyć go ponownie na stronie edycji punktu końcowego. Licznik wraca wtedy do zera.
Historia wysyłek
Dział zatytułowany „Historia wysyłek”Zakładka Wysyłki każdego punktu końcowego pokazuje próby wysyłki z ostatnich 30 dni. Każdy wpis zawiera typ zdarzenia, status HTTP, liczbę prób i znacznik czasu. Użyj przycisku Załaduj więcej, aby zobaczyć starsze wpisy z tego okresu.
| Limit | Wartość |
|---|---|
| Punkty końcowe na organizację | 5 |
| Niestandardowe nagłówki na punkt końcowy | 10 |
| Długość wartości nagłówka | 1024 bajty |
| Rozmiar szablonu ładunku | 16 KB |
| Limit czasu wysyłki | 10 s |
| Retencja historii wysyłek | 30 dni |
| Maksymalna szybkość wysyłki na organizację | 120 / 60 s |
Wymiana klucza podpisującego
Dział zatytułowany „Wymiana klucza podpisującego”- Otwórz stronę edycji punktu końcowego.
- Kliknij Wymień klucz podpisujący.
- Potwierdź wymianę w oknie dialogowym.
- Skopiuj nowy klucz natychmiast. Jonot pokazuje go raz i nie potrafi go odtworzyć. Jeśli zamkniesz okno bez zapisania klucza, wymień klucz ponownie, aby otrzymać nową wartość.
- Zaktualizuj swój serwer, aby weryfikował podpisy nowym kluczem.
Stary i nowy klucz nigdy nie działają jednocześnie. Stary klucz przestaje weryfikować żądania w momencie, w którym potwierdzisz wymianę. Zaktualizuj system odbierający webhooki natychmiast po wymianie.
Interfejs pokazuje Aktywny klucz: ····XXXX obok przycisku wymiany. Ostatnie cztery znaki pozwalają potwierdzić, który klucz jest aktywny po wymianie.
Higiena klucza podpisującego
Dział zatytułowany „Higiena klucza podpisującego”Kiedy wymieniać:
- Podejrzenie kompromitacji: klucz pojawił się w pliku logu, został udostępniony odchodzącemu pracownikowi lub został uchwycony w nagraniu ekranu.
- Regularna wymiana: okresowa zmiana klucza skraca czas, przez który ujawniony klucz da się wykorzystać.
- Zmiana dostępu: wymień klucz po zmianie tego, które usługi mogą go odczytać.
Jak zaktualizować odbiornik:
- Wymień klucz w interfejsie administracyjnym i skopiuj nowy klucz.
- Zaktualizuj klucz używany przez system odbierający. Przechowuj go w zmiennej środowiskowej, menedżerze sekretów lub innym bezpiecznym magazynie.
- Wdróż zaktualizowany odbiornik.
- Potwierdź w zakładce Wysyłki, że kolejna wysyłka się powiodła.
Jeśli nowy klucz zostanie zgubiony przed jego zapisaniem:
Wymień klucz ponownie. Każda wymiana generuje nowy losowy klucz. Jonot nie odtworzy poprzedniej czytelnej wartości, ponieważ serwer przechowuje wyłącznie postać zaszyfrowaną.