ProposalWriterAI is a full-stack SaaS app for generating client-ready business proposals with Claude AI. Users can sign up, upload company knowledge documents, generate proposals from a short form, review prior proposals, and export proposals as formatted PDFs.
Live app: https://proposal-writer-ai.vercel.app
- AI proposal generation - Claude writes complete proposals from client details, your solution, pricing, and company context.
- Streaming output - Proposals stream into the UI token-by-token over Server-Sent Events as Claude writes them.
- Win/loss learning - Mark past proposals as won, lost, or pending; winning proposals are fed back into new generations as positive examples and losing ones as patterns to avoid.
- Company document grounding - Users can upload PDF or TXT documents such as service descriptions, pricing sheets, and case studies.
- In-app editing & regenerate - Generated proposals can be edited in place, regenerated from the same inputs, or copied to the clipboard.
- Proposal history - Generated proposals are saved per user and can be opened again later.
- PDF export - Proposals are converted to branded, client-ready PDF downloads with ReportLab.
- Supabase authentication - Email/password signup and login with bearer-token protected API routes.
- Private user data - Documents, proposals, and subscription records are scoped by the authenticated Supabase user.
- Free-tier limits & upgrade - Free accounts are capped at a server-enforced proposal limit, with an in-app usage indicator and Stripe Checkout upgrade to Pro.
- Landing, pricing, and legal pages - The frontend includes the marketing page, pricing cards, FAQ, privacy, terms, and security modal content.
- Stripe subscription support - The API includes checkout-session, webhook, and subscription-check endpoints for a Pro subscription flow.
- React 19
- Vite 8
- Plain CSS in
frontend/src/index.cssandfrontend/src/App.css react-hot-toastfor notifications- Supabase JS client dependency available in the frontend package
- Vercel deployment
- FastAPI
- Uvicorn
- Anthropic Claude API
- Supabase Auth and PostgreSQL
- PyPDF2 for PDF text extraction
- ReportLab for PDF generation
- Stripe Python SDK for subscription endpoints
- Render deployment
ProposalWriterAI/
├── backend/
│ ├── main.py # FastAPI app, auth, proposals, documents, PDFs, Stripe endpoints
│ ├── requirements.txt # Python dependencies
│ ├── runtime.txt # Render Python runtime
│ └── .env.example # Backend environment variable template
├── frontend/
│ ├── public/ # Icons and static assets
│ ├── src/
│ │ ├── App.jsx # Authenticated dashboard and proposal workflow
│ │ ├── Landing.jsx # Landing page, pricing, FAQ, legal/security modals
│ │ ├── index.css # Main styling
│ │ ├── App.css
│ │ └── assets/ # Logo and image assets
│ ├── package.json
│ └── vite.config.js
└── README.md
- Python 3.11+
- Node.js 18+
- A Supabase project
- An Anthropic API key
- Stripe keys if you want to test subscription endpoints
cd backend
python -m venv venv
source venv/bin/activate
pip install -r requirements.txtCreate backend/.env:
ANTHROPIC_API_KEY=sk-ant-your-key-here
SUPABASE_URL=https://your-project.supabase.co
SUPABASE_KEY=your-anon-key-here
STRIPE_SECRET_KEY=sk_test_your-secret-key-here
STRIPE_PUBLISHABLE_KEY=pk_test_your-publishable-key-here
STRIPE_WEBHOOK_SECRET=whsec_your-webhook-secretRun the backend:
cd backend
source venv/bin/activate
uvicorn main:app --reloadThe API runs at http://localhost:8000.
cd frontend
npm install
npm run devOpen http://localhost:5173.
The frontend uses http://localhost:8000 when running on localhost; otherwise it calls the deployed API at https://proposalwriterai-api.onrender.com.
The backend expects these tables to exist:
documents- uploaded document text and metadataproposals- generated proposal inputs and output textsubscriptions- Stripe-backed plan status
The app code scopes queries by user_id. In production, keep Row Level Security enabled and add policies so users can only access rows where user_id = auth.uid().
The win/loss learning feature requires a status column on proposals. Run this once in
the Supabase SQL editor (there is no migration system in the repo, so schema changes are
applied manually):
ALTER TABLE proposals ADD COLUMN status text DEFAULT 'pending'; -- values: won | lost | pendingThis backfills existing rows to pending. Until this column exists, proposal generation
still works normally (the outcome lookup fails open and simply contributes nothing), but
marking a proposal won/lost will error.
The proposals table also needs an UPDATE RLS policy (separate from SELECT/INSERT) for
both win/loss marking and in-app text editing to work. If updates return "proposal not
found" even though the proposal is visible, add:
create policy "Users can update own proposals"
on proposals for update
using (auth.uid() = user_id)
with check (auth.uid() = user_id);GET /- API status check
POST /auth/signup- Create a Supabase Auth userPOST /auth/login- Sign in and return the Supabase session token
POST /documents/upload- Upload a PDF or TXT documentGET /documents- List the authenticated user's documentsDELETE /documents/{document_id}- Delete one of the authenticated user's documents
POST /generate-proposal- Generate and save a proposal using Claude (non-streaming)POST /generate-proposal-stream- Generate a proposal and stream it token-by-token via Server-Sent Events, then save itGET /proposals- List saved proposals for the authenticated userPATCH /proposals/{proposal_id}- Update a proposal's text (in-app editor) and/or its win/lossstatusPOST /proposals/{proposal_id}/download- Generate a PDF download payload for a saved proposal
POST /create-checkout-session- Create a Stripe subscription checkout session (returns the Checkout URL)POST /webhook/stripe- Receive Stripe subscription events and update SupabaseGET /check-subscription- Return the authenticated user's current plan statusGET /usage- Return the user's plan plus their proposal count and remaining free allowance
- The user logs in and optionally uploads company documents.
- Uploaded PDFs are parsed with PyPDF2; TXT files are decoded as UTF-8.
- Document text is stored in Supabase.
- When the user generates a proposal, the backend retrieves that user's documents and injects document excerpts into the Claude prompt.
- The backend also pulls the user's most recent won and lost proposals (if any) and injects them — winners as examples to mirror, losers as patterns to avoid (excerpts are truncated to keep token cost bounded).
- Claude streams back a structured proposal with sections such as Executive Summary, The Problem, Our Solution, Investment, and Next Steps; the frontend renders it live as it arrives.
- The proposal is saved to Supabase and can be edited, regenerated, marked won/lost, or downloaded as a PDF.
This is context injection over stored documents and past outcomes, not vector-search RAG yet.
- Root directory:
frontend - Build command:
npm run build - Output directory:
dist
- Root directory:
backend - Build command:
pip install -r requirements.txt - Start command:
uvicorn main:app --host 0.0.0.0 --port $PORT - Runtime: Python 3.11.7 from
backend/runtime.txt
Set the same backend environment variables in Render:
ANTHROPIC_API_KEYSUPABASE_URLSUPABASE_KEYSTRIPE_SECRET_KEYSTRIPE_WEBHOOK_SECRET
- The Stripe checkout flow is wired into the dashboard and free-tier proposal limits are enforced server-side.
STRIPE_WEBHOOK_SECRETis still required for the webhook that flips a user to Pro after payment. - Document and outcome grounding currently send excerpts directly in the Claude prompt. Vector embeddings and semantic retrieval would be a future improvement.
- The repository does not currently include Supabase migration files, so database tables, the
proposals.statuscolumn, and RLS policies must be created manually (see Supabase Setup).
# Frontend
cd frontend
npm run dev
npm run build
npm run lint
# Backend
cd backend
source venv/bin/activate
uvicorn main:app --reload- Add Supabase migrations and documented RLS policies
- Add vector embeddings for semantic document and outcome retrieval
- Weight outcome examples by similarity to the current client, not just recency
- Add proposal templates
- Add CRM or email integrations