# PRD: Quote-to-Cash Automation for Contractors

> **Version:** v1
> **Generated:** 2026-05-14
> **Direction:** field-service-management-ai
> **Score:** 32/40 — BUILD

---

## 1. Problem & User

**Target User:** Independent and small HVAC, plumbing, and electrical contractors (1–10 person teams) who handle their own estimating, invoicing, and collections.

**Core Problem:** Converting an on-site estimate into a professional quote takes 1–3 days using manual templates, email, and spreadsheets. During that window, customers move on. Even when quotes are approved, invoice follow-up is done manually by phone or text — resulting in slow or missed payments. Enterprise FSM tools like ServiceTitan cost $300+/user/month and require implementation consultants, pricing them out entirely.

**User Quote:** "报价太慢、转化差 — 现场估价到书面报价需要1-3天，客户流失率高；ServiceTitan 太贵（$300+/mo per user）"

---

## 2. Target Outcome & KPIs

- Quote turnaround from job site to customer: **< 2 minutes**
- Invoice payment rate within 14 days: **> 70%** (vs. industry avg ~50%)
- Time to first paid invoice for new user (Aha Moment): **< 5 minutes after registration**
- Monthly paid conversion from free trial: **> 8%**

---

## 3. MVP Scope (In)

- Mobile-first web app (Next.js, works on phone browser)
- Customer creation (name, phone, address)
- Quick quote builder: field notes + line items + total → branded quote page
- Quote sent via email with "Approve" button
- One-click convert approved quote → invoice
- Stripe payment link on invoice
- Automatic payment reminders at Day 3 / Day 7 / Day 14 (email)
- Dashboard: Draft / Sent / Approved quotes, Unpaid / Paid invoices
- Registration + login (Supabase Auth)
- Subscription gate: Stripe, $29/mo solo, $79/mo team

---

## 4. Out of Scope

- Native mobile app (iOS/Android)
- Route optimization / GPS dispatch
- Inventory management
- Payroll or accounting sync (QuickBooks, Xero)
- AI-based field note parsing (plain text template only)
- SMS delivery (email only for v1)
- Multi-branch / franchise support

---

## 5. User Flow (Happy Path)

1. Contractor registers → onboarding wizard: "Enter your first customer"
2. Creates customer (name, phone, job address)
3. Taps "New Quote" → types field notes in plain text + adds line items + sets total
4. Taps "Send Quote" → system generates branded quote page, emails customer
5. Customer opens email, clicks "Approve" → quote status flips to Approved
6. Contractor taps "Convert to Invoice" → invoice created with Stripe payment link
7. Customer pays via Stripe → invoice marked Paid → contractor sees dashboard update
8. If unpaid after 3 / 7 / 14 days: system auto-sends reminder emails

**Aha Moment:** Within 5 minutes of registration, contractor sends their first quote to a real customer and sees it show up as "Sent" in the dashboard — zero template fiddling, zero PDF export.

**Paywall Trigger:** 6th quote created → paywall modal: "You've sent 5 quotes free. Upgrade to keep the jobs flowing."

---

## 6. Functional Requirements (P0 Only)

### Auth & Onboarding
- Email/password registration + login via Supabase Auth
- Onboarding: force-create first customer + quote before reaching dashboard
- Free plan: up to 5 quotes lifetime (no credit card required)

### Quote Builder
- Form fields: Customer (dropdown or inline create), Job Address, Notes (textarea), Line Items (description + amount, repeatable), Total (auto-sum)
- Generate public quote URL (e.g. `/q/[id]`) — no auth required for customer
- "Approve" button on public quote page → sets `status = approved`, records timestamp

### Invoice & Payment
- Convert quote → invoice: one click, pre-fills all fields
- Attach Stripe payment link (Stripe Checkout session) to invoice
- Track payment status via Stripe webhook → update `invoices.paid_at`

### Automated Reminders
- On invoice creation: schedule reminder queue (Day 3, 7, 14)
- Supabase cron / server cron checks unpaid invoices each morning
- Send reminder email with payment link if still unpaid

### Dashboard
- Tabs: Quotes (Draft / Sent / Approved), Invoices (Unpaid / Paid)
- Each row: customer name, amount, date, status, action button

### Subscription
- Stripe Checkout for subscription ($29/$79)
- Paywall at 6th quote: block creation, show upgrade modal
- Webhook: `customer.subscription.created` → unlock unlimited

