API Documentation
Everything you need to integrate with PointPoker programmatically.
Table of Contents
Overview
The PointPoker API lets you create voting rooms, manage stories, and build integrations with your existing tools. It consists of a REST API for room management and Socket.io for real-time voting events.
Base URL
https://api.pointpoker.coDeep Links Base URL
https://pointpoker.co/voteAll REST responses use application/json. Errors return { "error": "message" } with an appropriate HTTP status code.
Authentication
No authentication is required for basic API usage. You can create rooms, join them, and participate in voting without any API key or token.
Pro features (room passwords, custom card decks, and themes) require a proToken in the request body. This is an HMAC-SHA256 signed token obtained through the PointPoker Pro subscription.
/api/roomsCreate a new voting room. Returns the room ID, a shareable room code, and a join URL. The room expires after 72 hours of inactivity.
Rate limit: 10 requests per 15 minutes per IP
Request Body
| Name | Type | Required | Description |
|---|---|---|---|
| facilitatorName | string | Yes | Display name of the facilitator (max 30 chars) |
| cardScale | "fibonacci" | "tshirt" | "simple" | "custom" | Yes | Voting scale to use |
| roles | string[] | Yes | Participant roles (1-4 items, each 1-20 chars) |
| anonymousVoting | boolean | Yes | Whether votes are anonymous until reveal |
| excludedFromEstimate | string[] | No | Roles to exclude from estimate calculations (must be subset of roles) |
| autoReveal | boolean | No | Auto-reveal votes when all participants have voted (default: false) |
| votingTimerSeconds | number | No | Countdown timer per story in seconds (15-300, omit for no timer) |
| facilitatorParticipates | boolean | No | Whether the facilitator can vote (default: false) |
| storyQueue | QueuedStory[] | No | Pre-loaded stories to vote on (max 50 items). Each item: { title: string (1-200), description?: string (max 1000), url?: string (max 500) } |
| customCardValues | string[] | No | Custom card values when cardScale is "custom" (2-15 items, each 1-10 chars). Pro only. |
| theme | "default" | "midnight" | "retro" | "neon" | No | Visual theme for the room. Pro only. |
| password | string | No | Room password (max 50 chars). Pro only. |
| proToken | string | No | HMAC-SHA256 signed token for Pro feature verification |
Response
| Name | Type | Required | Description |
|---|---|---|---|
| roomId | string | Yes | Unique room identifier (UUID) |
| roomCode | string | Yes | Short shareable room code |
| joinUrl | string | Yes | Full URL to join the room |
Example Response
{
"roomId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"roomCode": "ABC123",
"joinUrl": "https://pointpoker.co/room/a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}curl
curl -X POST https://api.pointpoker.co/api/rooms \
-H "Content-Type: application/json" \
-d '{
"facilitatorName": "Sarah",
"cardScale": "fibonacci",
"roles": ["Dev", "QE"],
"anonymousVoting": false
}'JavaScript
const response = await fetch("https://api.pointpoker.co/api/rooms", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
facilitatorName: "Sarah",
cardScale: "fibonacci",
roles: ["Dev", "QE"],
anonymousVoting: false,
}),
});
const { roomId, roomCode, joinUrl } = await response.json();Pro features (password, customCardValues, theme) require a valid proToken or will return 403.
The room code is a short alphanumeric string that participants can enter to join.
/api/rooms/code/:codeResolve a room code to its room ID. Used when participants join via room code instead of direct link.
Rate limit: 30 requests per minute per IP
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| code | string | Yes | The room code to resolve |
Response
| Name | Type | Required | Description |
|---|---|---|---|
| roomId | string | Yes | The room ID for the given code |
Example Response
{
"roomId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}curl
curl https://api.pointpoker.co/api/rooms/code/ABC123JavaScript
const response = await fetch("https://api.pointpoker.co/api/rooms/code/ABC123");
const { roomId } = await response.json();Returns 404 if the room code does not exist or the room has expired.
/api/rooms/:id/infoGet public metadata about a room. Used to display the join form with available roles and current participant count.
Rate limit: 30 requests per minute per IP
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| id | string | Yes | The room ID |
Response
| Name | Type | Required | Description |
|---|---|---|---|
| roomId | string | Yes | Room identifier |
| roomCode | string | Yes | Room code for sharing |
| roles | string[] | Yes | Available roles in this room |
| cardScale | string | Yes | Voting scale used in this room |
| participantCount | number | Yes | Current number of participants |
| maxParticipants | number | Yes | Maximum allowed participants (25) |
| passwordRequired | boolean | No | Whether a password is needed to join |
Example Response
{
"roomId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"roomCode": "ABC123",
"roles": ["Dev", "QE"],
"cardScale": "fibonacci",
"participantCount": 4,
"maxParticipants": 25,
"passwordRequired": false
}curl
curl https://api.pointpoker.co/api/rooms/a1b2c3d4-e5f6-7890-abcd-ef1234567890/infoJavaScript
const response = await fetch(
"https://api.pointpoker.co/api/rooms/a1b2c3d4-e5f6-7890-abcd-ef1234567890/info"
);
const roomInfo = await response.json();/healthHealth check endpoint. Returns server status, uptime in seconds, and current timestamp.
Response
| Name | Type | Required | Description |
|---|---|---|---|
| status | "ok" | Yes | Server status |
| uptime | number | Yes | Server uptime in seconds |
| timestamp | string | Yes | ISO 8601 timestamp |
Example Response
{
"status": "ok",
"uptime": 86400,
"timestamp": "2026-03-08T12:00:00.000Z"
}curl
curl https://api.pointpoker.co/healthJavaScript
const response = await fetch("https://api.pointpoker.co/health");
const { status, uptime } = await response.json();Platform Integration API
The Platform Integration API enables Slack, Microsoft Teams, and Discord bots to participate in PointPoker sessions via REST endpoints. This allows platform users to vote, reveal, and manage sessions without leaving their messaging app.
Feature flag: These endpoints require NEXT_PUBLIC_FEATURE_PLATFORM_INTEGRATIONS to be enabled. When disabled, all platform endpoints return 404.
Base Path
/api/platform/:roomIdAuthentication Model
Platform endpoints use a three-tier token model. Each token type has different permissions and is scoped to a specific room.
| Token | Obtained From | Permissions |
|---|---|---|
| apiToken | Pro subscription dashboard | Join rooms on behalf of platform users |
| facilitatorRestToken | Room creation response (facilitator only) | Reveal votes, end session, view status |
| participantToken | Join endpoint response | Submit votes, view status |
Pass tokens via the Authorization header: Bearer <token>
/api/platform/:roomId/joinJoin a room on behalf of a platform user (Slack, Teams, Discord). Creates a participant entry and returns tokens for subsequent actions.
Auth: Bearer apiToken
Rate limit: 10 requests per minute per API token
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| roomId | string | Yes | The room ID to join |
Request Body
| Name | Type | Required | Description |
|---|---|---|---|
| name | string | Yes | Display name of the participant (max 30 chars) |
| role | string | Yes | Participant role (must be in room's role list) |
| platformUserId | string | Yes | Unique user ID from the platform (e.g., Slack user ID) |
| platform | "slack" | "teams" | "discord" | Yes | Platform identifier |
| asObserver | boolean | No | Join as non-voting observer (default: false) |
Response
| Name | Type | Required | Description |
|---|---|---|---|
| participantId | string | Yes | Unique participant identifier |
| participantToken | string | Yes | Token for vote/status actions |
| roomCode | string | Yes | Room code for sharing |
Example Response
{
"participantId": "p_abc123",
"participantToken": "pt_xxxxxxxxxxxxxxxx",
"roomCode": "ABC123"
}curl
curl -X POST https://api.pointpoker.co/api/platform/a1b2c3d4-.../join \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-d '{
"name": "Sarah",
"role": "Dev",
"platformUserId": "U024BE7LH",
"platform": "slack"
}'JavaScript
const response = await fetch(
"https://api.pointpoker.co/api/platform/a1b2c3d4-.../join",
{
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: "Bearer YOUR_API_TOKEN",
},
body: JSON.stringify({
name: "Sarah",
role: "Dev",
platformUserId: "U024BE7LH",
platform: "slack",
}),
}
);
const { participantId, participantToken } = await response.json();The participantToken is scoped to this participant and room. Use it for vote and status requests.
If the platformUserId already has an active session in this room, the existing participant is returned.
/api/platform/:roomId/voteSubmit a vote on behalf of a platform participant. The vote value must be valid for the room's card scale.
Auth: Bearer participantToken
Rate limit: 20 requests per minute per participant token
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| roomId | string | Yes | The room ID |
Request Body
| Name | Type | Required | Description |
|---|---|---|---|
| value | string | Yes | Card value to vote (must be valid for room's scale) |
Response
| Name | Type | Required | Description |
|---|---|---|---|
| success | boolean | Yes | Whether the vote was accepted |
| voteCount | number | Yes | Total votes cast so far |
| totalEligible | number | Yes | Total eligible voters |
Example Response
{
"success": true,
"voteCount": 4,
"totalEligible": 6
}curl
curl -X POST https://api.pointpoker.co/api/platform/a1b2c3d4-.../vote \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_PARTICIPANT_TOKEN" \
-d '{ "value": "5" }'JavaScript
const response = await fetch(
"https://api.pointpoker.co/api/platform/a1b2c3d4-.../vote",
{
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: "Bearer YOUR_PARTICIPANT_TOKEN",
},
body: JSON.stringify({ value: "5" }),
}
);
const { success, voteCount, totalEligible } = await response.json();Returns 400 if the value is not in the room's card scale.
Returns 409 if voting is not currently active (no story set or votes already revealed).
/api/platform/:roomId/revealReveal all votes for the current story. Requires facilitator-level authorization.
Auth: Bearer facilitatorRestToken
Rate limit: 5 requests per minute per facilitator token
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| roomId | string | Yes | The room ID |
Response
| Name | Type | Required | Description |
|---|---|---|---|
| votes | VoteResult[] | Yes | Array of participant votes with names and values |
| overall | OverallResult | Yes | Aggregate stats: average, median, consensus |
| byRole | RoleResult[] | Yes | Per-role breakdown of votes |
Example Response
{
"votes": [
{ "participantName": "Sarah", "participantRole": "Dev", "value": "5" },
{ "participantName": "Mike", "participantRole": "QE", "value": "8" }
],
"overall": { "average": 6.5, "median": 6.5, "consensus": "discuss" },
"byRole": [
{ "role": "Dev", "count": 1, "votes": ["5"], "average": 5, "median": 5 },
{ "role": "QE", "count": 1, "votes": ["8"], "average": 8, "median": 8 }
]
}curl
curl -X POST https://api.pointpoker.co/api/platform/a1b2c3d4-.../reveal \
-H "Authorization: Bearer YOUR_FACILITATOR_TOKEN"JavaScript
const response = await fetch(
"https://api.pointpoker.co/api/platform/a1b2c3d4-.../reveal",
{
method: "POST",
headers: { Authorization: "Bearer YOUR_FACILITATOR_TOKEN" },
}
);
const { votes, overall, byRole } = await response.json();Only the room facilitator can reveal votes. Returns 403 for non-facilitator tokens.
If autoReveal is enabled, votes are revealed automatically when all eligible voters have voted.
/api/platform/:roomId/statusGet the current voting status of a room, including the active story, vote counts, and whether votes have been revealed.
Auth: Bearer participantToken | facilitatorRestToken
Rate limit: 30 requests per minute per token
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| roomId | string | Yes | The room ID |
Response
| Name | Type | Required | Description |
|---|---|---|---|
| currentStory | Story | null | Yes | Currently active story or null |
| phase | "waiting" | "voting" | "revealed" | Yes | Current voting phase |
| voteCount | number | Yes | Number of votes cast |
| totalEligible | number | Yes | Total eligible voters |
| participantCount | number | Yes | Total participants in room |
| revealResult | RevealResult | null | No | Vote results (only when phase is "revealed") |
Example Response
{
"currentStory": { "id": "s_123", "title": "PP-42 Login flow", "description": null, "url": null },
"phase": "voting",
"voteCount": 3,
"totalEligible": 5,
"participantCount": 6,
"revealResult": null
}curl
curl https://api.pointpoker.co/api/platform/a1b2c3d4-.../status \
-H "Authorization: Bearer YOUR_PARTICIPANT_TOKEN"JavaScript
const response = await fetch(
"https://api.pointpoker.co/api/platform/a1b2c3d4-.../status",
{ headers: { Authorization: "Bearer YOUR_PARTICIPANT_TOKEN" } }
);
const status = await response.json();/api/platform/:roomId/end-sessionEnd the voting session and retrieve the full session history. Requires facilitator-level authorization.
Auth: Bearer facilitatorRestToken
Rate limit: 2 requests per minute per facilitator token
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| roomId | string | Yes | The room ID |
Response
| Name | Type | Required | Description |
|---|---|---|---|
| history | CompletedStory[] | Yes | Full voting history for all stories in the session |
| totalStories | number | Yes | Number of stories voted on |
| sessionDuration | number | Yes | Session duration in seconds |
Example Response
{
"history": [
{
"id": "s_123",
"title": "PP-42 Login flow",
"finalEstimate": "5",
"wasRevoted": false,
"result": { "overall": { "average": 5, "median": 5, "consensus": "consensus" } },
"completedAt": "2026-03-20T10:30:00.000Z"
}
],
"totalStories": 1,
"sessionDuration": 1800
}curl
curl -X POST https://api.pointpoker.co/api/platform/a1b2c3d4-.../end-session \
-H "Authorization: Bearer YOUR_FACILITATOR_TOKEN"JavaScript
const response = await fetch(
"https://api.pointpoker.co/api/platform/a1b2c3d4-.../end-session",
{
method: "POST",
headers: { Authorization: "Bearer YOUR_FACILITATOR_TOKEN" },
}
);
const { history, totalStories, sessionDuration } = await response.json();This action is irreversible. The room will be closed and all participants disconnected.
The history array includes all stories, even those that were revoted.
Deep Links
Deep links let you send users directly into a voting session with a story pre-loaded. The user enters their name, picks a card scale, and starts voting immediately.
URL Format
https://pointpoker.co/vote?title=PP-123&desc=User+story&url=https://...Query Parameters
| Param | Required | Description | Example |
|---|---|---|---|
| title | Yes | Story title to vote on (max 200) | PP-123 User login flow |
| desc | No | Story description or acceptance criteria (max 1000) | As a user, I want to... |
| url | No | Link back to the ticket (must start with http:// or https://) (max 500) | https://jira.example.com/browse/PP-123 |
| scale | No | Card scale to use (default: fibonacci) | tshirt |
| timer | No | Voting timer in seconds (15-300) | 60 |
| source | No | Source identifier for tracking (e.g., jira, github, slack) (max 20) | jira |
Example
https://pointpoker.co/vote?title=PP-123%20User%20login%20flow&desc=As%20a%20user%2C%20I%20want%20to%20log%20in&url=https%3A%2F%2Fjira.example.com%2Fbrowse%2FPP-123&scale=fibonacci&timer=60&source=jiraSocket.io Events
PointPoker uses Socket.io for real-time communication. Connect to the server and emit/listen for events to participate in voting sessions.
Connection
import { io } from "socket.io-client";
const socket = io("https://api.pointpoker.co", {
transports: ["websocket"],
});
// Join a room
socket.emit("join-room", {
roomId: "a1b2c3d4-...",
name: "Sarah",
role: "Dev",
}, (response) => {
if (response.success) {
console.log("Joined!", response.state);
} else {
console.error(response.error);
}
});Client → Server
Events you emit to the server.
join-roomJoin a voting room. Returns full room state on success.
{
roomId: string; // Room ID to join
name: string; // Your display name
role: string; // Your role (must be in room's role list)
asObserver?: boolean; // Join as non-voting observer
reconnectToken?: string; // Token for reconnecting after disconnect
avatarSeed?: string; // Seed for avatar generation
password?: string; // Room password (if required)
}Returns a callback with { success, state, participantId, reconnectToken } or { success: false, error }.
submit-voteSubmit or change your vote for the current story.
{ value: string } // Must be a valid card value for the room's scaleIf autoReveal is enabled and all eligible voters have voted, votes are automatically revealed.
clear-voteClear your current vote.
set-storyFacilitator onlySet the current story for voting. Clears all votes and starts a new round.
{
title: string; // Story title (required)
description?: string; // Story description (max 1000 chars)
url?: string; // Link to ticket (max 500 chars)
}reveal-votesFacilitator onlyReveal all votes and compute statistics (average, median, consensus).
When the "open controls" feature is enabled, any non-observer participant can reveal.
next-storyFacilitator onlyMove to the next story. Archives the current story to session history.
revoteFacilitator onlyClear all votes and restart voting on the current story.
add-to-queueFacilitator onlyAdd a story to the voting queue.
{
title: string; // Story title (1-200 chars)
description?: string; // Description (max 1000 chars)
url?: string; // Ticket URL (max 500 chars)
}bulk-add-to-queueFacilitator onlyAdd multiple stories to the queue at once.
{ titles: string[] } // Array of story titles (max 50 items)start-next-from-queueFacilitator onlyStart voting on the next story in the queue.
remove-participantFacilitator onlyRemove (kick) a participant from the room.
{ participantId: string } // ID of participant to removetransfer-facilitatorFacilitator onlyTransfer facilitator role to another participant.
{ targetParticipantId: string }remove-from-queueFacilitator onlyRemove a story from the queue.
{ storyId: string }reorder-queueFacilitator onlyReorder stories in the queue by providing the new order.
{ orderedIds: string[] } // Story IDs in desired orderupdate-queue-itemFacilitator onlyUpdate the title, description, or URL of a queued story.
{
storyId: string;
title: string;
description?: string;
url?: string;
}skip-storyFacilitator onlySkip the current story mid-vote and return it to the front of the queue. Clears all votes.
Requires the skip-story feature flag. The skipped story goes back to the top of the queue so it can be revisited later.
start-from-queueFacilitator onlyStart voting on a specific story from the queue (not necessarily the next one).
{ storyId: string } // ID of the queued story to startThe current story (if revealed) is archived to history before the new story starts.
switch-to-voterSwitch an observer to a voting participant. Facilitators can switch any observer; non-facilitators can only switch themselves.
{
participantId: string; // ID of the observer to switch
role?: string; // Role to assign (must be in room's role list)
}Requires the observer-switch feature flag. The facilitator cannot be switched (they are never an observer).
set-final-estimateFacilitator onlyOverride the final estimate for the current story after votes are revealed.
{ value: string } // The final agreed-upon estimateupdate-configFacilitator onlyUpdate room settings mid-session (timer, auto-reveal, facilitator participation, etc.).
{
votingTimerSeconds?: number | null;
autoReveal?: boolean;
facilitatorParticipates?: boolean;
anonymousVoting?: boolean;
}extend-timerFacilitator onlyExtend the voting timer by the room's configured timer duration.
Only works when a timer is active and has expired or is about to expire.
mood-reactSend an emoji reaction visible to all participants. Used for quick team mood feedback.
{ emoji: string } // A single emoji characterAny participant (including observers) can react. Rate limited to 5 per 10 seconds.
update-participantUpdate your own participant data (e.g., display name).
{ name?: string }done-for-todayFacilitator onlyMark the session as done for today. Broadcasts the current history to all participants and shows a session summary screen. The room stays alive — participants can resume later.
Idempotent — calling it when already done is a no-op. The room TTL is not reset by this event; use resume-from-done to continue the session.
resume-from-doneFacilitator onlyResume a session that was marked as done for today, returning all participants to the active room view.
Idempotent — calling it when the session is not in a done state is a no-op.
end-sessionFacilitator onlyEnd the voting session. Broadcasts session history to all participants and closes the room.
Server → Client
Events you listen for from the server.
room-stateFull room state broadcast. Sent on join and after major state changes.
{
config: RoomConfig;
participants: Participant[];
currentStory: Story | null;
votingStatus: Record<string, boolean>;
revealResult: RevealResult | null;
history: CompletedStory[];
storyQueue: QueuedStory[];
finalEstimateOverride: string | null;
}participant-joinedA new participant joined the room.
{
id: string;
name: string;
role: string;
isFacilitator: boolean;
isObserver: boolean;
connectionStatus: "online" | "offline";
hasVoted: boolean;
}participant-leftA participant disconnected from the room.
{ participantId: string }participant-statusA participant's connection status changed (online/offline).
{
participantId: string;
status: "online" | "offline";
}participant-removedA participant was removed (kicked) from the room by the facilitator.
{ participantId: string }vote-submittedA participant submitted or changed their vote (value is hidden until reveal).
{ participantId: string }vote-clearedA participant cleared their vote.
{ participantId: string }votes-revealedVotes have been revealed. Includes per-vote data, averages, and consensus status.
{
votes: Array<{
participantId: string;
participantName: string;
participantRole: string;
value: string;
}>;
overall: {
average: number | null;
median: number | null;
consensus: "consensus" | "discuss";
};
byRole: Array<{
role: string;
count: number;
votes: string[];
average: number | null;
median: number | null;
}>;
hasRoleGap: boolean;
}voting-resetVoting was reset (revote). All votes are cleared and a new timer starts if configured.
{ timerEndsAt: string | null }story-changedThe current story has changed.
{
id: string;
title: string;
description: string | null;
url: string | null;
phase: "waiting" | "voting" | "revealed";
timerEndsAt: string | null;
}story-skippedThe current story was skipped and returned to the queue.
{
queue: Array<{
id: string;
title: string;
description: string | null;
url: string | null;
}>
}queue-updatedThe story queue has been modified (added, removed, or reordered).
{
queue: Array<{
id: string;
title: string;
description: string | null;
url: string | null;
}>
}facilitator-changedThe facilitator role was transferred to a different participant.
{ newFacilitatorId: string }final-estimate-changedThe facilitator set or changed the final estimate override for the current story.
{ value: string | null }timer-expiredThe voting timer has expired.
timer-expiringThe voting timer is about to expire. Shows current vote progress for the extend/reveal prompt.
{
votedCount: number;
totalCount: number;
}timer-extendedThe voting timer was extended by the facilitator.
{ timerEndsAt: string } // ISO 8601 timestampmood-reactionA participant sent an emoji reaction.
{
participantId: string;
participantName: string;
emoji: string;
}done-for-todayThe facilitator marked the session as done for today. All participants switch to the session summary screen.
{
history: Array<{
id: string;
title: string;
finalEstimate: string | null;
wasRevoted: boolean;
result: RevealResult;
completedAt: string;
}>
}session-resumedThe facilitator resumed a session that was done for today. All participants return to the active room view.
session-endedThe session has ended. Includes full voting history for all stories.
{
history: Array<{
id: string;
title: string;
finalEstimate: string | null;
wasRevoted: boolean;
result: RevealResult;
completedAt: string;
}>
}force-disconnectServer is forcing a disconnect (e.g., participant was kicked or room was closed).
{ reason: string }errorAn error occurred processing your request.
{ message: string }Rate Limits
Rate limits protect the service from abuse. REST endpoints return standard RateLimit-* headers (draft-7). Socket events that exceed limits emit an error event.
REST API
| Scope | Limit | Window |
|---|---|---|
| All /api/* endpoints | 100 requests | 1 minute per IP |
| POST /api/rooms | 10 requests | 15 minutes per IP |
| GET /api/rooms/code/:code | 30 requests | 1 minute per IP |
| GET /api/rooms/:id/info | 30 requests | 1 minute per IP |
Socket.io Events
| Scope | Limit | Window |
|---|---|---|
| Socket connections | 20 connections | 1 minute per IP |
| join-room | 3 events | 30 seconds per socket |
| submit-vote / clear-vote | 10 events | 10 seconds per socket |
| set-story | 5 events | 30 seconds per socket |
| reveal-votes / next-story / revote | 3 events | 30 seconds per socket |
| skip-story / start-from-queue / switch-to-voter | 3 events | 30 seconds per socket |
| remove-participant / transfer-facilitator | 3-5 events | 30 seconds per socket |
| add-to-queue / remove-from-queue / update-queue-item | 10 events | 30 seconds per socket |
| bulk-add-to-queue | 3 events | 30 seconds per socket |
| reorder-queue | 5 events | 10 seconds per socket |
| set-final-estimate / update-config | 5 events | 30 seconds per socket |
| extend-timer | 3 events | 30 seconds per socket |
| mood-react | 5 events | 10 seconds per socket |
| update-participant | 5 events | 30 seconds per socket |
| done-for-today / resume-from-done | 3 events | 30 seconds per socket |
| end-session | 2 events | 60 seconds per socket |
| Password attempts | 5 attempts | 15 minutes per IP:room |
Platform API
| Scope | Limit | Window |
|---|---|---|
| POST .../join | 10 requests | 1 minute per API token |
| POST .../vote | 20 requests | 1 minute per participant token |
| POST .../reveal | 5 requests | 1 minute per facilitator token |
| GET .../status | 30 requests | 1 minute per token |
| POST .../end-session | 2 requests | 1 minute per facilitator token |
Card Scales
Available card scales and their values. Use the scale ID when creating a room.
| ID | Name | Values |
|---|---|---|
| fibonacci | Fibonacci | 0, 1, 2, 3, 5, 8, 13, 21, ☕, ❓ |
| tshirt | T-Shirt | XS, S, M, L, XL, ☕, ❓ |
| simple | Simple (1-10) | 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ☕, ❓ |
| custom | Custom (Pro) | 2-15 user-defined values |
Error Handling
All errors follow a consistent format.
HTTP Errors
{
"error": "Human-readable error message"
}| Status | Meaning |
|---|---|
| 400 | Invalid request body or parameters |
| 403 | Pro feature used without valid Pro token |
| 404 | Room or resource not found |
| 429 | Rate limit exceeded |
Socket.io Errors
Socket errors are emitted via the error event with a { message: string } payload. Common errors include invalid permissions (non-facilitator attempting facilitator actions), rate limiting, and room state violations (e.g., voting when no story is set).
Need help? Open an issue on GitHub