Webhooks

Empfange die Events deiner Sessions in deinem Backend: Jede Zustellung ist HMAC-SHA256-signiert, wird mit exponentiellem Backoff wiederholt und trägt eine eindeutige ID, damit dein Empfänger idempotent sein kann.

Wann Webhooks

Um auf den Raum zu reagieren, ohne zu pollen: Die Plattform ruft dich, wenn eine Session startet oder endet, eine Frage schließt oder ihre Ergebnisse konsolidiert sind. Die Zustellung ist at-least-once — sie kann sich wiederholen, geht nie still verloren — deshalb muss dein Empfänger idempotent sein.

Event-Katalog

Von der API unter /public/v1/limits ausgeliefert — diese Seite hält keine Kopie.

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

Header jeder Zustellung

  • X-Votinova-EventDer Event-Typ, exakt wie im Katalog.
  • X-Votinova-Delivery-IdEindeutig pro Zustellung — dein Deduplizierungsschlüssel.
  • X-Votinova-TimestampEpoche in Sekunden; Teil des Signierten — eine wiederholte Zustellung kann nicht umdatiert werden.
  • X-Votinova-SignatureHMAC-SHA256 in Hex, ohne Präfix.

Der Body ist kompaktes JSON mit dem Event und allen beteiligten IDs — dies ist, Byte für Byte, die signierte Beispielzustellung:

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

Signatur prüfen

Jede Zustellung ist mit HMAC-SHA256 über Zeitstempel, Punkt und Body signiert, mit dem Secret des Endpunkts. Vergleichen Sie das Ergebnis mit dem Header X-Votinova-Signature.

  • Signieren Sie den ROHEN Body. Parsen und neu serialisieren ändert Leerzeichen, und die Signatur passt nicht mehr.
  • Vergleichen Sie in konstanter Zeit, nicht mit ==.
Gegen eine echte Zustellung verifiziert
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

Wiederholungen

Webhook-Zustellungen: bis zu 8 Versuche mit wachsenden Wartezeiten von 20, 41, 80, 185, 333, 669, 1413 Sekunden. Danach landet die Zustellung in der Dead-Letter-Liste und kann manuell erneut gesendet werden.

Idempotenz

Nutze X-Votinova-Delivery-Id als Deduplizierungsschlüssel: Eine Zustellung kann mehr als einmal ankommen, dein Effekt darf nur einmal wirken.

X-Votinova-Delivery-Id

Best Practices

  • Antworte 200, sobald die Zustellung sicher ist, und verarbeite in deiner Queue — ein langsamer Empfänger stapelt Wiederholungen.
  • Verifiziere IMMER die Signatur vor dem Parsen: Ohne sie kann jeder mit deiner URL Events erfinden.
  • Dedupliziere über X-Votinova-Delivery-Id: At-least-once heißt, dieselbe Zustellung kann zweimal anrufen.
  • Nach 10 Fehlern in Folge pausiert sich der Endpoint selbst; reaktiviere ihn im Workspace oder per API, sobald dein Empfänger zurück ist.