Webhooks

Rep al teu backend els esdeveniments de les teves sessions: cada lliurament va signat amb HMAC-SHA256, es reintenta amb espera exponencial i porta un identificador únic perquè el teu receptor pugui ser idempotent.

Quan fer servir els webhooks

Per reaccionar al que passa a la sala sense fer sondeigs: la plataforma et truca quan una sessió comença o acaba, quan es tanca una pregunta o quan se'n tanquen els resultats. El lliurament és com a mínim un cop —es pot repetir, però no falla mai en silenci—, i per això el teu receptor ha de ser idempotent.

Catàleg d'esdeveniments

El serveix l'API a /public/v1/limits: aquesta pàgina no en guarda cap còpia.

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

Capçaleres de cada lliurament

  • X-Votinova-EventEl tipus d'esdeveniment, exactament tal com surt al catàleg.
  • X-Votinova-Delivery-IdÚnic per lliurament: la teva clau de desduplicació.
  • X-Votinova-TimestampSegons des de l'època; forma part del que se signa, així que un lliurament repetit no es pot redatar.
  • X-Votinova-SignatureHMAC-SHA256 en hexadecimal, sense prefix.

El cos és JSON compacte amb l'esdeveniment i tots els identificadors implicats; aquest és, byte a byte, el lliurament d'exemple signat:

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

Verificar la signatura

Cada lliurament se signa amb HMAC-SHA256 sobre la marca de temps, un punt i el cos, amb el secret de l'extrem com a clau. Compara el resultat amb la capçalera X-Votinova-Signature.

  • Signa el cos EN BRUT. Si l'analitzes i el tornes a serialitzar, canvien els espais i la signatura deixa de coincidir.
  • Compara en temps constant, no amb ==.
Verificat amb un lliurament real
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

Reintents

Lliuraments de webhook: fins a 8 intents, amb esperes creixents de 21, 45, 95, 171, 369, 732, 1480 segons. Un cop esgotats, el lliurament es descarta i es pot tornar a enviar a mà.

Idempotència

Fes servir X-Votinova-Delivery-Id com a clau de desduplicació: un lliurament pot arribar més d'un cop i el teu efecte només s'ha d'aplicar una vegada.

X-Votinova-Delivery-Id

Bones pràctiques

  • Respon 200 tan bon punt el lliurament estigui segur i processa'l a la teva pròpia cua: un receptor lent acumula reintents.
  • Verifica SEMPRE la signatura abans d'analitzar el cos: sense això, qualsevol que conegui la teva URL pot inventar-se esdeveniments.
  • Desduplica per X-Votinova-Delivery-Id: «com a mínim un cop» vol dir que el mateix lliurament et pot trucar dues vegades.
  • Després de 10 fallades seguides, l'extrem es posa en pausa tot sol; torna'l a activar des del tauler o per API quan el teu receptor torni a estar en línia.