Webhooks

Ta emot dina sessioners händelser i din backend: varje leverans är signerad med HMAC-SHA256, görs om med exponentiell backoff och bär ett unikt id så att din mottagare kan vara idempotent.

När du ska använda webhooks

För att reagera på det som händer i rummet utan att polla: plattformen anropar dig när en session startar eller avslutas, när en fråga stängs eller när dess resultat blir slutgiltiga. Leveransen sker minst en gång – den kan upprepas, den misslyckas aldrig tyst – och därför måste din mottagare vara idempotent.

Händelsekatalog

Levereras av API:et på /public/v1/limits – den här sidan har ingen egen kopia.

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

Headers i varje leverans

  • X-Votinova-EventHändelsetypen, exakt som den står i katalogen.
  • X-Votinova-Delivery-IdUnik per leverans – din dedupliceringsnyckel.
  • X-Votinova-TimestampUnix-tid i sekunder; en del av det som signeras, så en återuppspelad leverans kan inte få ny tidsstämpel.
  • X-Votinova-SignatureHMAC-SHA256 i hex, utan prefix.

Kroppen är kompakt JSON med händelsen och alla id:n som ingår – det här är, byte för byte, den signerade exempelleveransen:

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

Verifiera signaturen

Varje leverans signeras med HMAC-SHA256 över tidsstämpeln, en punkt och kroppen, med endpointens hemlighet som nyckel. Jämför resultatet med headern X-Votinova-Signature.

  • Signera den RÅA kroppen. Att parsa och serialisera om den ändrar blanktecknen, och då stämmer signaturen inte längre.
  • Jämför i konstant tid, inte med ==.
Verifierad mot en verklig leverans
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

Omförsök

Webhook-leveranser: upp till 8 försök, med växande väntetider på 21, 45, 95, 171, 369, 732, 1480 sekunder. När de är slut hamnar leveransen i dead letter-kön och kan skickas om för hand.

Idempotens

Använd X-Votinova-Delivery-Id som dedupliceringsnyckel: en leverans kan komma fram mer än en gång, och din effekt får bara ske en gång.

X-Votinova-Delivery-Id

Bästa praxis

  • Svara 200 så snart leveransen är säkrad och bearbeta den i din egen kö – en långsam mottagare bygger upp omförsök.
  • Verifiera ALLTID signaturen innan du parsar: utan den kan vem som helst som känner till din URL hitta på händelser.
  • Deduplicera på X-Votinova-Delivery-Id: minst en gång betyder att samma leverans kan anropa två gånger.
  • Efter 10 misslyckanden i rad pausar sig endpointen själv; slå på den igen från panelen eller via API:et när din mottagare är tillbaka.