← All work

SaaS for HVAC contractors

QuoteDuct

Estimates your customers can approve, and pay a deposit on, in under three minutes.

Role
Product, Brand, Interface, Full-stack, Billing
Year
2026
Status
Live since Sept 2026
Site
quoteduct.com ↗
QuoteDuct landing page with the duck mascot, the tagline, three feature cards and an amber "Get started" button.
The problem

An HVAC estimate usually leaves the driveway as a PDF or a text, then waits. The contractor has already done the work of pricing it; what's missing is a fast way for the homeowner to say yes and put money down before they call the next company.

What I built
  • An estimate builder designed for a phone in one hand: 11 job types plus custom jobs, line items, parts and labour
  • A public estimate page the homeowner opens from a single link, with one-tap approval
  • A 30% deposit collected through a Stripe Payment Link on approval
  • Magic-link sign-in, customer history, internal notes and follow-up reminders
  • Free and Pro plans: 10 estimates a month free, Pro at $14.99/mo as a Stripe subscription driven by webhooks
Stack
  • Next.js 16
  • Tailwind v4
  • Prisma
  • Supabase Postgres
  • Stripe
  • Resend
  • Vercel
Decisions worth mentioning
  1. 01

    The homeowner page works with JavaScript off

    It’s fully server-rendered. Approve is a plain form POST and the deposit button is a plain link to Stripe. Homeowners open it on whatever phone they have; it can’t be the thing that breaks.

  2. 02

    The contractor's own price, not a guessed one

    The spec called for a hardcoded price book and AI pre-fill. I cut both. Market ranges show as greyed reference only; the one thing that pre-fills is your own last price for the same job type, because that’s the number a contractor actually trusts.

  3. 03

    Tax on parts, not labour

    Tax applies to parts only. Any line billed by the hour is untaxed, and the rate is editable per quote, so the total matches what the contractor would write by hand.

  4. 04

    Limits enforced on the server

    The free-plan cap is checked in the send endpoint (a 402, not a hidden button) and surfaces as an upgrade sheet in the builder. Pro status is toggled only by Stripe webhooks.