June 19, 2026 · 10 min read
Cómo llevar resultados de encuestas en vivo a nuestro dashboard interno con webhooks
La guía de un desarrollador para consumir webhooks de PollsLive: verifica la firma HMAC, reacciona a eventos vote.created y de sesión en tiempo real, y usa el endpoint de resultados como respaldo.
Tenemos un dashboard montado en la pared de la oficina que muestra lo que pasa en todo el producto. Quería resultados de encuestas en vivo en él durante el all-hands de la empresa - sin machacar la API con un temporizador. Los webhooks de PollsLive lo convirtieron en un par de horas de trabajo. Aquí va la integración, con verificación de firma incluida, porque nunca deberías confiar en un webhook sin verificar.
Tip
You do not need to be technical to follow this guide. Every step uses plain buttons in PollsLive - no coding, no app install for your audience.
Los eventos a los que te puedes suscribir
Registras una URL de endpoint y un conjunto de eventos en Studio → Developers. Los que me importan para un dashboard en vivo:
- `session.started` / `session.ended` - muestra o desmonta el panel en vivo.
- `response.created` - un participante respondió una diapositiva en una sesión en vivo (el de alta frecuencia).
- `vote.created` - un nuevo voto en una encuesta asíncrona.
- `poll.published` / `poll.closed` - ciclo de vida, útil para archivar.
Cada entrega es un `POST` JSON con el mismo sobre: un nombre de `event`, una marca de tiempo `createdAt` y un objeto `data`.
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"
}
}Verifica la firma primero - siempre
Cada petición lleva `X-PollsLive-Signature: t=<unix>,v1=<hex>`. El valor `v1` es un HMAC-SHA256 de `<t>.<rawBody>` usando el secreto de firma de tu endpoint. Debes calcularlo sobre el cuerpo crudo de la petición (no el JSON parseado), compararlo en tiempo constante, y rechazar todo aquello cuya marca de tiempo sea demasiado antigua para bloquear repeticiones.
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);
}El único detalle en Express: necesitas los bytes crudos, así que registra el handler de verificación con `express.raw()` (o captura el buffer) en lugar de `express.json()` para esa ruta. Luego confirma rápido - devuelve `2xx` de inmediato y haz el trabajo real de forma asíncrona, o PollsLive tratará una respuesta lenta como una entrega fallida.
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);Convertir eventos en un panel en vivo
A propósito no pongo *recuentos* de votos en el payload del webhook - el evento es un empujón que dice "algo cambió", y mi worker amortigua una ráfaga de eventos `response.created` (uno por respuesta puede ser mucho durante un all-hands) en un solo refresco que llama a `GET /polls/{id}/results`. Eso me mantiene bien por debajo del límite de 120 peticiones/min incluso con una encuesta concurrida.
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
}La pared acaba mostrando el panel en vivo de abajo - alimentado por empujones de webhook, no por un bucle de sondeo. Durante el último all-hands se actualizaba en menos de un segundo tras cada voto y nunca disparó el límite de tasa:
¿Dónde deberíamos poner la hack week de este año?
Ten siempre un respaldo
Los webhooks pueden retrasarse o perderse - las redes son las redes. Así que la misma función `refresh()` corre con un intervalo de red de seguridad lento (cada 30s) mientras una sesión está en vivo. Con tirantes y cinturón: webhooks para actualizaciones instantáneas, un sondeo suave para que el panel nunca esté mal por mucho tiempo. Si solo haces una cosa, haz la verificación; si haces dos, añade el respaldo.
Trata el webhook como una señal de 've a mirar', no como la fuente de la verdad. Verifícalo, amortígualo y luego lee el endpoint de resultados - y obtienes un dashboard en tiempo real que además es resistente.
La lista completa de eventos y el formato de la firma están en la guía para desarrolladores y en la referencia de la API interactiva. Si también estás automatizando la parte de *creación*, el artículo de mi colega sobre ejecutar retros desde la API es la pieza complementaria.
Tip
Webhook payloads include the full result snapshot - store them, do not poll the API on a timer.



Read results in Studio with read and export poll results. See /integrations.
Construye sobre PollsLive - crea encuestas, dirige sesiones en vivo, descarga resultados y recibe webhooks desde tu propio código.
Lee la documentación para desarrolladores