Webhooki

Zdarzenia z Państwa sesji trafiają prosto do Państwa backendu: każde dostarczenie jest podpisane HMAC-SHA256, ponawiane z wykładniczo rosnącym odstępem i ma unikalny identyfikator, dzięki czemu odbiornik może być idempotentny.

Kiedy używać webhooków

Aby reagować na to, co dzieje się w pokoju, bez odpytywania: platforma sama wywołuje Państwa endpoint, gdy sesja się zaczyna lub kończy, gdy pytanie zostaje zamknięte albo gdy jego wyniki są finalizowane. Dostarczenie działa w trybie at-least-once – może się powtórzyć, nigdy nie zawodzi po cichu – i właśnie dlatego odbiornik musi być idempotentny.

Katalog zdarzeń

Udostępniany przez API pod /public/v1/limits – ta strona nie przechowuje kopii.

  • presentation.import.completed
  • presentation.import.failed
  • question.activated
  • question.closed
  • question.results.finalized
  • report.ready
  • session.ended
  • session.paused
  • session.resumed
  • session.started

Nagłówki w każdym dostarczeniu

  • X-Votinova-EventTyp zdarzenia, dokładnie tak jak w katalogu.
  • X-Votinova-Delivery-IdUnikalny dla każdego dostarczenia – Państwa klucz deduplikacji.
  • X-Votinova-TimestampSekundy epoki; objęte podpisem, więc powtórzonego dostarczenia nie da się przedatować.
  • X-Votinova-SignatureHMAC-SHA256 szesnastkowo, bez prefiksu.

Treść to zwarty JSON ze zdarzeniem i wszystkimi powiązanymi identyfikatorami – to jest, bajt w bajt, podpisane przykładowe dostarczenie:

{"event":"session.ended","session_id":"68b2e3d4c5f607182930a5bc"}

Weryfikacja podpisu

Każde dostarczenie jest podpisane HMAC-SHA256 z sygnatury czasowej, kropki i treści, a kluczem jest sekret endpointu. Wynik należy porównać z nagłówkiem X-Votinova-Signature.

  • Podpisywać należy SUROWĄ treść. Sparsowanie i ponowna serializacja zmieniają białe znaki i podpis przestaje się zgadzać.
  • Porównanie powinno przebiegać w stałym czasie, nie przez ==.
Zweryfikowano na rzeczywistym dostarczeniu
const crypto = require("node:crypto");

function verify(headers, rawBody, secret) {
  const timestamp = headers["x-votinova-timestamp"];
  const signature = headers["x-votinova-signature"];
  const expected = crypto
    .createHmac("sha256", secret)
    .update(`${timestamp}.${rawBody}`)
    .digest("hex");
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature));
}
secret     whsec_3f8a1c92d4e5b6a7
timestamp  1785321600
body       {"event":"session.ended","session_id":"68b2e3d4c5f607182930a5bc"}

HMAC-SHA256(secret, timestamp + "." + body)
= 10a86171bd92f5fcf8c28b873978e0214302cc2291c54cc016d18347f3ee7c38

Ponowienia

Dostarczenia webhooków: liczba prób maksymalnie 8, z rosnącymi odstępami 21, 45, 95, 171, 369, 732, 1480 s. Po ich wyczerpaniu dostarczenie trafia do martwej kolejki i można je ponowić ręcznie.

Idempotentność

Jako klucza deduplikacji należy używać X-Votinova-Delivery-Id: dostarczenie może przyjść więcej niż raz, a efekt po Państwa stronie musi zajść tylko raz.

X-Votinova-Delivery-Id

Dobre praktyki

  • Odpowiedź 200 należy wysyłać, gdy tylko dostarczenie jest bezpiecznie przyjęte, a przetwarzanie prowadzić we własnej kolejce – wolny odbiornik piętrzy ponowienia.
  • ZAWSZE należy zweryfikować podpis przed parsowaniem: bez tego każdy, kto zna Państwa adres URL, może zmyślać zdarzenia.
  • Deduplikacja po X-Votinova-Delivery-Id: at-least-once oznacza, że to samo dostarczenie może wywołać endpoint dwukrotnie.
  • Po 10 kolejnych niepowodzeniach endpoint sam się wstrzymuje; po przywróceniu odbiornika należy włączyć go ponownie w panelu albo przez API.