June 18, 2026 · 10 min read
Comment on a mis notre rétro hebdomadaire en pilote automatique avec l'API PollsLive
Le guide d'une développeuse : créer un sondage de rétro, ouvrir une session live, puis récupérer les résultats et un CSV - le tout depuis un petit script Node sur un cron. De vrais endpoints /api/v1, de vrais payloads.
Je suis ingénieure plateforme, et chaque vendredi je passais dix minutes à reconstruire à la main le même deck de rétro. Les cinq mêmes questions, une semaine différente. Typiquement le genre de truc qu'un script devrait faire. Alors je l'ai basculé sur l'API PollsLive et un cron - le sondage se crée désormais tout seul, une session live s'ouvre automatiquement, et après le standup un CSV atterrit dans notre Slack. Voici exactement comment, avec les vrais endpoints et payloads.
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.
Auth : un seul token Bearer
J'ai généré une clé d'API d'espace de travail dans Studio → Developers (elles ressemblent à `plv_live_…`) et je l'ai placée dans l'environnement du script. Chaque requête n'est qu'un token Bearer - pas de valse OAuth pour un usage serveur à serveur.
# Base URL for all calls
export POLLSLIVE_API=https://pollslive.com/api/v1
export POLLSLIVE_KEY=plv_live_xxxxxxxxxxxxxxxxxxxx
# Every request carries the key as a Bearer token:
# Authorization: Bearer $POLLSLIVE_KEY
# Rate limit: 120 requests/min per key (HTTP 429 if you exceed it).Étape 1 - Créer le sondage de rétro
Un sondage est un deck typé : `content.questions` est un tableau de slides, chacune avec un discriminant `kind`. Pour la rétro j'utilise une scale, une open_ended rendue en nuage, et une multiple_choice pour voter sur le correctif. `POST /polls` avec `renderingMode: "LIVE"` me renvoie un brouillon.
curl -sS -X POST "$POLLSLIVE_API/polls" \
-H "Authorization: Bearer $POLLSLIVE_KEY" \
-H "Content-Type: application/json" \
-d '{
"title": "Weekly retro - week of Jun 16",
"renderingMode": "LIVE",
"content": {
"questions": [
{
"id": "health",
"kind": "scale",
"text": "How healthy did this sprint feel?",
"min": 1, "max": 5,
"minLabel": "Rough", "maxLabel": "Great"
},
{
"id": "blockers",
"kind": "open_ended",
"text": "What slowed us down this sprint?",
"render": "cloud",
"maxLength": 120
},
{
"id": "fix",
"kind": "multiple_choice",
"type": "single",
"text": "What should we fix first next sprint?",
"options": [
{ "id": "ci", "label": "Stabilise the CI pipeline" },
{ "id": "tickets", "label": "Tighter ticket acceptance criteria" },
{ "id": "wip", "label": "Cap WIP and reviews SLA" },
{ "id": "meetings", "label": "Trim the meeting load" }
]
}
]
}
}'La réponse `201` enveloppe le sondage dans une enveloppe `data` - je garde `data.id` pour la suite du flux, et `data.accessPin` / `data.shareUrl` pour les détails de connexion :
{
"data": {
"id": "poll_9aZ2kP",
"slug": "weekly-retro-jun-16",
"title": "Weekly retro - week of Jun 16",
"renderingMode": "LIVE",
"isPublished": false,
"accessPin": "6098",
"shareUrl": "https://pollslive.com/p/weekly-retro-jun-16",
"createdAt": "2026-06-16T08:00:11.204Z"
}
}Étape 2 - Publier, puis ouvrir une session live
Publier fige le contenu pour qu'il soit sûr à présenter. Un `PATCH /polls/{id}` avec `publish: true` fait l'affaire ; puis `POST /sessions` lance la session présentateur et renvoie le PIN de connexion plus un `hostToken` à usage unique (le secret d'autorité hôte - il n'est renvoyé qu'à la création, alors je le stocke immédiatement).
# Publish the draft
curl -sS -X PATCH "$POLLSLIVE_API/polls/poll_9aZ2kP" \
-H "Authorization: Bearer $POLLSLIVE_KEY" \
-H "Content-Type: application/json" \
-d '{ "publish": true }'
# Open a presenter-paced live session
curl -sS -X POST "$POLLSLIVE_API/sessions" \
-H "Authorization: Bearer $POLLSLIVE_KEY" \
-H "Content-Type: application/json" \
-d '{ "pollId": "poll_9aZ2kP", "pace": "PRESENTER" }'
# → { "data": { "id": "ses_4dF1", "pin": "6098",
# "joinUrl": "https://pollslive.com/join/6098",
# "hostUrl": "https://pollslive.com/host/ses_4dF1",
# "hostToken": "hst_…", "status": "LOBBY" } }Mon cron poste le `joinUrl` et le PIN dans le canal de l'équipe une minute avant le standup. L'équipe scanne, et les slides que j'ai construites en code sont ce qu'ils voient - voici le résultat live de la slide `health` une fois que tout le monde a répondu :
À quel point ce sprint a-t-il semblé sain ? (1 = difficile, 5 = excellent)
Étape 3 - Récupérer les résultats et un CSV après le standup
Quand la session se termine, `GET /polls/{id}/results` renvoie les décomptes - `totalVotes`, une map `voteCounts` pour les slides à choix, et un tableau `slides[]` avec des agrégats par type (moyennes de scale, texte libre regroupé, etc.).
curl -sS "$POLLSLIVE_API/polls/poll_9aZ2kP/results" \
-H "Authorization: Bearer $POLLSLIVE_KEY"
{
"data": {
"pollId": "poll_9aZ2kP",
"title": "Weekly retro - week of Jun 16",
"closed": true,
"totalVotes": 9,
"voteCounts": { "ci": 4, "tickets": 3, "wip": 2, "meetings": 0 },
"slides": [
{ "slideId": "health", "kind": "scale", "average": 2.9 },
{ "slideId": "blockers", "kind": "open_ended", "responseCount": 14 },
{ "slideId": "fix", "kind": "multiple_choice",
"optionCounts": { "ci": 4, "tickets": 3, "wip": 2, "meetings": 0 } }
]
}
}Pour l'archive, `GET /polls/{id}/export` diffuse un CSV RFC-4180 (numéro de slide, question, type, libellé de réponse, décompte/valeur). Je l'enregistre et je le joins au message Slack. Toute l'étape post-standup tient en une petite fonction :
const API = process.env.POLLSLIVE_API;
const KEY = process.env.POLLSLIVE_KEY;
const auth = { Authorization: `Bearer ${KEY}` };
export async function summariseRetro(pollId) {
const res = await fetch(`${API}/polls/${pollId}/results`, { headers: auth });
if (res.status === 429) throw new Error("rate_limited"); // back off + retry
if (!res.ok) throw new Error(`results ${res.status}`);
const { data } = await res.json();
const winner = Object.entries(data.voteCounts)
.sort((a, b) => b[1] - a[1])[0];
// Grab the CSV for the archive
const csv = await fetch(`${API}/polls/${pollId}/export`, { headers: auth })
.then((r) => r.text());
return {
health: data.slides.find((s) => s.slideId === "health")?.average,
topFix: winner?.[0],
voters: data.totalVotes,
csv,
};
}Et voilà tout. Le deck de rétro se construit tout seul, la session s'ouvre à l'heure, et le résumé + le CSV se postent automatiquement. J'ai récupéré mes dix minutes du vendredi, et le format est identique chaque semaine - ce qui est exactement ce qu'on attend d'une rétro.
Le plus chouette : les slides que je décris en JSON sont exactement celles que l'équipe voit et sur lesquelles elle vote. Aucun décalage entre « ce que le script a fabriqué » et « ce qu'on a lancé ».
La référence complète des endpoints et les schémas sont dans la référence d'API interactive, et le démarrage rapide narratif est dans le guide développeur. Prochain sur ma liste : les webhooks, pour que le résumé Slack se déclenche à l'instant où la session se termine plutôt que sur une minuterie - ce que couvre justement l'article sur les webhooks.
Tip
Use a cron job to clone last week's retro deck and publish a fresh link automatically.


No-code alternative: recurring polls without rebuilding.
Construisez sur PollsLive - créez des sondages, pilotez des sessions live, récupérez des résultats et recevez des webhooks depuis votre propre code.
Lire la doc développeur