Lupr is currently under development.
One script tag or API call. Your users see top feedback ranked by votes, status updates from you, and can submit their own — without leaving your app.
Unlike generic feedback tools, Lupr is built for the full loop — from user signal to shipped code.
Users upvote feedback. You set status: under consideration, planned, in progress, addressed. Everyone sees progress.
Respond directly to feedback. Your responses appear publicly so users know you're listening.
Dispatch validated feedback as GitHub issues. Coding agents pick them up. The full loop closes automatically.
Gate sensitive feedback with pledges or NDAs. No other feedback tool offers this for early-stage ideas.
Lupr spots patterns across feedback and suggests improvements ranked by impact. Not just collection — intelligence.
Every project gets a validation score based on "Would you use this?" responses. Track user demand over time.
Choose the approach that fits your stack. All methods use the same public API.
One script tag. Shows an in-app feedback panel.
Fetch feedback data and build your own UI.
SVG badge for READMEs, landing pages, decks.
Your domain, your brand. Full feedback portal.
Add a floating feedback panel to your app. Users see top feedback sorted by votes, with status labels and your responses. Paste this before </body>:
<!-- Lupr Feedback Widget -->
<script>
(function() {
var projectId = 'YOUR_PROJECT_ID';
var s = document.createElement('script');
s.src = 'https://lupr.dev/widget.js';
s.setAttribute('data-project', projectId);
s.async = true;
document.body.appendChild(s);
})();
</script>// components/LuprWidget.tsx
'use client';
import { useEffect } from 'react';
export function LuprWidget({ projectId }: { projectId: string }) {
useEffect(() => {
const script = document.createElement('script');
script.src = 'https://lupr.dev/widget.js';
script.setAttribute('data-project', projectId);
script.async = true;
document.body.appendChild(script);
return () => { script.remove(); };
}, [projectId]);
return null;
}
// Usage in layout.tsx or page.tsx:
// <LuprWidget projectId="YOUR_PROJECT_ID" />Owner: Coming in v2.1!
Fetch your project's feedback data and build a completely custom UI. All endpoints are public, CORS-enabled, and require the badge to be enabled on your project.
Returns your project info, aggregate stats, and public feedback sorted by votes. Includes status labels, owner responses, and validation score.
curl https://lupr.dev/api/pitches/YOUR_PROJECT_ID/widget\
?sort=votes\
&limit=10\
&status=PLANNED{
"project": {
"id": "clx...",
"title": "My SaaS App",
"summary": "A tool that helps teams..."
},
"stats": {
"totalFeedback": 47,
"publicFeedback": 10,
"pmfScore": 78,
"statusBreakdown": {
"OPEN": 5,
"PLANNED": 2,
"ADDRESSED": 3
}
},
"feedback": [
{
"id": "fb_...",
"text": "Add dark mode support",
"author": "Sarah K.",
"wouldUse": "yes",
"avgRating": 4.2,
"status": "PLANNED",
"ownerResponse": "Coming in v2.1!",
"ownerRespondedAt": "2026-02-28T...",
"tags": ["feature-request", "ui"],
"votes": 24,
"createdAt": "2026-02-15T..."
}
]
}| Param | Type | Description |
|---|---|---|
| sort | string | "votes" (default) or "recent" |
| limit | number | Number of results, 1–50 (default 10) |
| status | string | Filter by status: OPEN, UNDER_CONSIDERATION, PLANNED, IN_PROGRESS, ADDRESSED, NOT_NOW, THANK_YOU |
Returns validation score, feedback count, avg rating, and validation status.
curl https://lupr.dev/api/pitches/YOUR_PROJECT_ID/badge{
"pitchTitle": "My SaaS App",
"pmfScore": 78,
"feedbackCount": 47,
"avgRating": 4.1,
"validated": true
}Set status on feedback from your Lupr dashboard to communicate progress to users.
Show your validation score anywhere — GitHub READMEs, landing pages, investor decks. Enable the badge from your project settings.
<a href="https://lupr.dev/p/YOUR_PROJECT_ID" target="_blank">
<img
src="https://lupr.dev/api/pitches/YOUR_PROJECT_ID/badge/svg"
alt="Validated by Lupr"
/>
</a>[](https://lupr.dev/p/YOUR_PROJECT_ID)Badge examples
Use your own domain for the feedback portal instead of a Lupr URL. Your users see feedback.yourapp.com instead of lupr.dev/p/abc123.
Replace the auto-generated project ID with a readable URL. No DNS changes needed.
Set from Project Settings → Domain tab.
Point your own domain to Lupr. Your feedback portal lives at your URL, fully branded.
Requires 2 DNS records (CNAME + TXT).
Choose your domain
Go to your project → Domain tab. Enter your domain (e.g. feedback.yourapp.com).
Add DNS records
In your DNS provider, add these records:
| Type | Name | Value |
|---|---|---|
| CNAME | feedback | cname.lupr.dev |
| TXT | _lupr-verify.feedback | (token shown in settings) |
Verify
Click “Verify domain” in the Domain tab. Once DNS propagates (usually 1-5 minutes), your portal is live at your domain.
Tip: Custom slugs work instantly and are great for sharing. Custom domains are best for public-facing portals where you want to match your brand. Both can be used together — the custom domain takes priority when configured.
Build a custom feedback board with the widget API. Here are starter examples.
import { useEffect, useState } from 'react';
const STATUS_COLORS = {
OPEN: 'bg-gray-100 dark:bg-gray-800 text-gray-600 dark:text-gray-400',
UNDER_CONSIDERATION: 'bg-blue-50 dark:bg-blue-950 text-blue-700 dark:text-blue-400',
PLANNED: 'bg-purple-50 dark:bg-purple-950 text-purple-700 dark:text-purple-400',
IN_PROGRESS: 'bg-amber-50 dark:bg-amber-950 text-amber-700 dark:text-amber-400',
ADDRESSED: 'bg-green-50 dark:bg-green-950 text-green-700 dark:text-green-400',
};
export function FeedbackBoard({ projectId }) {
const [data, setData] = useState(null);
useEffect(() => {
fetch(`https://lupr.dev/api/pitches/${projectId}/widget?sort=votes&limit=20`)
.then(r => r.json())
.then(setData);
}, [projectId]);
if (!data) return <p>Loading feedback...</p>;
return (
<div>
<h2>{data.project.title} — Feedback</h2>
<p>{data.stats.totalFeedback} responses · {data.stats.pmfScore}% would use</p>
{data.feedback.map(fb => (
<div key={fb.id} style={{ display: 'flex', gap: 12, padding: 12 }}>
<div style={{ textAlign: 'center' }}>
<span>▲</span>
<div>{fb.votes}</div>
</div>
<div>
<strong>{fb.text}</strong>
<span className={STATUS_COLORS[fb.status]}>{fb.status}</span>
<p>— {fb.author}</p>
{fb.ownerResponse && (
<blockquote>Owner: {fb.ownerResponse}</blockquote>
)}
</div>
</div>
))}
</div>
);
}<template>
<div v-if="data">
<h2>{{ data.project.title }} — Feedback</h2>
<p>{{ data.stats.pmfScore }}% would use · {{ data.stats.totalFeedback }} responses</p>
<div v-for="fb in data.feedback" :key="fb.id" class="feedback-item">
<div class="votes">▲ {{ fb.votes }}</div>
<div>
<strong>{{ fb.text }}</strong>
<span :class="statusClass(fb.status)">{{ fb.status }}</span>
<p v-if="fb.ownerResponse" class="response">
Owner: {{ fb.ownerResponse }}
</p>
</div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue';
const props = defineProps({ projectId: String });
const data = ref(null);
onMounted(async () => {
const res = await fetch(
`https://lupr.dev/api/pitches/${props.projectId}/widget?sort=votes`
);
data.value = await res.json();
});
</script>import requests
def get_lupr_feedback(project_id, sort="votes", limit=10):
"""Fetch top feedback from Lupr for display in your app."""
resp = requests.get(
f"https://lupr.dev/api/pitches/{project_id}/widget",
params={"sort": sort, "limit": limit},
)
resp.raise_for_status()
data = resp.json()
print(f"Project: {data['project']['title']}")
print(f"Validation Score: {data['stats']['pmfScore']}%")
print(f"Total feedback: {data['stats']['totalFeedback']}\n")
for fb in data["feedback"]:
print(f" [{fb['votes']} votes] {fb['text']}")
print(f" Status: {fb['status']} | By: {fb['author']}")
if fb.get("ownerResponse"):
print(f" → Owner: {fb['ownerResponse']}")
print()
return dataLupr doesn't just collect feedback. It closes the loop — user signal becomes code that ships, and status labels keep your users informed at every step.
Create a project, enable the badge, and start embedding feedback in minutes. Free to start.