REST API
The FeedbackKit REST API is the HTTP interface behind every SDK: submit feedback, read votes and manage projects with plain JSON requests. Use it from any language or platform that has no FeedbackKit SDK, or generate a client from the OpenAPI file.
Authentication
All API requests require authentication via the X-API-Key header. You can find your project API key in the FeedbackKit dashboard under Project Settings.
X-API-Key: your-project-api-key
X-User-Id: optional-user-id # For personalized has_voted state
Content-Type: application/json
Optional: X-User-Id Header
Include the X-User-Id header to get personalized has_voted state in feedback responses. Without it, has_voted will always be false.
Field naming
Response bodies are snake_case — every field below is spelled exactly as it arrives on the wire.
Request bodies accept either casing. Both {"userId": …} and {"user_id": …} decode to the same field, so the camelCase request examples below are correct and so is the snake_case every official SDK sends.
Query parameters are read verbatim — neither convention applies, so includeMerged is spelled exactly as documented.
Endpoints Overview
All endpoints are prefixed with /api/v1. The base URL for production is https://api.prod.getfeedbackkit.com.
| Method | Path | Description |
|---|---|---|
| GET | /feedbacks | List all feedback |
| POST | /feedbacks | Submit new feedback |
| GET | /feedbacks/:id | Get feedback by ID |
| POST | /feedbacks/:id/votes | Vote for feedback |
| DELETE | /feedbacks/:id/votes | Remove vote |
| GET | /feedbacks/:id/comments | List comments |
| POST | /feedbacks/:id/comments | Add comment |
| POST | /users/register | Register SDK user |
| POST | /events/track | Track analytics event |
Feedback
/feedbacks
Retrieves feedback for the project, sorted by vote count (descending) unless sort says otherwise. Comment counts are totals; comment text is not included. Merged feedback items are excluded by default. The default body is a JSON array of every visible row. page and per_page do not change that array. Send paged=1 for { items, meta }. Then page defaults to 1 and per_page defaults to 20 (clamped to 1–100). A page past the end returns an empty items array and the real total. The array path has no row cap, because a cap would drop rows existing clients already receive.
Query Parameters
| Parameter | Type | Description |
|---|---|---|
| status | string | Filter by status: pending, approved, in_progress, testflight, completed, rejected |
| category | string | Filter by category: feature_request, bug_report, improvement, other |
| includeMerged | boolean | Include merged feedback items. Default: false |
| sort | string | Sort order: votes, newest, oldest, comments. Default: votes |
| paged | string | 1 or true selects the page envelope. Omitted, the body stays a JSON array. |
| page | integer | 1-indexed page. Ignored unless paged=1. Default: 1 |
| per_page | integer | Page size, clamped to 1–100. Ignored unless paged=1. Default: 20 |
curl -X GET "https://api.prod.getfeedbackkit.com/api/v1/feedbacks?status=approved" \
-H "X-API-Key: your-api-key" \
-H "X-User-Id: user_12345"
Response when paged=1
Without paged=1 the same objects are returned as a JSON array, with no items or meta wrapper.
{
"items": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"title": "Add dark mode support",
"description": "It would be great to have a dark mode option.",
"status": "approved",
"category": "feature_request",
"user_id": "user_12345",
"vote_count": 42,
"has_voted": false,
"comment_count": 5,
"created_at": "2026-02-01T10:30:00Z",
"updated_at": "2026-02-05T14:20:00Z"
}
],
"meta": {
"total": 1,
"page": 1,
"per_page": 20,
"total_pages": 1
}
}
/feedbacks
Creates a new feedback item. The creator automatically receives a vote (vote_count starts at 1). Triggers notifications to project members and configured integrations.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
| title | string | Yes | Feedback title (1-200 chars) |
| description | string | Yes | Detailed description (1-5000 chars) |
| category | string | Yes | feature_request, bug_report, improvement, other |
| userId | string | Yes | Unique identifier of the submitting user |
| userEmail | string | No | Email for status update notifications |
curl -X POST "https://api.prod.getfeedbackkit.com/api/v1/feedbacks" \
-H "X-API-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{
"title": "Add dark mode support",
"description": "It would be great to have a dark mode option.",
"category": "feature_request",
"userId": "user_12345",
"userEmail": "user@example.com"
}'
/feedbacks/:id
Retrieves a single feedback item by its UUID.
curl -X GET "https://api.prod.getfeedbackkit.com/api/v1/feedbacks/550e8400-e29b-41d4-a716-446655440000" \
-H "X-API-Key: your-api-key" \
-H "X-User-Id: user_12345"
Votes
/feedbacks/:id/votes
Adds a vote to a feedback item. Each user can only vote once per feedback.
Restrictions
- Cannot vote on archived projects (403)
- Cannot vote on completed or rejected feedback (403)
- Cannot vote twice on the same feedback (409)
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
| userId | string | Yes | Unique identifier of the voting user |
| string | No | Email for status change notifications | |
| notifyStatusChange | boolean | No | Opt-in to email notifications on status changes. Default: false |
curl -X POST "https://api.prod.getfeedbackkit.com/api/v1/feedbacks/550e8400-.../votes" \
-H "X-API-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{
"userId": "user_12345",
"email": "user@example.com",
"notifyStatusChange": true
}'
Response
{
"feedback_id": "550e8400-e29b-41d4-a716-446655440000",
"vote_count": 43,
"has_voted": true
}
/feedbacks/:id/votes
Removes the user's vote from a feedback item.
curl -X DELETE "https://api.prod.getfeedbackkit.com/api/v1/feedbacks/550e8400-.../votes" \
-H "X-API-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{"userId": "user_12345"}'
Users
/users/register
Registers or updates an SDK user. Use this to track user activity and associate MRR (Monthly Recurring Revenue). If the user already exists, their last_seen_at timestamp and MRR will be updated.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
| userId | string | Yes | Unique identifier of the SDK user |
| mrr | number | No | Monthly Recurring Revenue for this user |
curl -X POST "https://api.prod.getfeedbackkit.com/api/v1/users/register" \
-H "X-API-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{
"userId": "user_12345",
"mrr": 9.99
}'
Response
{
"id": "770e8400-e29b-41d4-a716-446655440000",
"user_id": "user_12345",
"mrr": 9.99,
"first_seen_at": "2026-01-15T08:00:00Z",
"last_seen_at": "2026-02-08T12:30:00Z"
}
Events
/events/track
Tracks a custom event for analytics. Use this to measure user engagement with the feedback system.
Common Events
| Event Name | Description |
|---|---|
| feedback_list | User viewed the feedback list |
| feedback_detail | User viewed feedback details |
| submit_feedback | User opened the submit form |
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
| eventName | string | Yes | Name of the event (1-100 chars) |
| userId | string | Yes | Unique identifier of the user |
| properties | object | No | Key-value properties for the event |
curl -X POST "https://api.prod.getfeedbackkit.com/api/v1/events/track" \
-H "X-API-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{
"eventName": "feedback_list",
"userId": "user_12345",
"properties": {
"filter": "feature_request",
"sort": "votes"
}
}'
Models
FeedbackResponse
| Field | Type | Description |
|---|---|---|
| id | UUID | Unique identifier |
| title | string | Feedback title |
| description | string | Detailed description |
| status | FeedbackStatus | Current status of the feedback |
| category | FeedbackCategory | Feedback category |
| user_id | string | ID of the submitting user |
| user_email | string? | Email of the submitter (if provided) |
| vote_count | integer | Total number of votes |
| has_voted | boolean | Whether the current user has voted |
| comment_count | integer | Total number of comments |
| total_mrr | number? | Combined MRR of all voters |
| created_at | ISO 8601 | Creation timestamp |
| updated_at | ISO 8601 | Last update timestamp |
| rejection_reason | string? | Explanation for rejection (max 500 chars) |
| merged_into_id | UUID? | ID of feedback this was merged into |
| merged_at | ISO 8601? | When this feedback was merged |
| merged_feedback_ids | UUID[]? | IDs of feedback merged into this one |
FeedbackStatus
| Value | Description | Voting |
|---|---|---|
| pending | New, awaiting review | Allowed |
| approved | Accepted for consideration | Allowed |
| in_progress | Currently being worked on | Allowed |
| testflight | Available in beta/TestFlight | Allowed |
| completed | Shipped and available | Blocked |
| rejected | Won't be implemented | Blocked |
FeedbackCategory
| Value | Description |
|---|---|
| feature_request | New functionality request |
| bug_report | Issue or problem report |
| improvement | Enhancement to existing feature |
| other | General feedback |
VoteResponse
| Field | Type | Description |
|---|---|---|
| feedback_id | UUID | ID of the feedback item |
| vote_count | integer | Updated vote count |
| has_voted | boolean | Whether the user has voted after this action |
CommentResponse
| Field | Type | Description |
|---|---|---|
| id | UUID | Unique identifier |
| content | string | Comment text |
| user_id | string | ID of the commenting user |
| is_admin | boolean | Whether this is an admin comment |
| created_at | ISO 8601 | Creation timestamp |
Error Handling
All errors follow a consistent format with an error boolean and a human-readable reason string.
{
"error": true,
"reason": "Invalid API key"
}
HTTP Status Codes
| Code | Name | Description |
|---|---|---|
| 200 | OK | Request succeeded |
| 400 | Bad Request | Validation error (missing or invalid fields) |
| 401 | Unauthorized | Missing or invalid API key |
| 402 | Payment Required | Subscription tier limit exceeded |
| 403 | Forbidden | Action not allowed (archived project, completed feedback, etc.) |
| 404 | Not Found | Resource does not exist |
| 409 | Conflict | Duplicate action (e.g., already voted) |
| 429 | Too Many Requests | Rate limit exceeded |
Subscription Tiers
Some API features are limited by subscription tier. Exceeding limits returns a 402 Payment Required response.
| Feature | Free | Pro | Team |
|---|---|---|---|
| Projects | 1 | 2 | Unlimited |
| Feedback per project | 10 | Unlimited | Unlimited |
| Team members | - | - | Unlimited |
| Integrations | - | All | All |
| Voter notifications | - | - | Yes |
Guides
What Is a Feedback SDK? A Practical Guide for App Teams
A feedback SDK lets users send, vote on and track feature requests inside your app. How it differs from surveys, support desks and analytics.
9 min read
How to Add a Feedback Widget to a JavaScript Web App
Build a feedback widget in a JS web app with feedbackkit-js 1.0.1 from npm: user ids, list, vote, submit, comments, MRR, and what the browser API key can do.
11 min read
Comments
/feedbacks/:id/commentsRetrieves comments for a feedback item, oldest first. The default body is a JSON array of every comment.
paged=1selects{ items, meta }, and only then dopageandper_pageapply, with the same clamps as the feedback list.Response when paged=1
Without
paged=1the same comments are returned as a JSON array./feedbacks/:id/commentsAdds a comment to a feedback item. Cannot comment on archived projects.
Request Body
false