June 19, 2026 · 10 min read
Levando resultados de enquetes ao vivo para o nosso dashboard interno com webhooks
O guia de um desenvolvedor para consumir webhooks do PollsLive: verificar a assinatura HMAC, reagir a eventos vote.created e de sessão em tempo real, e recorrer ao endpoint de resultados como fallback.
Mantemos um dashboard fixado na parede do escritório que mostra o que está acontecendo pelo produto. Eu queria resultados de enquete ao vivo nele durante o all-hands da empresa - sem martelar a API num timer. Os webhooks do PollsLive transformaram isso em algumas horas de trabalho. Aqui vai a integração, verificação de assinatura incluída, porque você nunca deve confiar num webhook não verificado.
Tip
Você não precisa ser técnico para seguir este guia. Cada passo usa botões simples no PollsLive - sem código, sem instalar app para seu público.
Os eventos que você pode assinar
Você registra uma URL de endpoint e um conjunto de eventos em Studio → Developers. Os que me importam para um dashboard ao vivo:
- `session.started` / `session.ended` - mostrar ou desmontar o card ao vivo.
- `response.created` - um participante respondeu a um slide numa sessão ao vivo (o de alta frequência).
- `vote.created` - um novo voto numa enquete assíncrona.
- `poll.published` / `poll.closed` - ciclo de vida, útil para arquivar.
Toda entrega é um `POST` JSON com o mesmo envelope: um nome de `event`, um timestamp `createdAt` e um 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"
}
}Verifique a assinatura primeiro - sempre
Cada requisição carrega `X-PollsLive-Signature: t=<unix>,v1=<hex>`. O valor `v1` é um HMAC-SHA256 de `<t>.<rawBody>` usando o segredo de assinatura do seu endpoint. Você deve computá-lo sobre o corpo bruto da requisição (não o JSON parseado), comparar em tempo constante e rejeitar qualquer coisa em que o timestamp seja velho demais, para bloquear replays.
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);
}A única pegadinha no Express: você precisa dos bytes brutos, então registre o handler de verificação com `express.raw()` (ou capture o buffer) em vez de `express.json()` para essa rota. Depois confirme rápido - retorne `2xx` imediatamente e faça o trabalho de verdade de forma assíncrona, ou o PollsLive vai tratar uma resposta lenta como uma entrega que falhou.
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);Transformando eventos num card ao vivo
Não coloco as *contagens* de votos no payload do webhook de propósito - o evento é um empurrãozinho que diz "algo mudou", e meu worker faz debounce de uma rajada de eventos `response.created` (um por resposta pode ser muito durante um all-hands) num único refresh que chama `GET /polls/{id}/results`. Isso me mantém bem abaixo do limite de 120 requisições/min mesmo com uma enquete movimentada.
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
}A parede acaba mostrando o card ao vivo abaixo - alimentado por empurrõezinhos de webhook, não por um loop de polling. No último all-hands ele atualizava em menos de um segundo a cada voto e nunca estourou o rate limit:
Onde devemos fazer a hack week deste ano?
Sempre tenha um fallback
Webhooks podem atrasar ou ser perdidos - redes são redes. Então a mesma função `refresh()` roda num intervalo lento de segurança (a cada 30s) enquanto a sessão está ao vivo. Cinto e suspensório: webhooks para atualizações instantâneas, um polling gentil para que o card nunca fique errado por muito tempo. Se você só fizer uma coisa, faça a verificação; se fizer duas, adicione o fallback.
Trate o webhook como um sinal de 'vá olhar', não como a fonte da verdade. Verifique, faça debounce e então leia o endpoint de resultados - e você tem um dashboard em tempo real que também é resiliente.
A lista completa de eventos e o formato da assinatura estão no guia para desenvolvedores e na referência de API interativa. Se você está automatizando o lado da *criação* também, o artigo do meu colega sobre rodar retros a partir da API é a peça complementar.
Tip
Payloads de webhook incluem o snapshot completo - armazene, não consulte a API em timer.



Leia resultados no Studio com leia e exporte resultados de enquetes. Veja /integrations.
Desenvolva no PollsLive - crie enquetes, conduza sessões ao vivo, obtenha resultados e receba webhooks pelo seu próprio código.
Leia a documentação para desenvolvedores