Lupr is currently under development.

Developer integration

Embed a feedback portal in your app.

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.

Why developers choose Lupr

Unlike generic feedback tools, Lupr is built for the full loop — from user signal to shipped code.

Votes + status labels

Users upvote feedback. You set status: under consideration, planned, in progress, addressed. Everyone sees progress.

Owner responses

Respond directly to feedback. Your responses appear publicly so users know you're listening.

Feedback → GitHub → shipped

Dispatch validated feedback as GitHub issues. Coding agents pick them up. The full loop closes automatically.

Confidentiality gating

Gate sensitive feedback with pledges or NDAs. No other feedback tool offers this for early-stage ideas.

AI-powered insights

Lupr spots patterns across feedback and suggests improvements ranked by impact. Not just collection — intelligence.

Validation scoring

Every project gets a validation score based on "Would you use this?" responses. Track user demand over time.

Four ways to integrate

Choose the approach that fits your stack. All methods use the same public API.

Embed widget

One script tag. Shows an in-app feedback panel.

REST API

Fetch feedback data and build your own UI.

Badge embed

SVG badge for READMEs, landing pages, decks.

Custom domain

Your domain, your brand. Full feedback portal.

1. Embed widget

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>:

index.htmlHTML
<!-- 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>
React / Next.jsTSX
// 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" />
Widget preview
FeedbackPowered by Lupr
24
Add dark mode support
Planned

Owner: Coming in v2.1!

18
Mobile app notifications are delayed
In progress
12
Export to CSV would save us hours
Under consideration

2. REST API

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.

Get feedback feed

Returns your project info, aggregate stats, and public feedback sorted by votes. Includes status labels, owner responses, and validation score.

GET /api/pitches/{projectId}/widgetcurl
curl https://lupr.dev/api/pitches/YOUR_PROJECT_ID/widget\
  ?sort=votes\
  &limit=10\
  &status=PLANNED
ResponseJSON
{
  "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..."
    }
  ]
}

Query parameters

ParamTypeDescription
sortstring"votes" (default) or "recent"
limitnumberNumber of results, 1–50 (default 10)
statusstringFilter by status: OPEN, UNDER_CONSIDERATION, PLANNED, IN_PROGRESS, ADDRESSED, NOT_NOW, THANK_YOU

Get badge data

Returns validation score, feedback count, avg rating, and validation status.

GET /api/pitches/{projectId}/badgecurl
curl https://lupr.dev/api/pitches/YOUR_PROJECT_ID/badge
ResponseJSON
{
  "pitchTitle": "My SaaS App",
  "pmfScore": 78,
  "feedbackCount": 47,
  "avgRating": 4.1,
  "validated": true
}

Status labels

Set status on feedback from your Lupr dashboard to communicate progress to users.

OpenUnder considerationPlannedIn progressAddressedNot nowThank you

3. Badge embed

Show your validation score anywhere — GitHub READMEs, landing pages, investor decks. Enable the badge from your project settings.

HTMLHTML
<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>
Markdown (GitHub README)Markdown
[![Validated by Lupr](https://lupr.dev/api/pitches/YOUR_PROJECT_ID/badge/svg)](https://lupr.dev/p/YOUR_PROJECT_ID)

Badge examples

Validated by Lupr78% would use
Validated by Lupr45% would use
Validated by Luprpending

4. Custom domain

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.

Custom slug (instant)

Replace the auto-generated project ID with a readable URL. No DNS changes needed.

lupr.dev/p/clx7k9m2a...
lupr.dev/p/my-app

Set from Project Settings → Domain tab.

Custom domain (your brand)

Point your own domain to Lupr. Your feedback portal lives at your URL, fully branded.

lupr.dev/p/my-app
feedback.yourapp.com

Requires 2 DNS records (CNAME + TXT).

Setup steps

1

Choose your domain

Go to your project → Domain tab. Enter your domain (e.g. feedback.yourapp.com).

2

Add DNS records

In your DNS provider, add these records:

TypeNameValue
CNAMEfeedbackcname.lupr.dev
TXT_lupr-verify.feedback(token shown in settings)
3

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.

Framework examples

Build a custom feedback board with the widget API. Here are starter examples.

React — Custom feedback boardTSX
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>
  );
}
Vue.js — Feedback listVue
<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>
Python (Flask / Django) — Server-side fetchPython
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 data

The feedback-to-code loop

Lupr 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.

User feedback Votes + ranking Status: Planned GitHub issue Agent PR Addressed

Ready to integrate?

Create a project, enable the badge, and start embedding feedback in minutes. Free to start.