Minolith Feedback API
The Feedback service lets you collect structured feedback from users, customers, and team members. Create feedback items, triage them with status and priority, add internal notes, and embed a public widget for anonymous submissions. Pairs with Changelog (turn feedback into shipped features) and Context (feed patterns back into agent memory).
Base URL: https://api.minolith.io/v1/feedback
Authentication: All endpoints (except the public widget endpoint) require an API key via the Authorization header:
Authorization: Bearer mlth_your_api_key_here
Errors: All errors follow the standard Minolith error format. See Platform API — Error Response Format for the full specification.
Content Negotiation: All endpoints support two response formats:
Accept: application/json— JSON (default)Accept: text/markdown— Raw markdown response
Resource Shapes
Item
{
"id": "fbk_7f3a9b2c4d1e",
"title": "Search results are slow on large datasets",
"body": "When I search for records with more than 10,000 results, the page takes over 15 seconds to load. This started happening after the last update.",
"type": "bug",
"status": "open",
"priority": "high",
"source": "api",
"tags": ["performance", "search"],
"page_url": "https://app.example.com/search",
"user_email": "alice@example.com",
"user_name": "Alice Chen",
"user_id_external": "usr_12345",
"created_at": "2026-03-17 12:00:00",
"updated_at": "2026-03-17 14:30:00"
}
Note
{
"id": "fbn_9d4b1e7c3a2f",
"item_id": "fbk_7f3a9b2c4d1e",
"body": "Confirmed — the slow query is the full-text search on the records table. Adding an index should fix it.",
"created_at": "2026-03-17 15:00:00"
}
The notes array is included when fetching a single item via GET /items/:id.
Items
Create Item
POST /v1/feedback/items
Creates a new feedback item. Costs 1 credit.
Request body:
{
"body": "When I search for records with more than 10,000 results, the page takes over 15 seconds to load.",
"title": "Search results are slow on large datasets",
"type": "bug",
"tags": ["performance", "search"],
"page_url": "https://app.example.com/search",
"user_email": "alice@example.com",
"user_name": "Alice Chen",
"user_id_external": "usr_12345"
}
| Field | Type | Required | Description |
|---|---|---|---|
body |
string | yes | The feedback content. Max 50,000 characters. |
title |
string | no | Short summary. Max 500 characters. If omitted, the system does not generate one. |
type |
string | no | One of: feedback, bug, idea, question, praise. Defaults to feedback. |
tags |
array | no | Array of tag strings. Max 10 tags, each max 100 characters. |
page_url |
string | no | URL of the page where the feedback was submitted. Max 2,000 characters. |
user_email |
string | no | Email address of the person who submitted the feedback. Max 255 characters. |
user_name |
string | no | Display name of the person who submitted the feedback. Max 255 characters. |
user_id_external |
string | no | Your application's user ID for the submitter. Max 255 characters. |
Response (201 Created):
{
"data": {
"id": "fbk_7f3a9b2c4d1e",
"title": "Search results are slow on large datasets",
"body": "When I search for records with more than 10,000 results, the page takes over 15 seconds to load.",
"type": "bug",
"status": "open",
"priority": null,
"source": "api",
"tags": ["performance", "search"],
"page_url": "https://app.example.com/search",
"user_email": "alice@example.com",
"user_name": "Alice Chen",
"user_id_external": "usr_12345",
"created_at": "2026-03-17 12:00:00",
"updated_at": "2026-03-17 12:00:00"
}
}
Error responses:
422— Validation error (missing body, invalid type, etc.)402— Spending cap exceeded.
List Items
GET /v1/feedback/items
Returns a paginated list of feedback items for the authenticated project.
Query parameters:
| Parameter | Type | Description |
|---|---|---|
status |
string | Filter by status: open, under_review, planned, in_progress, completed, declined |
type |
string | Filter by type: feedback, bug, idea, question, praise. Supports comma-separated values: type=bug,idea |
priority |
string | Filter by priority: low, medium, high, urgent. Supports comma-separated values: priority=high,urgent |
tags |
string | Filter by tags. Comma-separated. Items matching any of the listed tags are returned: tags=performance,search |
user_email |
string | Filter by submitter email (exact match) |
created_after |
string | ISO 8601 datetime. Only items created after this time. |
created_before |
string | ISO 8601 datetime. Only items created before this time. |
limit |
integer | Number of results (1-100, default 20) |
cursor |
string | Cursor from a previous response's pagination.next_cursor |
Example requests:
GET /v1/feedback/items?status=open&type=bug&priority=high,urgent
GET /v1/feedback/items?tags=performance&created_after=2026-03-01T00:00:00Z
GET /v1/feedback/items?user_email=alice@example.com&limit=50
GET /v1/feedback/items?type=idea,feedback&status=open
Response (200 OK):
{
"data": [
{
"id": "fbk_7f3a9b2c4d1e",
"title": "Search results are slow on large datasets",
"body": "When I search for records with more than 10,000 results, the page takes over 15 seconds to load.",
"type": "bug",
"status": "open",
"priority": "high",
"source": "api",
"tags": ["performance", "search"],
"page_url": "https://app.example.com/search",
"user_email": "alice@example.com",
"user_name": "Alice Chen",
"user_id_external": "usr_12345",
"created_at": "2026-03-17 12:00:00",
"updated_at": "2026-03-17 14:30:00"
},
{
"id": "fbk_4c5d6e7f8a9b",
"title": null,
"body": "Would be great if the dashboard had a dark mode option.",
"type": "idea",
"status": "open",
"priority": null,
"source": "mcp",
"tags": ["ui"],
"page_url": null,
"user_email": "bob@example.com",
"user_name": "Bob",
"user_id_external": null,
"created_at": "2026-03-16 09:30:00",
"updated_at": "2026-03-16 09:30:00"
}
],
"pagination": {
"has_more": false,
"next_cursor": null,
"limit": 20
}
}
Get Item
GET /v1/feedback/items/:id
Returns a single feedback item by its ID, including its internal notes.
Response (200 OK):
{
"data": {
"id": "fbk_7f3a9b2c4d1e",
"title": "Search results are slow on large datasets",
"body": "When I search for records with more than 10,000 results, the page takes over 15 seconds to load.",
"type": "bug",
"status": "under_review",
"priority": "high",
"source": "api",
"tags": ["performance", "search"],
"page_url": "https://app.example.com/search",
"user_email": "alice@example.com",
"user_name": "Alice Chen",
"user_id_external": "usr_12345",
"created_at": "2026-03-17 12:00:00",
"updated_at": "2026-03-17 15:00:00",
"notes": [
{
"id": "fbn_9d4b1e7c3a2f",
"item_id": "fbk_7f3a9b2c4d1e",
"body": "Confirmed — the slow query is the full-text search on the records table. Adding an index should fix it.",
"created_at": "2026-03-17 15:00:00"
}
]
}
}
Error responses:
404— Item not found.
{
"error": {
"code": "item_not_found",
"message": "Feedback item 'fbk_nonexistent' not found.",
"docs": "https://docs.minolith.io/api/feedback#get-item"
}
}
Update Item
PUT /v1/feedback/items/:id
Updates an existing feedback item. Only status, priority, and tags can be changed — the body, title, type, and user fields are immutable after creation. Only include the fields you want to change. Free — no credit cost.
Request body (all fields optional):
{
"status": "under_review",
"priority": "high",
"tags": ["performance", "search", "database"]
}
| Field | Type | Description |
|---|---|---|
status |
string | One of: open, under_review, planned, in_progress, completed, declined |
priority |
string | One of: low, medium, high, urgent. Set to null to clear. |
tags |
array | Replaces all existing tags. Max 10 tags, each max 100 characters. |
Response (200 OK):
Returns the full updated item in the same format as the create response (without notes).
Error responses:
404— Item not found.422— Validation error (invalid status, invalid priority, etc.)
Delete Item
DELETE /v1/feedback/items/:id
Permanently deletes a feedback item and all of its notes. This is a hard delete — the item cannot be recovered. Free — no credit cost.
Response (204 No Content):
Empty response body.
Error responses:
404— Item not found.
Notes
Add Note
POST /v1/feedback/items/:id/notes
Adds an internal note to a feedback item. Notes are for your team — they are never exposed via the public widget. Free — no credit cost.
Request body:
{
"body": "Confirmed — the slow query is the full-text search on the records table. Adding an index should fix it."
}
| Field | Type | Required | Description |
|---|---|---|---|
body |
string | yes | Note content. Max 50,000 characters. |
Response (201 Created):
{
"data": {
"id": "fbn_9d4b1e7c3a2f",
"item_id": "fbk_7f3a9b2c4d1e",
"body": "Confirmed — the slow query is the full-text search on the records table. Adding an index should fix it.",
"created_at": "2026-03-17 15:00:00"
}
}
Error responses:
404— Item not found.422— Validation error (missing body).
Public Endpoint
Widget Submission
POST /v1/feedback/public/:slug
Accepts feedback from the embeddable widget. No authentication required. Rate limited by IP address: 10 submissions per IP per hour per project.
The :slug is the project's public slug, configured in the Minolith dashboard.
The widget automatically captures page_url from the browser. You can also pass user context via the widget's data- attributes (see JS Widget below).
Request body:
{
"body": "The export button doesn't work on Firefox.",
"title": "Export broken on Firefox",
"type": "bug",
"page_url": "https://app.example.com/exports",
"user_email": "alice@example.com",
"user_name": "Alice Chen",
"user_id_external": "usr_12345"
}
| Field | Type | Required | Description |
|---|---|---|---|
body |
string | yes | The feedback content. Max 50,000 characters. |
title |
string | no | Short summary. Max 500 characters. |
type |
string | no | One of: feedback, bug, idea, question, praise. Defaults to feedback. |
page_url |
string | no | Auto-captured by the widget. Max 2,000 characters. |
user_email |
string | no | Passed via widget data-user-email attribute. Max 255 characters. |
user_name |
string | no | Max 255 characters. |
user_id_external |
string | no | Passed via widget data-user-id attribute. Max 255 characters. |
Response (201 Created):
{
"data": {
"id": "fbk_3e2d1c4b5a6f",
"title": "Export broken on Firefox",
"body": "The export button doesn't work on Firefox.",
"type": "bug",
"status": "open",
"priority": null,
"source": "widget",
"tags": [],
"page_url": "https://app.example.com/exports",
"user_email": "alice@example.com",
"user_name": "Alice Chen",
"user_id_external": "usr_12345",
"created_at": "2026-03-17 16:00:00",
"updated_at": "2026-03-17 16:00:00"
}
}
Error responses:
404— Project slug not found.422— Validation error (missing body, invalid type).429— Public rate limit exceeded.
{
"error": {
"code": "public_rate_limited",
"message": "Rate limit exceeded. Maximum 10 submissions per hour.",
"docs": "https://docs.minolith.io/api/feedback#rate-limiting"
}
}
JS Widget
Embed a feedback widget on your website or application:
<script src="https://api.minolith.io/widget/feedback.js"
data-project="your-slug"
data-position="bottom-right"
data-color="#6366f1"
data-user-email="alice@example.com"
data-user-id="usr_12345"></script>
| Attribute | Required | Description |
|---|---|---|
data-project |
yes | Your project's public slug |
data-position |
no | Widget position: bottom-right (default), bottom-left, top-right, top-left |
data-color |
no | Primary accent color as hex. Defaults to #6366f1. |
data-user-email |
no | Pre-fills the submitter's email. Passed through to the user_email field on the created item. |
data-user-id |
no | Your application's user ID. Passed through to the user_id_external field on the created item. |
The widget renders inside a Shadow DOM, so it will not conflict with your site's styles or scripts. It automatically captures the current page_url when the user submits feedback. Submissions go to POST /v1/feedback/public/:slug.
Validation Rules
| Field | Type | Required | Constraints |
|---|---|---|---|
body (item) |
string | yes | Max 50,000 characters |
title (item) |
string | no | Max 500 characters |
type |
enum | no | feedback, bug, idea, question, praise. Default: feedback |
status |
enum | no | open, under_review, planned, in_progress, completed, declined. Default: open |
priority |
enum | no | low, medium, high, urgent. Default: null |
source |
string | no | Read-only. Set automatically: api (REST API), mcp (MCP tool), widget (public widget). |
tags |
array | no | Max 10 tags, each max 100 characters |
page_url |
string | no | Max 2,000 characters |
user_email |
string | no | Max 255 characters, must be valid email format |
user_name |
string | no | Max 255 characters |
user_id_external |
string | no | Max 255 characters |
body (note) |
string | yes | Max 50,000 characters |
Credit Costs
| Action | Credits | Cost |
|---|---|---|
| Item created (API, widget, or MCP) | 1 | $0.01 |
| All other operations (reads, updates, deletes, notes) | 0 | Free |
Error Codes
| Code | HTTP | Message |
|---|---|---|
body_required |
422 | The body field is required. |
invalid_feedback_type |
422 | Type '{value}' is not valid. Accepted values are: feedback, bug, idea, question, praise. |
invalid_status |
422 | Status '{value}' is not valid. Accepted values are: open, under_review, planned, in_progress, completed, declined. |
invalid_priority |
422 | Priority '{value}' is not valid. Accepted values are: low, medium, high, urgent. |
item_not_found |
404 | Feedback item '{id}' not found. |
public_rate_limited |
429 | Rate limit exceeded. Maximum 10 submissions per hour. |
spending_cap_exceeded |
402 | Spending cap exceeded. |
validation_error |
422 | Field-specific validation message. |
not_found |
404 | Resource not found. |
unauthorized |
401 | Authentication required. |
rate_limit_exceeded |
429 | Rate limit exceeded. |
All error responses include a docs URL pointing to https://docs.minolith.io/api/feedback#errors.
ID Format
Item IDs use the prefix fbk_ and note IDs use the prefix fbn_, each followed by 12 random hex characters:
fbk_7f3a9b2c4d1e
fbn_9d4b1e7c3a2f
Always use the full ID (including prefix) when referencing resources in API calls.
Rate Limiting
- Authenticated endpoints: 100 requests per minute per API key (standard). See Platform API — Rate Limiting for full details.
- Public widget endpoint: 10 submissions per IP address per hour per project. This limit is separate from the authenticated rate limit.
MCP Tools
The Feedback service is available via MCP for direct integration with AI coding tools.
Connection
claude mcp add --transport http minolith https://mcp.minolith.io \
--header "Authorization: Bearer mlth_your_api_key_here" \
--header "X-Minolith-Agent: your-agent-name"
submit_feedback
Submit a new feedback item. Costs 1 credit.
Input Schema:
{
"type": "object",
"properties": {
"body": {
"type": "string",
"description": "The feedback content. Max 50,000 characters."
},
"title": {
"type": "string",
"description": "Short summary. Max 500 characters."
},
"type": {
"type": "string",
"enum": ["feedback", "bug", "idea", "question", "praise"],
"description": "Type of feedback. Defaults to feedback."
},
"tags": {
"type": "array",
"items": { "type": "string" },
"description": "Array of tags. Max 10 tags."
},
"page_url": {
"type": "string",
"description": "URL of the page where the feedback originated."
},
"user_email": {
"type": "string",
"description": "Email of the person submitting feedback."
},
"user_name": {
"type": "string",
"description": "Name of the person submitting feedback."
},
"user_id_external": {
"type": "string",
"description": "Your application's user ID for the submitter."
}
},
"required": ["body"]
}
Example:
{
"name": "submit_feedback",
"arguments": {
"body": "The CSV export is missing the created_at column.",
"title": "CSV export missing timestamp",
"type": "bug",
"tags": ["export", "csv"]
}
}
list_feedback
List feedback items with optional filters.
Input Schema:
{
"type": "object",
"properties": {
"status": {
"type": "string",
"enum": ["open", "under_review", "planned", "in_progress", "completed", "declined"],
"description": "Filter by status."
},
"type": {
"type": "string",
"description": "Filter by type. Comma-separated for multiple: 'bug,idea'."
},
"priority": {
"type": "string",
"description": "Filter by priority. Comma-separated for multiple: 'high,urgent'."
},
"tags": {
"type": "string",
"description": "Filter by tags. Comma-separated: 'performance,search'."
},
"user_email": {
"type": "string",
"description": "Filter by submitter email (exact match)."
},
"limit": {
"type": "integer",
"description": "Max items to return (1-100, default 20)."
}
},
"required": []
}
Example:
{
"name": "list_feedback",
"arguments": {
"status": "open",
"type": "bug",
"priority": "high,urgent",
"limit": 50
}
}
get_feedback_item
Get a single feedback item with its notes.
Input Schema:
{
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Feedback item ID (fbk_xxxxxxxx)."
}
},
"required": ["id"]
}
Example:
{
"name": "get_feedback_item",
"arguments": {
"id": "fbk_7f3a9b2c4d1e"
}
}
update_feedback_status
Update the status and/or priority of a feedback item.
Input Schema:
{
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Feedback item ID (fbk_xxxxxxxx)."
},
"status": {
"type": "string",
"enum": ["open", "under_review", "planned", "in_progress", "completed", "declined"],
"description": "New status for the item."
},
"priority": {
"type": "string",
"enum": ["low", "medium", "high", "urgent"],
"description": "New priority for the item."
},
"tags": {
"type": "array",
"items": { "type": "string" },
"description": "Replace all tags on the item."
}
},
"required": ["id"]
}
Example:
{
"name": "update_feedback_status",
"arguments": {
"id": "fbk_7f3a9b2c4d1e",
"status": "in_progress",
"priority": "high"
}
}
add_feedback_note
Add an internal note to a feedback item.
Input Schema:
{
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Feedback item ID (fbk_xxxxxxxx)."
},
"body": {
"type": "string",
"description": "Note content. Max 50,000 characters."
}
},
"required": ["id", "body"]
}
Example:
{
"name": "add_feedback_note",
"arguments": {
"id": "fbk_7f3a9b2c4d1e",
"body": "Root cause identified — missing index on the records table. Fix deployed in v2.5.1."
}
}
Example Workflows
Simple: Collect and triage feedback
Submit feedback from an agent, then triage it.
1. POST /v1/feedback/items
{
"body": "Users are reporting that the dashboard loads slowly after the last deploy.",
"title": "Dashboard performance regression",
"type": "bug",
"tags": ["performance", "dashboard"]
}
-> 201, returns fbk_a3b7c1d9e5f2
2. PUT /v1/feedback/items/fbk_a3b7c1d9e5f2
{ "status": "under_review", "priority": "high" }
-> 200
3. POST /v1/feedback/items/fbk_a3b7c1d9e5f2/notes
{ "body": "Traced to the new analytics query running on every page load. Needs to be moved to a background job." }
-> 201
Agent workflow: Review open bugs before starting work
Use MCP tools to check for open bugs at the start of a coding session.
1. list_feedback { "status": "open", "type": "bug", "priority": "high,urgent" }
-> Returns list of high-priority open bugs
2. get_feedback_item { "id": "fbk_7f3a9b2c4d1e" }
-> Returns full item with notes for context
3. update_feedback_status { "id": "fbk_7f3a9b2c4d1e", "status": "in_progress" }
-> Marks the item as being worked on
4. (after fixing the bug)
add_feedback_note { "id": "fbk_7f3a9b2c4d1e", "body": "Fixed in commit abc123. Added index to records table." }
5. update_feedback_status { "id": "fbk_7f3a9b2c4d1e", "status": "completed" }
Widget: Embed on your app and triage submissions
1. Add the widget to your app:
<script src="https://api.minolith.io/widget/feedback.js"
data-project="my-app"
data-user-email="{{user.email}}"
data-user-id="{{user.id}}"></script>
2. Users submit feedback via the widget.
The widget POSTs to /v1/feedback/public/my-app automatically.
3. GET /v1/feedback/items?status=open&limit=50
-> Review new submissions
4. PUT /v1/feedback/items/fbk_xxx
{ "status": "planned", "priority": "medium", "tags": ["v3.0"] }
-> Triage into your roadmap