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
| Header | Required | Description |
|---|
| Authorization | Yes | Compound idea-bank token from Tenant API |
Query Parameters
| Parameter | Required | Description |
|---|
| sort | No | Sort order: recent (default) or votes |
| limit | No | Page size |
| cursor | No | Encrypted 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
| Field | Type | Description |
|---|
| items | array | List of proposal objects |
| cursor | string | (Optional) Encrypted cursor for the next page. Absent on last page. |
Proposal Object
| Field | Type | Description |
|---|
| proposalId | string | Unique proposal identifier (use in voting and comment endpoints) |
| title | string | Proposal title |
| description | string | Proposal description |
| votes | integer | Net vote score for the proposal |
| totalVotes | integer | Total number of votes cast on the proposal |
| tsCreated | integer | Unix timestamp (seconds) when the proposal was created |
| myVote | object | (Optional) The panelist’s own vote, present only when they have voted |
myVote Object
| Field | Type | Description |
|---|
| vote | integer | The panelist’s vote: 1 for, -1 against |
| isMutable | boolean | Whether the panelist may still change or remove this vote |
| tsVoted | integer | Unix 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.
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
| Status | Error | Description |
|---|
| 401 | Unauthorized | Missing or invalid token |
| 403 | Forbidden | Token expired or malformed |
| 404 | idea-bank-not-found | Idea bank is closed or not panelist-accessible |
Notes
- The cursor is encrypted; do not parse it or construct cursors yourself.
- When the panelist lacks
canViewProposals, the endpoint returns { "items": [] } rather than a 404. myVote is merged per request, so it always reflects the panelist’s latest vote at read time.