API Documentation
The VoteShip REST API v1 lets you programmatically manage posts, votes, comments, tags, users, and more.
https://app.voteship.app/api/v1All API endpoints are relative to this base URL.
MCP Server
The VoteShip MCP (Model Context Protocol) server lets AI coding assistants interact with your feedback board directly. Install it in Claude Code, Cursor, or Windsurf to list posts, triage feedback, plan sprints, and more -- all from your IDE.
claude_desktop_config.json or .cursor/mcp.json). Replace sk_... with your project's API secret key from the Share & Embed settings page.{
"mcpServers": {
"voteship": {
"command": "npx",
"args": ["@voteship/mcp-server"],
"env": { "VOTESHIP_API_KEY": "sk_..." }
}
}
}| Name | Description |
|---|---|
| list_posts | List and filter feature request posts |
| get_post | Get a single post by ID with full details |
| create_post | Create a new feature request post |
| update_post | Update an existing post (title, description, status, tags) |
| delete_post | Permanently delete a post |
| search_similar | Find semantically similar posts using AI embeddings |
| add_vote | Add a vote to a post on behalf of a user |
| get_voters | List all voters for a given post |
| add_comment | Add a public comment or internal note to a post |
| get_comments | List comments on a post (public, internal, or both) |
| delete_comment | Delete a comment from a post |
| list_tags | List all tags in the project |
| create_tag | Create a new tag with label and color |
| list_users | List board users who have interacted with the project |
| update_user | Update a board user's spend/revenue, name, or email |
| get_roadmap | Get the public roadmap grouped by status |
| get_analytics | Retrieve project analytics and daily stats |
| list_releases | List published changelog releases |
| create_release | Create a new changelog release |
| submit_feedback | Submit feedback from natural language text |
| triage_inbox | AI-powered triage of pending posts (tag, deduplicate, prioritise) |
| get_summary | Generate an AI summary of recent feedback trends |
| plan_sprint | AI-generated sprint plan based on votes, effort, and themes |
| sync_stripe_revenue | Sync customer MRR from connected Stripe account to board users |
| configure_webhook | Create or update a webhook endpoint |
| URI | Description |
|---|---|
| voteship://project/overview | Project name, description, plan, and key metrics |
| voteship://project/board | Full list of posts with vote counts and statuses |
| voteship://project/roadmap | Public roadmap items grouped by status column |
| voteship://project/changelog | Published changelog releases with linked posts |
| voteship://project/analytics | Daily stats, top posts, and trend data |
| Name | Description |
|---|---|
| triage_inbox | Review pending posts, auto-tag, detect duplicates, and suggest statuses |
| sprint_planning | Generate a prioritised sprint plan based on votes, effort, and themes |
| generate_changelog | Draft a changelog release from recently completed posts |
| feedback_summary | Summarise recent feedback trends, sentiment, and top requests |
Authentication
All API requests require a Bearer token. You can find your API secret key in your project's Share & Embed settings page.
curl https://app.voteship.app/api/v1/posts \
-H "Authorization: Bearer YOUR_API_SECRET_KEY"Include the header Authorization: Bearer { apiSecretKey } on every request.
Rate Limits
| Plan | Requests / minute | Requests / day |
|---|---|---|
| Free | 100 | 1,000 |
| Starter | 300 | 5,000 |
| Growth | 600 | 25,000 |
| Pro | 1,000 | 100,000 |
Rate-limited responses return HTTP 429 Too Many Requests with a Retry-After header.
Posts
Create, read, update, and delete feature request posts.
/api/v1/postsQuery Parameters
| Name | Type | Description |
|---|---|---|
| status | string | Filter by status: PENDING, APPROVED, IN_PROGRESS, COMPLETE, CLOSED, ARCHIVED |
| sort | string | Sort order: votes (default), date |
| page | number | Page number (default: 1) |
| limit | number | Number of results per page (default: 50, max: 100) |
Response
{
"data": [
{
"id": "post_abc123",
"title": "Dark mode support",
"description": "Add a dark theme option.",
"status": "APPROVED",
"voteCount": 42,
"createdAt": "2025-01-15T08:30:00Z",
"updatedAt": "2025-01-20T10:00:00Z",
"tags": [
{ "id": "tag_1", "label": "UI", "theme": "#6366f1" }
]
}
],
"pagination": {
"page": 1,
"limit": 50,
"total": 128,
"totalPages": 3
}
}Example
curl "https://app.voteship.app/api/v1/posts?status=APPROVED&sort=votes&limit=10&page=1" \
-H "Authorization: Bearer YOUR_API_SECRET_KEY"/api/v1/postsRequest Body
{
"title": "Dark mode support",
"description": "It would be great to have a dark theme option.",
"status": "APPROVED",
"tagIds": ["tag_1", "tag_2"]
}Response
{
"data": {
"id": "post_abc123",
"projectId": "proj_1",
"title": "Dark mode support",
"description": "It would be great to have a dark theme option.",
"status": "APPROVED",
"voteCount": 0,
"targetReleaseDate": null,
"mergedIntoPostId": null,
"releaseId": null,
"metadata": null,
"createdByBoardUserId": null,
"createdAt": "2025-01-15T08:30:00Z",
"updatedAt": "2025-01-15T08:30:00Z"
}
}Example
curl -X POST "https://app.voteship.app/api/v1/posts" \
-H "Authorization: Bearer YOUR_API_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"title": "Dark mode support",
"description": "It would be great to have a dark theme option.",
"status": "APPROVED"
}'/api/v1/posts/:postIdResponse
{
"data": {
"id": "post_abc123",
"projectId": "proj_1",
"title": "Dark mode support",
"description": "It would be great to have a dark theme option.",
"status": "APPROVED",
"voteCount": 42,
"targetReleaseDate": null,
"mergedIntoPostId": null,
"releaseId": null,
"metadata": null,
"createdByBoardUserId": null,
"tags": [
{ "id": "tag_1", "label": "UI", "theme": "#6366f1" }
],
"createdAt": "2025-01-15T08:30:00Z",
"updatedAt": "2025-01-20T10:00:00Z"
}
}Example
curl "https://app.voteship.app/api/v1/posts/post_abc123" \
-H "Authorization: Bearer YOUR_API_SECRET_KEY"/api/v1/posts/:postIdRequest Body
{
"title": "Dark mode support (updated)",
"status": "IN_PROGRESS",
"tagIds": ["tag_1"]
}Response
{
"data": {
"id": "post_abc123",
"title": "Dark mode support (updated)",
"description": "It would be great to have a dark theme option.",
"status": "IN_PROGRESS",
"voteCount": 42,
"createdAt": "2025-01-15T08:30:00Z",
"updatedAt": "2025-02-01T14:00:00Z"
}
}Example
curl -X PATCH "https://app.voteship.app/api/v1/posts/post_abc123" \
-H "Authorization: Bearer YOUR_API_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{ "status": "IN_PROGRESS" }'/api/v1/posts/:postIdResponse
{
"success": true
}Example
curl -X DELETE "https://app.voteship.app/api/v1/posts/post_abc123" \
-H "Authorization: Bearer YOUR_API_SECRET_KEY"Votes
List voters and add votes on posts.
/api/v1/posts/:postId/votesResponse
{
"data": [
{
"id": "vote_1",
"boardUserId": "bu_123",
"anonymousId": null,
"createdAt": "2025-01-16T09:00:00Z",
"boardUserName": "Jane Doe",
"boardUserEmail": "jane@example.com"
}
]
}Example
curl "https://app.voteship.app/api/v1/posts/post_abc123/votes" \
-H "Authorization: Bearer YOUR_API_SECRET_KEY"/api/v1/posts/:postId/votesRequest Body
// Option A: identified board user
{
"boardUserId": "bu_123"
}
// Option B: anonymous voter
{
"anonymousId": "anon_abc123"
}Response
{
"data": {
"id": "vote_1",
"postId": "post_abc123",
"boardUserId": "bu_123",
"anonymousId": null,
"createdAt": "2025-01-16T09:00:00Z"
}
}Example
curl -X POST "https://app.voteship.app/api/v1/posts/post_abc123/votes" \
-H "Authorization: Bearer YOUR_API_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{ "boardUserId": "bu_123" }'Users
Manage board users who have interacted with your project. Update user spend/revenue values to prioritize feedback from your most valuable customers.
/api/v1/usersQuery Parameters
| Name | Type | Description |
|---|---|---|
| page | number | Page number (default: 1) |
| limit | number | Results per page (default: 50, max: 100) |
Response
{
"data": [
{
"id": "bu_1",
"projectId": "proj_1",
"appUserId": "app_user_123",
"email": "jane@example.com",
"name": "Jane Doe",
"avatarUrl": null,
"userSpend": "0",
"createdAt": "2024-11-01T00:00:00Z"
},
{
"id": "bu_2",
"projectId": "proj_1",
"appUserId": null,
"email": "john@example.com",
"name": "John Smith",
"avatarUrl": null,
"userSpend": "49.99",
"createdAt": "2025-01-05T10:30:00Z"
}
],
"pagination": {
"page": 1,
"limit": 50,
"total": 245,
"totalPages": 5
}
}Example
curl "https://app.voteship.app/api/v1/users?page=1&limit=50" \
-H "Authorization: Bearer YOUR_API_SECRET_KEY"/api/v1/users/:userIdRequest Body
{
"userSpend": 149.99,
"name": "Jane Doe",
"email": "jane@example.com"
}Response
{
"data": {
"id": "bu_1",
"projectId": "proj_1",
"appUserId": "app_user_123",
"email": "jane@example.com",
"name": "Jane Doe",
"avatarUrl": null,
"userSpend": "149.99",
"createdAt": "2024-11-01T00:00:00Z"
}
}Example
curl -X PATCH "https://app.voteship.app/api/v1/users/bu_1" \
-H "Authorization: Bearer YOUR_API_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{ "userSpend": 149.99 }'Roadmap
Retrieve your public product roadmap grouped by status columns.
/api/v1/roadmapResponse
{
"data": {
"approved": [
{
"id": "post_abc123",
"title": "Dark mode support",
"description": "Add a dark theme option.",
"voteCount": 42,
"tags": [{ "id": "tag_1", "label": "UI", "theme": "#6366f1" }],
"createdAt": "2025-01-15T08:30:00Z"
}
],
"in_progress": [],
"complete": []
}
}Example
curl "https://app.voteship.app/api/v1/roadmap" \
-H "Authorization: Bearer YOUR_API_SECRET_KEY"Releases
Manage changelog releases to keep users informed about shipped features.
/api/v1/releasesQuery Parameters
| Name | Type | Description |
|---|---|---|
| page | number | Page number (default: 1) |
| limit | number | Results per page (default: 20, max: 100) |
| include_drafts | boolean | Include draft releases (default: false) |
Response
{
"data": [
{
"id": "rel_abc123",
"projectId": "proj_1",
"title": "January 2026 Update",
"content": "<p>We shipped dark mode and CSV export.</p>",
"isDraft": false,
"publishedAt": "2026-01-15T12:00:00Z",
"createdAt": "2026-01-14T08:30:00Z"
}
]
}Example
curl "https://app.voteship.app/api/v1/releases?limit=10" \
-H "Authorization: Bearer YOUR_API_SECRET_KEY"/api/v1/releasesRequest Body
{
"title": "February 2026 Update",
"content": "<p>New features shipped this month.</p>",
"isDraft": false
}Response
{
"data": {
"id": "rel_def456",
"projectId": "proj_1",
"title": "February 2026 Update",
"content": "<p>New features shipped this month.</p>",
"isDraft": false,
"publishedAt": "2026-02-14T12:00:00Z",
"createdAt": "2026-02-14T12:00:00Z"
}
}Example
curl -X POST "https://app.voteship.app/api/v1/releases" \
-H "Authorization: Bearer YOUR_API_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"title": "February 2026 Update",
"content": "<p>New features shipped this month.</p>",
"isDraft": false
}'Analytics
Get aggregated analytics for your project including stats, top posts, and trending tags.
/api/v1/analyticsQuery Parameters
| Name | Type | Description |
|---|---|---|
| period | string | Time period: week (default), month, or quarter |
Response
{
"data": {
"period": "week",
"stats": {
"new_posts": 12,
"total_votes": 87,
"new_comments": 34,
"page_views": 1250
},
"top_posts": [
{
"id": "post_abc123",
"title": "Dark mode support",
"voteCount": 42,
"status": "APPROVED"
}
],
"trending_tags": [
{ "label": "UI", "post_count": 8 },
{ "label": "Performance", "post_count": 5 }
]
}
}Example
curl "https://app.voteship.app/api/v1/analytics?period=month" \
-H "Authorization: Bearer YOUR_API_SECRET_KEY"Semantic Search
Find similar feature requests using AI-powered semantic search (pgvector embeddings). Useful for duplicate detection before creating new posts.
/api/v1/posts/similarQuery Parameters
| Name | Type | Description |
|---|---|---|
| query | string | Natural language search query (required) |
| threshold | number | Minimum similarity score 0-1 (default: 0.7) |
| limit | number | Maximum results to return (default: 5, max: 20) |
Response
{
"data": [
{
"id": "post_abc123",
"title": "Dark mode support",
"similarity": 0.92
},
{
"id": "post_def456",
"title": "Night theme toggle",
"similarity": 0.85
}
]
}Example
curl "https://app.voteship.app/api/v1/posts/similar?query=dark+theme&threshold=0.7&limit=5" \
-H "Authorization: Bearer YOUR_API_SECRET_KEY"Integrations
Configure third-party integrations (Slack, GitHub, Linear, Discord) via the API. This enables AI agents and CI/CD pipelines to manage VoteShip integrations without a browser session.
/api/v1/integrations/slackResponse
{
"data": {
"id": "int_abc123",
"webhookUrl": "••••••••••••abcd",
"enabled": true,
"createdAt": "2025-01-15T08:30:00Z"
}
}Example
curl "https://app.voteship.app/api/v1/integrations/slack" \
-H "Authorization: Bearer YOUR_API_SECRET_KEY"/api/v1/integrations/slackRequest Body
{
"webhookUrl": "https://hooks.slack.com/services/T.../B.../xxx",
"enabled": true
}Response
{
"data": {
"id": "int_abc123",
"webhookUrl": "••••••••••••/xxx",
"enabled": true,
"createdAt": "2025-01-15T08:30:00Z"
}
}Example
curl -X PUT "https://app.voteship.app/api/v1/integrations/slack" \
-H "Authorization: Bearer YOUR_API_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"webhookUrl": "https://hooks.slack.com/services/T.../B.../xxx",
"enabled": true
}'/api/v1/integrations/slackResponse
{
"success": true
}Example
curl -X DELETE "https://app.voteship.app/api/v1/integrations/slack" \
-H "Authorization: Bearer YOUR_API_SECRET_KEY"/api/v1/integrations/githubResponse
{
"data": {
"id": "int_def456",
"repoOwner": "acme-corp",
"repoName": "product-feedback",
"accessToken": "••••••••abcd",
"enabled": true,
"syncLabels": true,
"autoCreateIssues": true,
"createdAt": "2025-01-15T08:30:00Z"
}
}Example
curl "https://app.voteship.app/api/v1/integrations/github" \
-H "Authorization: Bearer YOUR_API_SECRET_KEY"/api/v1/integrations/githubRequest Body
{
"repoOwner": "acme-corp",
"repoName": "product-feedback",
"accessToken": "ghp_xxxxxxxxxxxx",
"enabled": true,
"syncLabels": true,
"autoCreateIssues": true
}Response
{
"data": {
"id": "int_def456",
"repoOwner": "acme-corp",
"repoName": "product-feedback",
"accessToken": "••••••••xxxx",
"enabled": true,
"syncLabels": true,
"autoCreateIssues": true,
"createdAt": "2025-01-15T08:30:00Z"
}
}Example
curl -X PUT "https://app.voteship.app/api/v1/integrations/github" \
-H "Authorization: Bearer YOUR_API_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"repoOwner": "acme-corp",
"repoName": "product-feedback",
"accessToken": "ghp_xxxxxxxxxxxx",
"enabled": true,
"syncLabels": true,
"autoCreateIssues": true
}'/api/v1/integrations/githubResponse
{
"success": true
}Example
curl -X DELETE "https://app.voteship.app/api/v1/integrations/github" \
-H "Authorization: Bearer YOUR_API_SECRET_KEY"/api/v1/integrations/linearResponse
{
"data": {
"id": "int_ghi789",
"apiKey": "••••••••abcd",
"teamId": "team_123",
"enabled": true,
"autoCreateIssues": true,
"createdAt": "2025-01-15T08:30:00Z"
}
}Example
curl "https://app.voteship.app/api/v1/integrations/linear" \
-H "Authorization: Bearer YOUR_API_SECRET_KEY"/api/v1/integrations/linearRequest Body
{
"apiKey": "lin_api_xxxxxxxxxxxx",
"teamId": "team_123",
"enabled": true,
"autoCreateIssues": true
}Response
{
"data": {
"id": "int_ghi789",
"apiKey": "••••••••xxxx",
"teamId": "team_123",
"enabled": true,
"autoCreateIssues": true,
"createdAt": "2025-01-15T08:30:00Z"
}
}Example
curl -X PUT "https://app.voteship.app/api/v1/integrations/linear" \
-H "Authorization: Bearer YOUR_API_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"apiKey": "lin_api_xxxxxxxxxxxx",
"teamId": "team_123",
"enabled": true,
"autoCreateIssues": true
}'/api/v1/integrations/linearResponse
{
"success": true
}Example
curl -X DELETE "https://app.voteship.app/api/v1/integrations/linear" \
-H "Authorization: Bearer YOUR_API_SECRET_KEY"/api/v1/integrations/discordResponse
{
"data": {
"id": "int_jkl012",
"webhookUrl": "••••••••••••abcd",
"enabled": true,
"createdAt": "2025-01-15T08:30:00Z"
}
}Example
curl "https://app.voteship.app/api/v1/integrations/discord" \
-H "Authorization: Bearer YOUR_API_SECRET_KEY"/api/v1/integrations/discordRequest Body
{
"webhookUrl": "https://discord.com/api/webhooks/1234/abcd",
"enabled": true
}Response
{
"data": {
"id": "int_jkl012",
"webhookUrl": "••••••••••••abcd",
"enabled": true,
"createdAt": "2025-01-15T08:30:00Z"
}
}Example
curl -X PUT "https://app.voteship.app/api/v1/integrations/discord" \
-H "Authorization: Bearer YOUR_API_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"webhookUrl": "https://discord.com/api/webhooks/1234/abcd",
"enabled": true
}'/api/v1/integrations/discordResponse
{
"success": true
}Example
curl -X DELETE "https://app.voteship.app/api/v1/integrations/discord" \
-H "Authorization: Bearer YOUR_API_SECRET_KEY"/api/v1/integrations/stripe/syncResponse
{
"data": {
"status": "success",
"message": "Matched 42 of 156 Stripe customers, created 8 new users",
"totalCustomers": 156,
"matchedUsers": 42,
"createdUsers": 8,
"skippedNoEmail": 3,
"errors": []
}
}Example
curl -X POST "https://app.voteship.app/api/v1/integrations/stripe/sync" \
-H "Authorization: Bearer YOUR_API_SECRET_KEY"Import
Bulk-import posts from CSV files or migrate from other platforms.
/api/v1/importRequest Body
{
"data": "title,description,status,votes\n\"Dark mode\",\"Add dark theme\",\"APPROVED\",12\n\"API access\",\"REST API for integrations\",\"APPROVED\",45",
"format": "csv"
}Response
{
"imported": 24,
"errors": [
"Row 15: missing title, skipped"
]
}Example
curl -X POST "https://app.voteship.app/api/v1/import" \
-H "Authorization: Bearer YOUR_API_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{ "data": "title,description\n\"Dark mode\",\"Add dark theme\"" }'/api/v1/import/noltRequest Body
{
"data": "title,description,status,upvotes\n\"Feature A\",\"Description\",\"open\",10"
}Response
{
"imported": 56,
"errors": []
}Example
curl -X POST "https://app.voteship.app/api/v1/import/nolt" \
-H "Authorization: Bearer YOUR_API_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{ "data": "title,description\n\"Feature A\",\"Description\"" }'/api/v1/import/uservoiceRequest Body
{
"data": "title,body,status,votes\n\"Feature A\",\"Description\",\"planned\",25"
}Response
{
"imported": 130,
"errors": []
}Example
curl -X POST "https://app.voteship.app/api/v1/import/uservoice" \
-H "Authorization: Bearer YOUR_API_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{ "data": "title,body\n\"Feature A\",\"Description\"" }'Activity
Retrieve the activity log for your project.
/api/v1/activityQuery Parameters
| Name | Type | Description |
|---|---|---|
| page | number | Page number (default: 1) |
| limit | number | Number of events per page (default: 50, max: 100) |
| entityType | string | Filter by entity type (e.g. post, vote, comment) |
Response
{
"data": [
{
"id": "evt_1",
"projectId": "proj_1",
"action": "created",
"entityType": "post",
"entityId": "post_abc123",
"metadata": {},
"userId": "user_1",
"createdAt": "2025-01-15T08:30:00Z"
},
{
"id": "evt_2",
"projectId": "proj_1",
"action": "status_changed",
"entityType": "post",
"entityId": "post_abc123",
"metadata": {
"oldStatus": "APPROVED",
"newStatus": "IN_PROGRESS"
},
"userId": "user_1",
"createdAt": "2025-01-20T10:00:00Z"
}
],
"pagination": {
"page": 1,
"limit": 50,
"total": 1024,
"totalPages": 21
}
}Example
curl "https://app.voteship.app/api/v1/activity?page=1&limit=50" \
-H "Authorization: Bearer YOUR_API_SECRET_KEY"Webhooks
Receive real-time notifications when events happen in your project. Configure webhook URLs in your project settings.
Event Types
| Event | Description |
|---|---|
| post.created | A new post was created |
| post.updated | A post was updated (title, description, or status) |
| post.deleted | A post was deleted |
| post.status_changed | A post's status was changed |
| post.merged | A post was merged into another |
| vote.created | A user voted on a post |
| vote.removed | A user removed their vote |
| comment.created | A new comment was added |
| comment.deleted | A comment was deleted |
| tag.created | A new tag was created |
| tag.deleted | A tag was deleted |
| release.published | A changelog release was published |
Payload Format
{
"event": "post.created",
"data": {
"id": "post_abc123",
"title": "Dark mode support",
"description": "Add a dark theme option.",
"status": "APPROVED"
},
"timestamp": "2025-01-15T08:30:00Z",
"webhookId": "whk_abc123"
}Signature Verification
Every webhook request includes a X-VoteShip-Signature header. This is an HMAC-SHA256 signature of the raw request body using your webhook secret as the key. Always verify this signature to ensure the request is authentic.
const crypto = require('crypto');
function verifyWebhookSignature(body, signature, secret) {
// Strip the "sha256=" prefix before comparing
const sig = signature.startsWith('sha256=')
? signature.slice(7)
: signature;
const expected = crypto
.createHmac('sha256', secret)
.update(body, 'utf8')
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(sig),
Buffer.from(expected)
);
}
// In your webhook handler:
app.post('/webhook', (req, res) => {
const signature = req.headers['x-voteship-signature'];
const isValid = verifyWebhookSignature(
req.rawBody,
signature,
process.env.WEBHOOK_SECRET
);
if (!isValid) {
return res.status(401).send('Invalid signature');
}
const event = req.body;
console.log('Received event:', event.event);
// Process the event...
res.status(200).send('OK');
});Retry Policy
If your endpoint returns a non-2xx status code or times out (after 30 seconds), VoteShip will retry the delivery up to 2 additional times (3 total attempts):
- 1st retry: after ~5 seconds
- 2nd retry: after ~15 seconds
Failed deliveries are logged and can be manually retried from the webhook settings in your project dashboard.
Webhook Endpoints
Manage webhook endpoints programmatically via the API. This allows AI agents and automation tools to configure webhooks without a browser session.
/api/v1/integrations/webhooksResponse
{
"data": [
{
"id": "whk_abc123",
"url": "https://your-app.com/webhook",
"secret": "••••••••abcd",
"events": ["post.created", "post.status_changed"],
"enabled": true,
"description": "Production webhook",
"createdAt": "2025-01-15T08:30:00Z"
}
]
}Example
curl "https://app.voteship.app/api/v1/integrations/webhooks" \
-H "Authorization: Bearer YOUR_API_SECRET_KEY"/api/v1/integrations/webhooksRequest Body
{
"url": "https://your-app.com/webhook",
"events": ["post.created", "vote.created"],
"description": "My webhook"
}Response
{
"data": {
"id": "whk_abc123",
"url": "https://your-app.com/webhook",
"secret": "whsec_xxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"events": ["post.created", "vote.created"],
"enabled": true,
"description": "My webhook",
"createdAt": "2025-01-15T08:30:00Z"
}
}Example
curl -X POST "https://app.voteship.app/api/v1/integrations/webhooks" \
-H "Authorization: Bearer YOUR_API_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-app.com/webhook",
"events": ["*"],
"description": "All events"
}'/api/v1/integrations/webhooks/:webhookIdResponse
{
"data": {
"id": "whk_abc123",
"url": "https://your-app.com/webhook",
"secret": "••••••••abcd",
"events": ["*"],
"enabled": true,
"description": "All events",
"createdAt": "2025-01-15T08:30:00Z",
"recentDeliveries": [
{
"id": "del_1",
"event": "post.created",
"success": true,
"responseStatus": 200,
"attempts": 1,
"createdAt": "2025-01-20T10:00:00Z"
}
]
}
}Example
curl "https://app.voteship.app/api/v1/integrations/webhooks/whk_abc123" \
-H "Authorization: Bearer YOUR_API_SECRET_KEY"/api/v1/integrations/webhooks/:webhookIdRequest Body
{
"enabled": false,
"events": ["post.created"]
}Response
{
"data": {
"id": "whk_abc123",
"url": "https://your-app.com/webhook",
"secret": "••••••••abcd",
"events": ["post.created"],
"enabled": false,
"description": "All events",
"createdAt": "2025-01-15T08:30:00Z"
}
}Example
curl -X PATCH "https://app.voteship.app/api/v1/integrations/webhooks/whk_abc123" \
-H "Authorization: Bearer YOUR_API_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{ "enabled": false }'/api/v1/integrations/webhooks/:webhookIdResponse
{
"success": true
}Example
curl -X DELETE "https://app.voteship.app/api/v1/integrations/webhooks/whk_abc123" \
-H "Authorization: Bearer YOUR_API_SECRET_KEY"AI Workflows
AI-powered endpoints for processing, triaging, and summarizing feature requests. Requires Growth plan or higher.
/api/v1/posts/from-textRequest Body
{
"text": "It would be really nice if I could export my board data to CSV so I can do analysis in Excel. Maybe include vote counts and status too.",
"authorEmail": "jane@example.com",
"authorName": "Jane Doe"
}Response
{
"data": {
"id": "post_xyz789",
"title": "CSV export for board data",
"description": "Allow exporting board data to CSV including vote counts and statuses for external analysis in tools like Excel.",
"status": "PENDING",
"voteCount": 1,
"tags": [
{ "id": "tag_2", "label": "Analytics", "theme": "#f59e0b" }
],
"createdAt": "2025-02-10T14:30:00Z",
"updatedAt": "2025-02-10T14:30:00Z",
"ai": {
"extractedTitle": "CSV export for board data",
"suggestedTags": ["Analytics"],
"duplicateOf": null,
"confidence": 0.92
}
}
}Example
curl -X POST "https://app.voteship.app/api/v1/posts/from-text" \
-H "Authorization: Bearer YOUR_API_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"text": "It would be really nice if I could export my board data to CSV so I can do analysis in Excel.",
"authorEmail": "jane@example.com",
"authorName": "Jane Doe"
}'/api/v1/ai/triageRequest Body
{
"limit": 20,
"autoApply": false
}Response
{
"data": {
"triaged": [
{
"postId": "post_abc123",
"title": "Dark mode support",
"suggestedStatus": "APPROVED",
"suggestedTags": ["UI", "Design"],
"duplicateOf": null,
"priorityScore": 0.87,
"reasoning": "High vote count (42), clear user need, aligns with existing UI tag."
},
{
"postId": "post_def456",
"title": "Add night theme",
"suggestedStatus": "CLOSED",
"suggestedTags": ["UI"],
"duplicateOf": "post_abc123",
"priorityScore": 0.34,
"reasoning": "Duplicate of 'Dark mode support'. Merging recommended."
}
],
"totalProcessed": 2,
"applied": false
}
}Example
curl -X POST "https://app.voteship.app/api/v1/ai/triage" \
-H "Authorization: Bearer YOUR_API_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{ "limit": 20, "autoApply": false }'/api/v1/ai/summaryRequest Body
{
"days": 30,
"includeComments": true
}Response
{
"data": {
"period": {
"from": "2025-01-11T00:00:00Z",
"to": "2025-02-10T23:59:59Z"
},
"overview": "128 new posts received with 1,024 total votes. Feature requests are up 15% from the previous period.",
"topThemes": [
{ "theme": "Integrations", "postCount": 32, "totalVotes": 256 },
{ "theme": "UI/UX improvements", "postCount": 28, "totalVotes": 198 },
{ "theme": "Performance", "postCount": 18, "totalVotes": 145 }
],
"sentiment": {
"positive": 0.62,
"neutral": 0.29,
"negative": 0.09
},
"insights": [
"Integration requests (especially Slack and Linear) are the strongest trend this period.",
"Several users are requesting CSV export -- consider bundling with the analytics theme.",
"Negative sentiment is concentrated around load times on the public board."
],
"topPosts": [
{ "id": "post_abc123", "title": "Slack two-way sync", "votes": 67 },
{ "id": "post_def456", "title": "Linear auto-create issues", "votes": 54 },
{ "id": "post_ghi789", "title": "Board performance on mobile", "votes": 48 }
]
}
}Example
curl -X POST "https://app.voteship.app/api/v1/ai/summary" \
-H "Authorization: Bearer YOUR_API_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{ "days": 30, "includeComments": true }'/api/v1/ai/sprint-planRequest Body
{
"sprintDurationDays": 14,
"maxItems": 10,
"focus": "high-impact"
}Response
{
"data": {
"sprintName": "Sprint 2025-02-10",
"duration": "14 days",
"items": [
{
"postId": "post_abc123",
"title": "Slack two-way sync",
"votes": 67,
"effort": "medium",
"impact": "high",
"theme": "Integrations",
"rationale": "Highest-voted request; builds on existing Slack integration."
},
{
"postId": "post_def456",
"title": "Linear auto-create issues",
"votes": 54,
"effort": "low",
"impact": "high",
"theme": "Integrations",
"rationale": "Low effort since Linear API is already integrated. Pairs well with Slack sync."
},
{
"postId": "post_ghi789",
"title": "Board performance on mobile",
"votes": 48,
"effort": "high",
"impact": "high",
"theme": "Performance",
"rationale": "Source of most negative sentiment. Addresses mobile usability gap."
}
],
"summary": "This sprint focuses on high-impact integration features and a critical performance fix. Estimated to address 169 user votes across 3 themes.",
"totalVotesAddressed": 169,
"themes": ["Integrations", "Performance"]
}
}Example
curl -X POST "https://app.voteship.app/api/v1/ai/sprint-plan" \
-H "Authorization: Bearer YOUR_API_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"sprintDurationDays": 14,
"maxItems": 10,
"focus": "high-impact"
}'Widget SDK
Embed a feedback widget directly in your application so users can submit and vote on feature requests without leaving your app.
Floating Button (default)
Add this snippet before the closing </body> tag. A floating feedback button will appear in the corner of your page.
<script
src="https://app.voteship.app/widget/widget.js"
data-project="YOUR_PUBLIC_KEY"
data-position="bottom-right"
data-theme="auto"
defer
></script>Inline Board
Embed the full voting board directly into your page by adding a data-container attribute pointing to a container element:
<div id="voteship-board"></div>
<script
src="https://app.voteship.app/widget/widget.js"
data-project="YOUR_PUBLIC_KEY"
data-container="voteship-board"
data-theme="auto"
defer
></script>Configuration Options
| Attribute | Required | Description |
|---|---|---|
| data-project | Yes | Your project's public API key (found in Settings → General) |
| data-position | No | Button position: bottom-right (default), bottom-left, top-right, top-left |
| data-container | No | Element ID for inline board mode — omit for floating button |
| data-theme | No | Theme: auto (default — detects from page), light, dark |
| data-locale | No | Locale code (e.g. en, es, fr). Defaults to browser language |
| data-user-id | No | Pre-identify the current user by their app ID (triggers auto-identify on load) |
| data-user-email | No | Email for the identified user (optional — enhances user profile) |
| data-user-name | No | Display name for the identified user |
Next.js Integration
Use the built-in <Script> component for optimal loading:
import Script from 'next/script';
export default function RootLayout({ children }) {
return (
<html>
<body>
{children}
<Script
src="https://app.voteship.app/widget/widget.js"
data-project="YOUR_PUBLIC_KEY"
data-position="bottom-right"
strategy="lazyOnload"
/>
</body>
</html>
);
}Next.js with User Identification
Pass your logged-in user's info so their feedback is linked to their account:
import Script from 'next/script';
// In your layout or page component:
<Script
src="https://app.voteship.app/widget/widget.js"
data-project="YOUR_PUBLIC_KEY"
data-user-id={currentUser.id}
data-user-email={currentUser.email}
data-user-name={currentUser.name}
strategy="lazyOnload"
/>React (non-Next.js)
import { useEffect } from 'react';
export function VoteShipWidget({ userId, userEmail, userName }) {
useEffect(() => {
const script = document.createElement('script');
script.src = 'https://app.voteship.app/widget/widget.js';
script.dataset.project = 'YOUR_PUBLIC_KEY';
script.dataset.position = 'bottom-right';
script.defer = true;
if (userId) script.dataset.userId = userId;
if (userEmail) script.dataset.userEmail = userEmail;
if (userName) script.dataset.userName = userName;
document.body.appendChild(script);
return () => {
document.body.removeChild(script);
};
}, []);
return null;
}Programmatic Control
After the widget loads, a global window.VoteShip object becomes available. Listen for the voteship:ready event to know when it's safe to call:
// Wait for the widget to be ready
window.addEventListener('voteship:ready', () => {
// Now safe to call VoteShip methods
console.log('VoteShip widget loaded');
});
// Open the floating popup
window.VoteShip.openPopup();
// Close the floating popup
window.VoteShip.closePopup();
// Identify a user (e.g. after login)
window.VoteShip.identify({
id: 'user_123', // required — your app's user ID
email: 'jane@example.com', // optional
name: 'Jane Doe', // optional
userSpend: 99.00, // optional — monthly spend for prioritization
});
// Clear user identity (e.g. after logout)
window.VoteShip.reset();
// Mount an inline voting board into a container
window.VoteShip.loadVotingBoard('my-container-id');
// Mount an inline roadmap
window.VoteShip.loadRoadmap('roadmap-container');
// Mount an inline changelog
window.VoteShip.loadChangelog('changelog-container');
Comments
List and create comments on posts. Comments can be public (visible to everyone) or internal notes (visible only to team members).
/api/v1/posts/:postId/commentsQuery Parameters
Response
Example
/api/v1/posts/:postId/commentsRequest Body
Response
Example
/api/v1/posts/:postId/comments/:commentIdResponse
Example