Developer docs
Get started in 5 minutes.
feedback-hub is open for anyone to use. Sign in, create a project, drop the widget on your app (or POST raw JSON from anywhere), and your users’ bugs, ideas, and praise land in your own private inbox — readable from the browser or from Claude via MCP.
Sign up & create a project
Go to /login and enter your email — feedback-hub sends a 6-digit code and a magic link (use whichever). No password, no profile setup.
Once signed in, hit /projects/new and name your project (e.g. my-app-prod). You’ll get an API key shaped pk_<32 hex chars>shown once. Save it — refreshing the page hides it, and it’s not recoverable. The key is meant to be public (it ships in your client-side bundle); your inbox is protected by row-level security, not by the key’s secrecy.
Integrate the widget
The fastest path for HTML, Next, Vite, Vue, Svelte, Astro — any web stack. One <script> tag, no install.
<!-- 1. Set the user (optional but recommended for reply-to-reporter) -->
<script>
window.feedbackHubUser = { id: "u_123", email: "[email protected]" };
</script>
<!-- 2. Drop in the bundle -->
<script
async
src="https://feedback-hub.orosa.io/standalone.js"
data-key="pk_…your project key…"
data-endpoint="https://feedback-hub.orosa.io"
data-accent="#5e6ad2"
data-position="bottom-right"
></script>That’s it. The script auto-mounts a floating FAB in the bottom-right corner. Capture, annotate, send. Console errors and uncaught exceptions are buffered from page load and attached automatically.
React component (typed)
For React apps that prefer an explicit component:
// Install (tarball or workspace link — see notes below)
// npm install file:./feedback-hub-widget-x.y.z.tgz
import { FeedbackWidget } from "@feedback-hub/widget";
export default function AppShell({ children, user }) {
return (
<>
{children}
<FeedbackWidget
apiKey={process.env.NEXT_PUBLIC_FEEDBACK_KEY!}
endpoint="https://feedback-hub.orosa.io"
user={user ? { id: user.id, email: user.email } : undefined}
theme={{ accent: "#5e6ad2" }}
/>
</>
);
}The package isn’t on npm yet (still pre-naming). Until then: pack via npm pack from packages/widget/ and install the resulting .tgz, OR just use the <script> tag above — it ships "use client" baked in and works the same.
Or call the API directly
The widget is a thin UI over a plain JSON API. If you want to build your own UI — or you’re on a stack the widget doesn’t fit (CLI, electron, embedded device, in-app web view) — POST directly:
curl -X POST https://feedback-hub.orosa.io/api/feedback/ingest \
-H "content-type: application/json" \
-H "x-feedback-key: pk_…your project key…" \
-d '{
"type": "bug",
"body": "Tooltip overflows on iPad portrait at 768px wide.",
"url": "https://myapp.com/dashboard",
"viewport": { "w": 768, "h": 1024, "dpr": 2 },
"user_agent": "Mozilla/5.0 ...",
"reporter_email": "[email protected]"
}'Endpoint reference
| POST | /api/feedback/ingest | Submit a feedback item. Returns { ok: true, id }. |
| POST | /api/feedback/screenshot-url | Get a signed PUT URL to upload a PNG. Returns { url, path }. Include the path as screenshot_path in the next ingest call. |
| POST | /api/mcp | JSON-RPC MCP endpoint. Bearer-auth with a personal access token. |
Request shape
| type | enum | bug | idea | praise | question |
| body | string | 1–10 000 chars. The actual feedback. |
| url | string? | Where the user was. Optional but very useful. |
| viewport | object? | { w, h, dpr } in CSS pixels. |
| user_agent | string? | Up to 500 chars. |
| reporter_user_id | string? | Opaque id from your auth system. |
| reporter_email | string? | If they want a reply, include it. |
| screenshot_path | string? | {project_id}/{uuid}.png — from the screenshot-url endpoint. |
| console_log | array? | Up to 50 { level, message, stack?, ts } entries. |
Headers
| x-feedback-key | Your pk_… project key. Required on /api/feedback/*. |
| content-type | application/json |
| Authorization | Bearer fhmcp_… — only for /api/mcp. |
Errors come back as { error, message? } with status 400 / 401 / 413 / 429 / 500. CORS is wide open (*) so the endpoint works from any origin.
Flutter, iOS, Android, anything else
Same JSON API. If your stack can http.post(...), you can integrate. There’s no Flutter widget package yet; here’s a 30-line Dart sketch:
// Minimal: just POST the JSON. ~30 lines of Dart, no package needed.
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:flutter/widgets.dart';
// (snapshot via RepaintBoundary.toImage() not shown — see Flutter docs)
Future<void> sendFeedback({
required BuildContext context,
required String body,
required String type, // 'bug' | 'idea' | 'praise' | 'question'
String? screenshotPath,
String? userId,
String? userEmail,
}) async {
final size = MediaQuery.of(context).size;
final dpr = MediaQuery.of(context).devicePixelRatio;
final route = ModalRoute.of(context)?.settings.name ?? '/';
final res = await http.post(
Uri.parse('https://feedback-hub.orosa.io/api/feedback/ingest'),
headers: {
'content-type': 'application/json',
'x-feedback-key': const String.fromEnvironment('FEEDBACK_KEY'),
},
body: jsonEncode({
'type': type,
'body': body,
'url': 'app://' + route,
'viewport': {
'w': size.width.toInt(),
'h': size.height.toInt(),
'dpr': dpr,
},
'user_agent': 'flutter/myapp 1.0.0',
'reporter_user_id': userId,
'reporter_email': userEmail,
'screenshot_path': screenshotPath,
}),
);
if (res.statusCode != 200) {
throw Exception('Feedback send failed: ' + res.body);
}
}For screenshots, Flutter has native screen capture via RepaintBoundary.toImage() — no html2canvas equivalent needed. Two-step upload: ask /api/feedback/screenshot-url for a signed URL, PUT the PNG bytes, include the returned path as screenshot_path in the ingest call.
For console / error capture, hook FlutterError.onError + PlatformDispatcher.onError into a ring buffer and pass it as the console_log array.
MCP — let Claude triage
feedback-hub speaks the Model Context Protocol. Any MCP-aware client (Claude Code, Claude Desktop, Cursor) can read your inbox, see your screenshots, and move items through the pipeline.
Generate a token at /settings/tokens. Tokens are scoped to your projects only — generate one per device / per integration and revoke individually.
Claude Code
claude mcp add --transport http feedback-hub \
https://feedback-hub.orosa.io/api/mcp \
--header "Authorization: Bearer fhmcp_…your token…"Claude Desktop / Cursor
{
"mcpServers": {
"feedback-hub": {
"url": "https://feedback-hub.orosa.io/api/mcp",
"headers": {
"Authorization": "Bearer fhmcp_…your token…"
}
}
}
}Three tools are exposed:
list_feedback— query the inbox by project, status, type, or substring.get_feedback— full row including the screenshot inlined as a base64 PNG so the model can actually see the bug.update_status— move items through the pipeline.
See the dedicated /mcp setup page for a transcript example and copy-paste-ready snippets.
Multi-user notes
Each signed-in user has their own inbox. Row-level security gates everything on the project owner’s user id, so you can’t see another person’s projects or feedback rows even if you both sign up here.
Sharing access with teammates— there’s no built-in collaborator model yet. Two practical options today:
- Each teammate signs up and creates their own projects (good for independent products).
- Generate an MCP token at /settings/tokens and share it — they wire it into their Claude Code and get read+write access to your inbox over MCP. Revoke the token anytime to cut them off.
A real teams-per-project model is on the roadmap (see docs/ROADMAP.md) — kicked in when a second product actually needs it.
Limits & policies
| Body length | 1–10,000 characters per submission. |
| Payload size | 100 KB max on /api/feedback/ingest. |
| Daily cap | 100 non-owner submissions per project per rolling 24h. Owner submissions bypass. |
| Screenshot size | 10 MB max per upload. PNG / JPEG / WebP. |
| Console log entries | 50 max per submission, 2 KB per message, 8 KB per stack. |
| MCP token format | fhmcp_<48 hex chars>. SHA-256 hashed at rest. |
| Auth tokens | Sign-in codes expire in 5 minutes; sessions follow Supabase default (~1 hour access, 1 week refresh). |
Stuck? Email [email protected] . feedback-hub is internal-tool-grade right now (one box, no SLA) — file a real bug at /login and it lands in the same inbox.