June 19, 2026 · 10 min read
Live-Umfrageergebnisse mit Webhooks in unser internes Dashboard leiten
Ein Entwicklerleitfaden zum Verarbeiten von PollsLive-Webhooks: die HMAC-Signatur verifizieren, in Echtzeit auf vote.created- und Session-Events reagieren und als Fallback den Results-Endpunkt abfragen.
Wir betreiben in unserem Büro ein an die Wand montiertes Dashboard, das zeigt, was über das Produkt hinweg passiert. Ich wollte während unserer Firmen-All-Hands Live-Umfrageergebnisse darauf haben - ohne die API per Timer zu bombardieren. PollsLive-Webhooks machten daraus ein paar Stunden Arbeit. Hier ist die Integration, inklusive Signaturprüfung, denn man sollte einem unverifizierten Webhook niemals trauen.
Tip
Du musst kein Technik-Profi sein, um dieser Anleitung zu folgen. Jeder Schritt nutzt normale Buttons in PollsLive - kein Code, keine App-Installation für dein Publikum.
Die Events, die du abonnieren kannst
Du registrierst eine Endpunkt-URL und eine Reihe von Events in Studio → Developers. Die, die mir für ein Live-Dashboard wichtig sind:
- `session.started` / `session.ended` - die Live-Kachel ein- oder ausblenden.
- `response.created` - ein Teilnehmer hat in einer Live-Session eine Slide beantwortet (das hochfrequente).
- `vote.created` - eine neue Stimme bei einer asynchronen Umfrage.
- `poll.published` / `poll.closed` - Lebenszyklus, praktisch fürs Archivieren.
Jede Zustellung ist ein JSON-`POST` mit derselben Hülle: einem `event`-Namen, einem `createdAt`-Zeitstempel und einem `data`-Objekt.
POST /webhooks/pollslive (from PollsLive → your server)
X-PollsLive-Signature: t=1750320000,v1=4f9a…c2
{
"event": "response.created",
"createdAt": "2026-06-19T16:00:00.000Z",
"data": {
"pollId": "poll_9aZ2kP",
"sessionId": "ses_4dF1",
"slideId": "fix",
"kind": "multiple_choice"
}
}Verifiziere die Signatur zuerst - immer
Jede Anfrage trägt `X-PollsLive-Signature: t=<unix>,v1=<hex>`. Der `v1`-Wert ist ein HMAC-SHA256 von `<t>.<rawBody>` mit dem Signing-Secret deines Endpunkts. Du musst ihn über den rohen Request-Body berechnen (nicht das geparste JSON), in konstanter Zeit vergleichen und alles ablehnen, bei dem der Zeitstempel zu alt ist, um Replays zu blockieren.
import crypto from "node:crypto";
const SECRET = process.env.POLLSLIVE_WEBHOOK_SECRET; // from Studio → Developers
export function verifyPollsLive(rawBody, header, toleranceSec = 300) {
// header looks like: "t=1750320000,v1=4f9a…c2"
const parts = Object.fromEntries(
header.split(",").map((kv) => kv.split("=")),
);
const t = Number(parts.t);
if (!t || Math.abs(Date.now() / 1000 - t) > toleranceSec) return false;
const expected = crypto
.createHmac("sha256", SECRET)
.update(`${t}.${rawBody}`)
.digest("hex");
const a = Buffer.from(expected);
const b = Buffer.from(parts.v1 ?? "");
return a.length === b.length && crypto.timingSafeEqual(a, b);
}Der eine Stolperstein in Express: Du brauchst die rohen Bytes, also registriere den Verify-Handler mit `express.raw()` (oder fange den Buffer ab) statt mit `express.json()` für diese Route. Dann bestätige schnell - gib sofort `2xx` zurück und erledige die eigentliche Arbeit asynchron, sonst behandelt PollsLive eine langsame Antwort als fehlgeschlagene Zustellung.
import express from "express";
import { verifyPollsLive } from "./verify.mjs";
const app = express();
app.post(
"/webhooks/pollslive",
express.raw({ type: "application/json" }),
(req, res) => {
const sig = req.get("X-PollsLive-Signature") ?? "";
if (!verifyPollsLive(req.body.toString("utf8"), sig)) {
return res.status(400).send("bad signature");
}
const evt = JSON.parse(req.body.toString("utf8"));
// ACK first, then process out of band.
res.sendStatus(200);
queue.add(evt); // e.g. push to a worker / pub-sub / websocket fan-out
},
);
app.listen(3000);Aus Events eine Live-Kachel machen
Ich packe absichtlich keine Stimmen*zahlen* in die Webhook-Payload - das Event ist ein Anstoß, der sagt "etwas hat sich geändert", und mein Worker entprellt einen Schwall von `response.created`-Events (eines pro Antwort kann während einer All-Hands eine Menge sein) zu einer einzigen Aktualisierung, die `GET /polls/{id}/results` aufruft. So bleibe ich selbst bei einer belebten Umfrage gut unter dem Limit von 120 Anfragen/Min.
const API = process.env.POLLSLIVE_API;
const auth = { Authorization: `Bearer ${process.env.POLLSLIVE_KEY}` };
const pending = new Map(); // pollId → timer
function onEvent(evt) {
if (evt.event === "session.ended") return teardownTile(evt.data.pollId);
const id = evt.data.pollId;
clearTimeout(pending.get(id));
pending.set(id, setTimeout(() => refresh(id), 750)); // debounce bursts
}
async function refresh(pollId) {
const res = await fetch(`${API}/polls/${pollId}/results`, { headers: auth });
if (res.status === 429) { // backed-off retry on rate limit
return setTimeout(() => refresh(pollId), 2000);
}
const { data } = await res.json();
pushToDashboard(pollId, data.voteCounts); // websocket → the wall display
}An der Wand erscheint am Ende die Live-Kachel unten - gespeist von Webhook-Anstößen, nicht von einer Polling-Schleife. Bei der letzten All-Hands aktualisierte sie sich innerhalb einer Sekunde nach jeder Stimme und löste das Rate-Limit nie aus:
Wohin sollen wir die Hack-Week dieses Jahr legen?
Hab immer einen Fallback
Webhooks können verzögert oder verloren gehen - Netzwerke sind Netzwerke. Also läuft dieselbe `refresh()`-Funktion auf einem langsamen Sicherheitsnetz-Intervall (alle 30 Sek.), solange eine Session live ist. Gürtel und Hosenträger: Webhooks für sofortige Updates, ein sanftes Polling, damit die Kachel nie lange falsch ist. Wenn du nur eines machst, mach die Verifizierung; wenn du zwei machst, ergänze den Fallback.
Behandle den Webhook als 'schau mal nach'-Signal, nicht als Quelle der Wahrheit. Verifiziere ihn, entpreller ihn, lies dann den Results-Endpunkt - und du bekommst ein Echtzeit-Dashboard, das obendrein robust ist.
Die vollständige Liste der Events und das Signaturformat stehen im Entwicklerleitfaden und der interaktiven API-Referenz. Wenn du auch die *Erstellungs*-Seite automatisierst, ist der Bericht meines Kollegen über Retros aus der API das passende Begleitstück.
Tip
Webhook-Payloads enthalten den vollen Ergebnis-Snapshot - speichere sie, polle die API nicht per Timer.



Lies Ergebnisse im Studio mit Umfrageergebnisse lesen und exportieren. Siehe /integrations.
Entwickle auf PollsLive - erstelle Umfragen, führe Live-Sessions durch, rufe Ergebnisse ab und empfange Webhooks aus deinem eigenen Code.
Lies die Entwicklerdokumentation