GET /ideabank/proposals

List the open proposals in an idea bank. The response is paginated with an encrypted cursor and, when the panelist can vote, each proposal is merged with the panelist’s own vote on that proposal.

This endpoint requires the canViewProposals capability (see Get Idea Bank). When the panelist cannot view proposals, the endpoint returns an empty items array.

Request

GET /public/ideabank/proposals

Headers

HeaderRequiredDescription
AuthorizationYesCompound idea-bank token from Tenant API

Query Parameters

ParameterRequiredDescription
sortNoSort order: recent (default) or votes
limitNoPage size
cursorNoEncrypted cursor from a previous response; omit for the first page

Response

{
	"items": [
		{
			"proposalId": "p-abc123",
			"title": "Dark mode",
			"description": "Please add a dark theme.",
			"votes": 12,
			"totalVotes": 15,
			"tsCreated": 1700000000,
			"myVote": {
				"vote": 1,
				"isMutable": true,
				"tsVoted": 1700001000
			}
		}
	],
	"cursor": "encrypted-cursor-token"
}

Response Fields

FieldTypeDescription
itemsarrayList of proposal objects
cursorstring(Optional) Encrypted cursor for the next page. Absent on last page.

Proposal Object

FieldTypeDescription
proposalIdstringUnique proposal identifier (use in voting and comment endpoints)
titlestringProposal title
descriptionstringProposal description
votesintegerNet vote score for the proposal
totalVotesintegerTotal number of votes cast on the proposal
tsCreatedintegerUnix timestamp (seconds) when the proposal was created
myVoteobject(Optional) The panelist’s own vote, present only when they have voted

myVote Object

FieldTypeDescription
voteintegerThe panelist’s vote: 1 for, -1 against
isMutablebooleanWhether the panelist may still change or remove this vote
tsVotedintegerUnix timestamp (seconds) when the panelist cast this vote

The myVote field is only merged into proposals when the panelist has the canVote capability. If the panelist has not voted on a proposal, myVote is absent for that proposal.

Pagination

Pages are traversed with an opaque, encrypted cursor. Request the first page without a cursor, then pass the cursor from each response into the next request until the response omits cursor, which signals the last page.

let cursor = undefined;
do {
	const url = new URL('https://api.votito.com/public/ideabank/proposals');
	url.searchParams.set('sort', 'votes');
	if (cursor) {
		url.searchParams.set('cursor', cursor);
	}
	const page = await fetch(url, { headers: { 'Authorization': publicKey } }).then((r) => r.json());
	page.items.forEach(renderProposal);
	cursor = page.cursor;
} while (cursor);

Examples

cURL

curl -X GET \
  "https://api.votito.com/public/ideabank/proposals?sort=votes" \
  -H "Authorization: abc123:xyz789:ses456"

JavaScript

const listProposals = async (publicKey, { sort = 'recent', limit, cursor } = {}) => {
	const url = new URL('https://api.votito.com/public/ideabank/proposals');
	url.searchParams.set('sort', sort);
	if (limit) {
		url.searchParams.set('limit', String(limit));
	}
	if (cursor) {
		url.searchParams.set('cursor', cursor);
	}

	const response = await fetch(url, {
		headers: { 'Authorization': publicKey }
	});

	if (!response.ok) {
		throw new Error(`Failed to list proposals: ${response.status}`);
	}

	return response.json();
};

// Usage
const firstPage = await listProposals(publicKey, { sort: 'votes' });
firstPage.items.forEach((p) => console.log(`${p.title}: ${p.votes}`));

Python

import requests

def list_proposals(public_key, sort="recent", limit=None, cursor=None):
    params = {"sort": sort}
    if limit:
        params["limit"] = limit
    if cursor:
        params["cursor"] = cursor
    response = requests.get(
        "https://api.votito.com/public/ideabank/proposals",
        headers={"Authorization": public_key},
        params=params
    )
    response.raise_for_status()
    return response.json()

# Usage
page = list_proposals(public_key, sort="votes")
for proposal in page["items"]:
    print(proposal["title"], proposal["votes"])

Error Handling

StatusErrorDescription
401UnauthorizedMissing or invalid token
403ForbiddenToken expired or malformed
404idea-bank-not-foundIdea bank is closed or not panelist-accessible

Notes