Webhook

Ricevi gli eventi delle tue sessioni nel tuo backend: ogni consegna viaggia firmata HMAC-SHA256, viene ritentata con backoff esponenziale e porta un identificatore unico perché il tuo ricevitore sia idempotente.

Quando usare i webhook

Per reagire a ciò che accade in sala senza fare polling: la piattaforma ti chiama quando una sessione parte o finisce, una domanda chiude o i suoi risultati sono consolidati. La consegna è at-least-once — può ripetersi, mai perdersi in silenzio — perciò il tuo ricevitore deve essere idempotente.

Catalogo degli eventi

Servito dall'API su /public/v1/limits — questa pagina non ne tiene una copia.

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

Header di ogni consegna

  • X-Votinova-EventIl tipo di evento, come nel catalogo.
  • X-Votinova-Delivery-IdUnico per consegna — la tua chiave di deduplica.
  • X-Votinova-TimestampEpoca in secondi; firmata, quindi una consegna ripetuta non può essere ri-datata.
  • X-Votinova-SignatureHMAC-SHA256 in esadecimale, senza prefisso.

Il corpo è JSON compatto con l'evento e tutti gli id coinvolti — questo è, byte per byte, quello della consegna firmata dell'esempio:

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

Verificare la firma

Ogni consegna è firmata con HMAC-SHA256 su timestamp, un punto e il corpo, con il segreto dell'endpoint. Confronta il risultato con l'header X-Votinova-Signature.

  • Firma sul corpo GREZZO. Se lo parsifichi e lo riserializzi cambiano gli spazi e la firma non torna.
  • Confronta in tempo costante, non con ==.
Verificato contro una consegna reale
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

Retry

Consegne webhook: fino a 8 tentativi, con attese crescenti di 20, 41, 80, 185, 333, 669, 1413 secondi. Esauriti, la consegna finisce in coda morta e si può rinviare a mano.

Idempotenza

Usa X-Votinova-Delivery-Id come chiave di deduplica: una consegna può arrivare più di una volta e il tuo effetto deve applicarsi una sola.

X-Votinova-Delivery-Id

Buone pratiche

  • Rispondi 200 appena la consegna è al sicuro e processa nella tua coda — un ricevitore lento accumula retry.
  • Verifica SEMPRE la firma prima di fare il parsing: senza, chiunque conosca la tua URL può inventare eventi.
  • Deduplica con X-Votinova-Delivery-Id: at-least-once significa che la stessa consegna può chiamare due volte.
  • Dopo 10 fallimenti consecutivi l'endpoint si mette in pausa da solo; riattivalo dallo spazio o via API quando il ricevitore torna.