---

## 7. Data Model (Minimal)

```
users (id, email, plan, stripe_customer_id, created_at)
customers (id, user_id, name, phone, address)
quotes (id, user_id, customer_id, notes, line_items jsonb, total, status, public_token, sent_at, approved_at)
invoices (id, user_id, quote_id, stripe_checkout_url, paid_at, reminder_sent_days int[])
reminder_queue (id, invoice_id, scheduled_day, sent_at)
```

---

## 8. API / Integration Notes

- **Supabase**: Auth, Postgres, cron via `pg_cron` or Edge Functions for reminder sweep
- **Stripe**: Subscription (Products + Prices), Checkout Sessions for invoice payment, Webhooks for `checkout.session.completed`
- **Email**: Resend or Nodemailer + Gmail SMTP for quote delivery + reminders
- **PDF**: Browser print styles on quote/invoice page (no server-side PDF library)

---

## 9. Acceptance Criteria

- [ ] `POST /api/quotes` with valid body returns 201 + quote with `public_token`
- [ ] Public quote page `/q/[token]` loads without auth, shows Approve button
- [ ] Clicking Approve sets `quotes.status = approved` and shows confirmation
- [ ] Convert to invoice: `POST /api/invoices` creates record + Stripe Checkout URL
- [ ] Stripe payment completion: webhook updates `invoices.paid_at`, dashboard reflects Paid
- [ ] Reminder cron: unpaid invoice at Day 3 triggers email with payment link
- [ ] 6th quote creation blocked with paywall modal for free-plan users
- [ ] Stripe subscription purchase → `users.plan = pro`, quote creation unblocked

---

## 10. Delivery Plan

### Milestone 1 — Auth + Customer + Quote (Day 1–2)
**Files to create:**
- `app/api/auth/` (Supabase Auth setup)
- `app/api/customers/route.ts` — CRUD
- `app/api/quotes/route.ts` — create, list
- `app/q/[token]/page.tsx` — public quote page + Approve button
- `lib/db.ts` — Supabase client
- `prisma/schema.prisma` — users, customers, quotes tables

**Exit criteria:** `POST /api/quotes` returns 201; public `/q/[token]` page loads and Approve button updates status to `approved`.

### Milestone 2 — Invoice + Stripe Payment (Day 3–4)
**Files to create:**
- `app/api/invoices/route.ts` — create invoice, generate Stripe Checkout session
- `app/api/webhooks/stripe/route.ts` — handle `checkout.session.completed`
- `app/dashboard/page.tsx` — quotes/invoices tabs

**Exit criteria:** Invoice created from approved quote; Stripe Checkout URL opens correctly; webhook marks invoice `paid_at`; dashboard shows Paid status.

### Milestone 3 — Reminders + Paywall + Deploy (Day 5–6)
**Files to create:**
- `app/api/cron/reminders/route.ts` — daily reminder sweep
- `lib/email.ts` — Resend integration, reminder template
- `app/api/quotes/route.ts` (update) — paywall check at 6th quote
- `app/upgrade/page.tsx` — Stripe subscription checkout
- `vercel.json` or `supabase/functions/` — cron schedule

**Exit criteria:** Reminder email sent to test address for Day 3 unpaid invoice; 6th quote blocked with modal; Stripe subscription purchase unlocks creation.

---

## 11. Risks & Mitigations

| Risk | Mitigation |
|------|-----------|
| Stripe webhook delivery failures | Idempotent webhook handler; retry via Stripe dashboard |
| Email deliverability (spam folder) | Use Resend with verified domain; plain-text reminder format |
| Mobile layout complexity | CSS-first approach; test on iPhone Safari early |
| Cron reliability on serverless | Use Supabase `pg_cron` as primary, Vercel cron as fallback |

---

## 12. Chargeability Rationale

This product charges because it directly recovers lost revenue: a single recovered quote (at avg. contractor job value $500–2000) or one avoided unpaid invoice repays the $29/mo subscription in the first week — making price objection irrelevant to any contractor with steady job flow.

**Free tier:** Up to 5 quotes — enough to send real quotes to real customers and experience the Aha Moment before hitting the paywall.
**Paid tier:** Unlimited quotes + invoices + auto-reminders. The paywall triggers at the 6th quote, when users already have evidence the product works.
**PLG flywheel:** Quote approval emails expose contractors' customers to the product UI — each sent quote is a passive acquisition touchpoint.
