GET /ideabank/roadmap
List the idea bank’s published roadmap — the proposals that have been accepted (status accepted), newest-first. Only the fields needed to display a public roadmap are returned; scores, votes, comments, and internal identifiers are never exposed.
Unlike the changelog, the roadmap is cursor-paginated: fetch pages until the response omits the cursor, and (when configured) each item carries a whitelisted set of tags.
Capability Gate
This endpoint is gated by canViewRoadmap, which is true only when the idea bank is OPENand the roadmap has been enabled for it. When the gate is closed the endpoint responds with 404 (the idea bank is indistinguishable from one that does not exist).
canViewRoadmap is independent of proposal-viewing access. A panelist can be allowed to read the roadmap of an internal idea bank (one whose proposals, voting, and comments are not panelist-accessible) using the same token. The single compound token drives every Panelist API surface; the per-surface capabilities are reported by GET /ideabank.
Request
GET /public/ideabank/roadmap
Headers
| Header | Required | Description |
|---|---|---|
| Authorization | Yes | Compound token from Tenant API |
Query Parameters
| Parameter | Required | Description |
|---|---|---|
| limit | No | Page size. Defaults to 50, clamped to the range 1–100 |
| cursor | No | Opaque, encrypted token from a previous response. Omit for the first page |
Response
{
"items": [
{
"title": "Mobile app",
"description": "Native iOS and Android apps.",
"lastModified": 1718524800,
"tags": ["PLANNED"]
},
{
"title": "SSO support",
"description": "SAML and OIDC single sign-on.",
"lastModified": 1718265600,
"tags": ["IN-PROGRESS"]
}
],
"cursor": "k0Vns2dF...encrypted..."
}
Response Fields
| Field | Type | Description |
|---|---|---|
| items | array | Accepted proposals, newest-first (see below) |
| cursor | string | Encrypted token for the next page. Present only when more pages exist |
Item Object
| Field | Type | Description |
|---|---|---|
| title | string | Proposal title |
| description | string | Proposal description |
| lastModified | integer | Unix timestamp (seconds) of the proposal’s last activity |
| tags | array | (Optional) Whitelisted roadmap tags for this item. See Tags |
Only title, description, lastModified, and the whitelisted tags are returned. Proposal IDs, status, vote counts, comment counts, and scores are never included.
Ordering
Items are sorted by activity, newest-first (most recently accepted at the top).
Pagination
The roadmap uses cursor pagination:
- The
cursoris an encrypted, opaque token. Do not parse, decode, or construct it — pass it back verbatim on the next request. - The presence of
cursormeans more pages exist. There is no separatehasMoreflag. - On the last page the
cursorfield is absent. Keep fetching until it is missing.
To retrieve the entire roadmap, fetch the first page (no cursor), then keep requesting with the returned cursor until a response comes back without one.
const loadEntireRoadmap = async (publicKey) => {
const all = [];
let cursor;
do {
const params = cursor ? `?cursor=${encodeURIComponent(cursor)}` : '',
page = await fetch(`https://api.votito.com/public/ideabank/roadmap${params}`, {
headers: { 'Authorization': publicKey }
}).then((r) => r.json());
all.push(...page.items);
cursor = page.cursor;
} while (cursor);
return all;
};
Tags
Roadmap items can carry whitelisted tags (e.g. PLANNED, IN-PROGRESS, SHIPPED) so the public roadmap can be grouped into columns or sections.
- The set of tags allowed to surface is the idea bank’s roadmap tag whitelist, configured by the idea bank owner.
- An item’s
tagsarray is the intersection of the whitelist with the proposal’s own tags. Tags applied to a proposal that are not in the whitelist are withheld. - Tags are normalized to upper case and returned in the configured whitelist order — this is the order in which to render roadmap groups/columns.
- When the idea bank has no whitelist configured, items omit the
tagsfield entirely.
The configured whitelist (the group order) is published by the metadata endpoint, GET /ideabank, as its roadmapTags array. Read it once to lay out the roadmap’s group headers, then place each item under its matching tags.
Examples
cURL
# First page
curl -X GET \
"https://api.votito.com/public/ideabank/roadmap?limit=50" \
-H "Authorization: abc123:xyz789:ses456"
# Next page
curl -X GET \
"https://api.votito.com/public/ideabank/roadmap?cursor=k0Vns2dF...encrypted..." \
-H "Authorization: abc123:xyz789:ses456"
JavaScript
const loadRoadmapPage = async (publicKey, { limit, cursor } = {}) => {
const params = new URLSearchParams();
if (limit) {
params.set('limit', limit);
}
if (cursor) {
params.set('cursor', cursor);
}
const query = params.toString() ? `?${params}` : '',
response = await fetch(`https://api.votito.com/public/ideabank/roadmap${query}`, {
headers: { 'Authorization': publicKey }
});
if (!response.ok) {
throw new Error(`Failed to load roadmap: ${response.status}`);
}
return response.json();
};
// Usage - group items by their whitelisted tag
const { items } = await loadRoadmapPage(publicKey, { limit: 50 });
items.forEach((item) => {
const group = item.tags?.[0] || 'OTHER';
console.log(`[${group}] ${item.title}`);
});
Python
import requests
def load_roadmap_page(public_key, limit=None, cursor=None):
params = {}
if limit is not None:
params["limit"] = limit
if cursor is not None:
params["cursor"] = cursor
response = requests.get(
"https://api.votito.com/public/ideabank/roadmap",
headers={"Authorization": public_key},
params=params
)
response.raise_for_status()
return response.json()
# Usage - fetch every page until the cursor is absent
def load_entire_roadmap(public_key):
items, cursor = [], None
while True:
page = load_roadmap_page(public_key, cursor=cursor)
items.extend(page["items"])
cursor = page.get("cursor")
if not cursor:
return items
Error Handling
| Status | Error | Description |
|---|---|---|
| 401 | Unauthorized | Missing or invalid authorization token |
| 404 | idea-bank-not-found | Idea bank does not exist, is closed, or roadmap is disabled |
Notes
- Use GET /ideabank to read
canViewRoadmap, the optionalroadmapSubtitle, and theroadmapTagswhitelist (group order) before rendering a roadmap view. - Always treat
cursoras opaque; clamplimityourself to1–100if you expose it to end users — out-of-range values are clamped server-side.