POST /ideabank/proposals/{proposalId}/comments
Post a comment on a proposal on behalf of a panelist. Requires the canComment capability, which is only granted by the community_forum access level (see Get Idea Bank). Comment text is stripped of HTML and length-limited.
To read existing comments, use List Comments.
Request
POST /public/ideabank/proposals/{proposalId}/comments
Path Parameters
| Parameter | Type | Description |
|---|---|---|
| proposalId | string | The proposal identifier from the proposal listing |
Headers
| Header | Required | Description |
|---|---|---|
| Authorization | Yes | Compound idea-bank token from Tenant API |
| Content-Type | Yes | application/json |
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
| text | string | Yes | The comment text. HTML is stripped; the result must be non-empty. |
| contactInfo | string | No | Panelist contact value, stored when the idea bank collects it |
| contactInfoLabel | string | No | Label identifying the kind of contact info (matches contactInfoCollection.label) |
{
"text": "Great idea, I would use this daily."
}
contactInfo is only stored when both contactInfo and contactInfoLabel are supplied.
Response
{
"commentId": "c-abc123",
"text": "Great idea, I would use this daily.",
"tsCreated": 1700000000
}
Response Fields
| Field | Type | Description |
|---|---|---|
| commentId | string | Unique identifier of the created comment |
| text | string | The stored comment text (HTML stripped) |
| tsCreated | integer | Unix timestamp (seconds) when the comment was posted |
Examples
cURL
curl -X POST \
"https://api.votito.com/public/ideabank/proposals/p-abc123/comments" \
-H "Authorization: abc123:xyz789:ses456" \
-H "Content-Type: application/json" \
-d '{"text":"Great idea, I would use this daily."}'
JavaScript
const addComment = async (publicKey, proposalId, text) => {
const response = await fetch(`https://api.votito.com/public/ideabank/proposals/${proposalId}/comments`, {
method: 'POST',
headers: {
'Authorization': publicKey,
'Content-Type': 'application/json'
},
body: JSON.stringify({ text })
});
if (!response.ok) {
throw new Error(`Failed to add comment: ${response.status}`);
}
return response.json();
};
// Usage
const comment = await addComment(publicKey, 'p-abc123', 'Great idea!');
console.log(`Created comment ${comment.commentId}`);
Python
import requests
def add_comment(public_key, proposal_id, text, contact_info=None, contact_info_label=None):
body = {"text": text}
if contact_info and contact_info_label:
body["contactInfo"] = contact_info
body["contactInfoLabel"] = contact_info_label
response = requests.post(
f"https://api.votito.com/public/ideabank/proposals/{proposal_id}/comments",
headers={
"Authorization": public_key,
"Content-Type": "application/json"
},
json=body
)
response.raise_for_status()
return response.json()
# Usage
comment = add_comment(public_key, "p-abc123", "Great idea!")
print(comment["commentId"])
Error Handling
| Status | Error | Description |
|---|---|---|
| 400 | field-required | text is missing or empty after HTML is stripped |
| 400 | text-exceeds-maximum-length | text is longer than the allowed maximum. details.maxLength gives the limit. |
| 401 | Unauthorized | Missing or invalid token |
| 403 | Forbidden | Token expired, or the panelist cannot comment |
| 404 | idea-bank-not-found | Comments not enabled, proposal not open, or idea bank not accessible |
| 429 | resource-busy | Server is busy. Retry after a short delay. |
Notes
- Comments are only available when the idea bank’s access level is
community_forum(canCommentis true). - The comment text is stripped of HTML before storage; a comment that is empty after stripping is rejected with
field-required. - If the target proposal was merged into another, the server resolves the effective open proposal and attaches the comment there.