Wychodzące webhooki
Webhooki wysyłają zdarzenia Manisma do publicznego endpointu HTTPS Twojej organizacji.
Konfiguracja endpointu
Otwórz jako Admin Zarządzanie → Połączenia, utwórz endpoint i wybierz nazwę, URL oraz zdarzenia. Secret podpisujący jest wyświetlany tylko raz. Przechowuj go bezpiecznie. Po rotacji nowy secret obowiązuje od momentu zapisu.
URL musi:
- używać HTTPS;
- być publicznie dostępny przez DNS;
- używać portu 443 lub 8443;
- nie zawierać nazwy użytkownika/hasła w URL;
- nie wskazywać na localhost, prywatne IP, link-local ani metadane chmury.
Użyj Test, aby wysłać ping przed aktywacją rzeczywistych zdarzeń.
Zdarzenia
| Zdarzenie | Wyzwalacz |
|---|---|
ping |
Ręczny test endpointu. |
client.created |
Klient utworzony. |
client.updated |
Klient zmieniony. |
job.created |
Zlecenie utworzone. |
job.planned |
Status zlecenia zmieniony na zaplanowane. |
job.completed |
Status zlecenia zmieniony na zakończone. |
job.cancelled |
Status zlecenia zmieniony na anulowane. |
approval.approved |
Klient zaakceptował propozycję. |
approval.declined |
Klient odrzucił propozycję. |
workorder.completed |
Dane zlecenia pracy gotowe do fakturowania przy zakończeniu. |
Zmiana innego pola już zakończonego zlecenia nie wysyła ponownie job.completed; musi zmienić się sam status.
Envelope i nagłówki
{
"event": "job.completed",
"version": 1,
"tenant_id": "tenant-uuid",
"occurred_at": "2026-07-13T09:15:00.000Z",
"data": {}
}
Nagłówki:
X-Manisma-Event: nazwa zdarzenia;X-Manisma-Delivery: stabilne id tej logicznej dostawy;X-Manisma-Signature:sha256=<hex HMAC-SHA256>dla dokładnej surowej treści.
Zweryfikuj HMAC przed parsowaniem JSON i używaj porównania bezpiecznego czasowo:
import { createHmac, timingSafeEqual } from 'node:crypto'
export function verify(rawBody, header, secret) {
if (!header?.startsWith('sha256=')) return false
const expected = createHmac('sha256', secret).update(rawBody).digest('hex')
const supplied = header.slice(7)
const a = Buffer.from(expected, 'hex')
const b = Buffer.from(supplied, 'hex')
return a.length === b.length && timingSafeEqual(a, b)
}
Payloady
Zdarzenia klienta zawierają id, customer_number, name, client_type, email i phone. Wewnętrzne notatki klienta są pomijane.
Zdarzenia zlecenia zawierają id/numer zlecenia, tytuł, status, priorytet, planowanie oraz zwięzłe obiekty dla klienta, adresu serwisowego i technika głównego. Nieprzypisane zlecenie lub zlecenie bez adresu używa null.
Zdarzenia akceptacji zawierają id akceptacji, job_id, status, wybrany termin i czas odpowiedzi. chosen_slot może być stringiem JSON lub starszym dowolnym tekstem; parsuj defensywnie.
workorder.completed zawiera:
- zwięzłe zlecenie i klienta;
- najnowszą zakończoną wizytę, jeśli dostępna;
- użyte materiały ze SKU, ceną sprzedaży, ilością i kwotą pozycji;
- czas pracy;
- suma materiałów w EUR.
Cena materiału to cena sprzedaży w momencie budowania payloadu webhooka, niekoniecznie historycznie przypięta cena.
Dostarczanie i retry
Webhooki są at least once. Uczyń swój odbiornik idempotentnym po `X-Manisma-Delivery**. Pomyślne odebranie to każda odpowiedź 2xx. Przekierowania nie są śledzone, a każda próba ma timeout 10 sekund.
Przy błędzie tymczasowym jest maksymalnie trzy próby: natychmiast, po 60 sekundach i ponownie po 300 sekundach. To samo delivery-id zostaje zachowane. Retry działają obecnie w procesie usługi i nie przeżywają redeploy; sprawdzaj również log dostarczania w Połączenia pod kątem pozostałych wpisów pending.
Najpierw zapisz delivery-id w tej samej transakcji co przetwarzanie, a następnie szybko odpowiedz 2xx. Wolne przetwarzanie przenieś do własnej kolejki.