June 18, 2026 · 10 min read
Wie wir unsere wöchentliche Retro mit der PollsLive-API auf Autopilot gestellt haben
Eine Schritt-für-Schritt-Anleitung für Entwickler: eine Retro-Umfrage erstellen, eine Live-Session öffnen und dann Ergebnisse samt CSV abrufen - alles aus einem kleinen Node-Skript per Cron. Echte /api/v1-Endpunkte, echte Payloads.
Ich bin Platform Engineer, und jeden Freitag habe ich früher zehn Minuten damit verbracht, dasselbe Retro-Deck von Hand zu bauen. Dieselben fünf Fragen, andere Woche. Genau die Art Aufgabe, die ein Skript erledigen sollte. Also habe ich das Ganze auf die PollsLive-API und einen Cron-Job umgestellt - die Umfrage erstellt sich jetzt selbst, eine Live-Session öffnet sich automatisch, und nach dem Standup landet eine CSV in unserem Slack. Hier ist genau, wie das geht, mit den echten Endpunkten und Payloads.
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.
Auth: ein einziger Bearer-Token
Ich habe in Studio → Developers einen Workspace-API-Key erzeugt (sie sehen aus wie `plv_live_…`) und ihn in die Umgebung des Skripts gelegt. Jede Anfrage ist einfach ein Bearer-Token - kein OAuth-Tanz für Server-zu-Server-Nutzung.
# 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).Schritt 1 - Die Retro-Umfrage erstellen
Eine Umfrage ist ein typisiertes Deck: `content.questions` ist ein Array von Slides, jedes mit einem `kind`-Diskriminator. Für die Retro nutze ich eine scale, ein als Cloud gerendertes open_ended und ein multiple_choice, um über den Fix abzustimmen. `POST /polls` mit `renderingMode: "LIVE"` gibt mir einen Entwurf zurück.
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" }
]
}
]
}
}'Die `201`-Antwort verpackt die Umfrage in einer `data`-Hülle - ich behalte `data.id` für den Rest des Ablaufs und `data.accessPin` / `data.shareUrl` für die Beitrittsdetails:
{
"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"
}
}Schritt 2 - Veröffentlichen, dann eine Live-Session öffnen
Das Veröffentlichen erstellt einen Snapshot des Inhalts, damit er sicher präsentiert werden kann. Ein `PATCH /polls/{id}` mit `publish: true` erledigt das; dann startet `POST /sessions` die Presenter-Session und gibt die Beitritts-PIN sowie einen einmaligen `hostToken` zurück (das Host-Authority-Secret - es wird nur beim Erstellen zurückgegeben, also speichere ich es sofort).
# 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" } }Mein Cron postet die `joinUrl` und PIN eine Minute vor dem Standup in den Team-Channel. Das Team scannt, und die Slides, die ich im Code gebaut habe, sind genau das, was sie sehen - hier ist das Live-Ergebnis der `health`-Slide, sobald alle geantwortet haben:
Wie gesund hat sich dieser Sprint angefühlt? (1 = mühsam, 5 = großartig)
Schritt 3 - Nach dem Standup Ergebnisse und eine CSV abrufen
Wenn die Session endet, gibt `GET /polls/{id}/results` die Auswertungen zurück - `totalVotes`, eine `voteCounts`-Map für Choice-Slides und ein `slides[]`-Array mit Aggregaten pro Typ (Skalen-Durchschnitte, gruppierter Freitext usw.).
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 } }
]
}
}Fürs Archiv streamt `GET /polls/{id}/export` eine RFC-4180-CSV (Slide-Nummer, Frage, Typ, Antwort-Label, Anzahl/Wert). Ich speichere sie und hänge sie an die Slack-Nachricht. Der ganze Schritt nach dem Standup ist eine kleine Funktion:
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,
};
}Das war's. Das Retro-Deck baut sich selbst, die Session öffnet sich pünktlich, und die Zusammenfassung plus CSV werden automatisch gepostet. Ich habe meine zehn Freitagsminuten zurück, und das Format ist jede Woche identisch - genau das, was man von einer Retro will.
Das Schönste daran: Die Slides, die ich in JSON beschreibe, sind dieselben Slides, die das Team sieht und über die es abstimmt. Kein Auseinanderdriften zwischen 'was das Skript gebaut hat' und 'was wir durchgeführt haben'.
Die vollständige Endpunkt-Referenz und die Schemas stehen in der interaktiven API-Referenz, und der erzählerische Schnellstart steht im Entwicklerleitfaden. Als Nächstes auf meiner Liste: Webhooks, damit die Slack-Zusammenfassung in dem Moment ausgelöst wird, in dem die Session endet, statt nach einem Timer - und genau das deckt der Webhook-Bericht ab.
Tip
Nutze einen Cron-Job, um das Retro-Deck der letzten Woche zu klonen und automatisch einen frischen Link zu veröffentlichen.


No-Code-Alternative: wiederkehrende Umfragen ohne Neuaufbau.
Entwickle auf PollsLive - erstelle Umfragen, führe Live-Sessions durch, rufe Ergebnisse ab und empfange Webhooks aus deinem eigenen Code.
Lies die Entwicklerdokumentation