Feedback Service
Collecting, storing, and reviewing user feedback on AI responses. Full conversation context with admin panel.
1 Overview
The feedback service allows users to rate AI responses with like or dislike directly in the chat. Each feedback is saved with full context: the user's message, AI response, parsed command, execution result, and the entire conversation history leading up to the feedback moment.
An admin panel is available on the web to review, filter, and manage feedback items. Admins can mark items as resolved or ignored, and leave notes for tracking.
Collect
Users rate AI messages with like/dislike in iOS & Web chat
Store
Full context saved: message, response, command, params, chat history
Review
Admin panel with filters, stats, and status management
2 Data Flow
3 Database Schema
chat_feedback
FEEDBACK| Column | Type | Notes |
|---|---|---|
| id | UUID PK | gen_random_uuid() |
| user_id | UUID NOT NULL | FK → users (CASCADE) |
| message_id | VARCHAR(50) | nanoid from client (no FK — messages are ephemeral) |
| type | VARCHAR(10) NOT NULL | CHECK: like | dislike |
| user_message | TEXT | What the user asked |
| ai_response | TEXT | What AI responded (cleaned of internal markers) |
| command_type | VARCHAR(50) | Parsed command (e.g. add_transaction) |
| command_params | JSONB | Parsed command parameters |
| execution_result | JSONB | Result of command execution |
| chat_history | JSONB | Array of {role, content} — conversation before feedback |
| status | VARCHAR(20) | DEFAULT pending. CHECK: pending | resolved | ignored |
| admin_notes | TEXT | Admin review notes |
| resolved_at | TIMESTAMPTZ | Set when status changes to resolved/ignored |
| created_at | TIMESTAMPTZ | DEFAULT NOW() |
idx_chat_feedback_user (user_id), idx_chat_feedback_type (type), idx_chat_feedback_status (status), idx_chat_feedback_created (created_at DESC)
message_id is a nanoid from the client, stored as a plain string for deduplication (upsert on user_id + message_id).
4 Status Workflow
pending
New feedback, not yet reviewed by admin
resolved
Admin reviewed and addressed the issue
ignored
Admin reviewed and deemed not actionable
5 API Endpoints
Submit or update feedback for a message. Uses upsert logic: if feedback from the same user for the same message exists, it updates the record; otherwise creates a new one.
Request body:
{
"type": "like" | "dislike", // required
"userMessage": "Add 500 for food", // what user asked
"aiResponse": "Added expense 500...", // AI response (cleaned)
"commandType": "add_transaction", // parsed command name
"commandParams": { // parsed parameters
"amount": 500,
"category": "food"
},
"executionResult": { ... }, // action result
"chatHistory": [ // conversation context
{ "role": "user", "content": "..." },
{ "role": "assistant", "content": "..." }
]
}Response: { "success": true }
List feedback with optional filters and pagination. Returns items sorted by created_at DESC. Includes user email via join.
Query parameters:
status=pending|resolved|ignored // optional filter
type=like|dislike // optional filter
page=1 // default: 1
limit=20 // default: 20, max: 100Response:
{
"success": true,
"data": [
{
"id": "uuid",
"user_id": "uuid",
"message_id": "nanoid",
"type": "dislike",
"user_message": "...",
"ai_response": "...",
"command_type": "add_transaction",
"command_params": { ... },
"execution_result": { ... },
"chat_history": [ ... ],
"status": "pending",
"admin_notes": null,
"resolved_at": null,
"created_at": "2026-01-29T12:00:00Z",
"users": { "email": "user@example.com" }
}
],
"pagination": {
"page": 1,
"limit": 20,
"total": 42,
"hasMore": true
}
}Quick summary statistics for the feedback dashboard.
Response:
{
"success": true,
"data": {
"total": 42, // all feedback items
"pending": 15, // not yet reviewed
"dislikes": 28 // total dislike count
}
}Update the status and/or admin notes for a specific feedback item. When status changes to resolved or ignored, resolved_at is set automatically. When changed back to pending, resolved_at is cleared.
Request body:
{
"status": "resolved", // optional
"adminNotes": "Fixed AI parsing" // optional
}Response: { "success": true, "data": { ... updated item ... } }
6 Admin Panel (Web)
The admin panel is a React page at /feedback on the web app. It provides a full management interface for feedback items.
https://monaime.app/feedback in the browser. The admin routes are currently public (no auth required).
Features:
- Stats cards — total, pending (yellow), dislikes (red)
- Filters — by status (pending/resolved/ignored) and type (like/dislike)
- Expandable cards — click to expand full details
- Collapsed view — type icon, status badge, command type, timestamp, preview of user message and AI response, user email
- Expanded view — full user message, full AI response, conversation history (scrollable), command details (type, params JSON, result JSON), admin notes textarea, status action buttons
- Infinite pagination — “Load more” button with TanStack Query infinite queries
7 Stored Context per Feedback
Each feedback item stores enough context to fully understand and reproduce the situation:
// Example stored feedback item
{
"type": "dislike",
"user_message": "Add 500 for taxi from cash",
"ai_response": "Added expense: 500 RUB for Taxi from Cash wallet",
"command_type": "add_transaction",
"command_params": {
"amount": 500,
"type": "expense",
"category": "Transport",
"wallet": "Cash",
"description": "Taxi"
},
"execution_result": {
"success": true,
"transaction_id": "abc-123"
},
"chat_history": [
{ "role": "user", "content": "What's my balance?" },
{ "role": "assistant", "content": "Cash: 10,000 RUB, Card: 25,000 RUB" },
{ "role": "user", "content": "Add 500 for taxi from cash" },
{ "role": "assistant", "content": "Added expense: 500 RUB for Taxi from Cash wallet" }
]
}<!-- SUGGEST:command --> are stripped from the AI response. Only the visible text is saved.