TerraQuotes
A patent-pending tool that prices yard work from public property records, so a homeowner gets a number before anyone comes out.
Shipped
My role
Founder, product and machine-learning engineer
- Company
- Good Citizens
- Industry
- Landscaping
- Years
- 2025 to 2026
- Team
- Sole developer
- Built TerraQuotes alone over 17 months, from one homeowner's question to a live product run by a pilot operator, Quiet Village Landscaping.
- Designed the estimator so the homeowner measures nothing: the county's lot size, minus the buildings, times the contractor's own rate per square foot.
- Set the rules for the pricing model before it learned anything: it may only nudge the price, inside written limits, and it may not learn until 75 won and 225 lost jobs come back.
- Live with a pilot operator, Quiet Village Landscaping, pricing real homeowners' yards
- 1 address is all a homeowner types to get four priced monthly plans, with no sales call
- 9 of 38 leads booked a visit after getting an instant price, and 4 became work
- 49 of 225 lost bids recorded so far, against the count the model needs before it may learn
Summary
A friend asked a landscaper what his new lawn would cost. He got a plan, the fire pit he had mentioned in passing, and a bill for seventy-five dollars an hour of design. He only wanted the number. TerraQuotes gives a homeowner that number from an address, using the lot sizes the county already publishes for free.
I built it alone. A simple rule sets the price, and a machine-learning model may only nudge it, inside limits I wrote down in advance. The model is still waiting, on purpose: it has to see the bids contractors lost, and only 49 of the 225 it needs have come back.
Who was involved
- Homeowners Want to know what yard work costs before anyone comes out.
- Landscaping contractors Quiet Village Landscaping, the pilot operator, uses it today, and its prices come from its own price list.
- Jeff Haring, Sundown Landscaping Saw an early build and drew the line between upkeep and installation.
Context
To give a homeowner a price before anyone comes out, I had to know how big their yard is without visiting it. The riskiest hypothesis was that a free source of lot sizes existed at all, and was good enough to price from. The whole product depended on it, and no national service offered one.
The hard part was never showing a buyer a range of numbers. It was resisting the urge to build a giant do-it-yourself landscape design tool, and staying on one question: which prices do people say yes to? In the trade that is called conversion, how often a quote turns into a paying job.
Research
Before building anything, I wrote each guess down as a hypothesis I could test, with what would prove me wrong. Then I ranked them by how much it hurts if I am wrong and how much a customer gains if I am right, and built in that order.
I went to the county records first: the city of St. Louis, St. Louis County, and Maricopa County in Arizona. St. Louis publishes every lot's land and building square footage for free, and the same approach worked in the other two. I showed an early build to Jeff Haring of Sundown Landscaping, and when Quiet Village came on, I tested the idea against their real price list.
Key hypotheses
Three hypotheses had to hold before this was worth building. Here is each one, and where it stands.
I tested these three hypotheses against a real price list when the first contractor, Quiet Village, came on. Their prices went into the model, and their customer records gave me the two questions I actually care about: does the estimate get closer to what jobs really sell for, and can a machine learn which quotes win.
Calculator trust
Can a calculator hold a sensible price while someone changes their mind about what they want? Yes. The packages and add-on switches shipped, and I took away the input people could get wrong without noticing.
Estimate price
Is the number close enough for a landscaper to take the lead seriously? Still open. A pilot operator runs it live, but I threw out the accuracy test before it could be scored, so the number itself is unproven.
Sq ft method
Can a landscaper price work by the square foot? Yes, with a catch. The first pilot rewrote their upkeep pricing to a flat rate per square foot. Installation stays out, because nobody knows what's under the lawn.
Training gate
The model cannot start learning until enough finished jobs come back.
These numbers aren't random. I ran a simulation: I fed the model pairs of jobs, one won and one lost, and asked it to pick the winner. I tried different numbers of jobs, ran the simulation 30 times at each, and kept the smallest number of jobs where it picked the winner at least 80% of the time. Turns out that's 75 won jobs and 225 lost. The practice jobs were made up, though, so they tell me how many real jobs I need, not whether there's a pattern to find.
Losses recorded
49/225
Wins recorded
6/75
Wins we can train on
0/75
Each dot is one belief, placed by how much it mattered and how much we actually knew. The top right corner is where being wrong would hurt most and we knew least, so that is where the research went first. Two beliefs landed there.
- Held up
- Partly held
- Didn't hold
- Never tested
| ID | Assumption | Certainty | Importance | Status |
|---|---|---|---|---|
| A-001 | Vercel's hosting will scale to all tenant traffic without us running our own servers | High | High | ValidatedN/A: validated in production |
| A-002 | A free geocoder is accurate enough for residential address lookup, so we never have to pay for one | High | High | ValidatedSupplemented with county GIS parcel data as primary address source (CR-20251012) |
| A-003 | Routing geocoding through our own server removes the browser security and privacy problems | High | Medium | ValidatedN/A |
| A-004 | Public county parcel boundaries are authoritative enough to estimate service area from | High | Critical | ValidatedAddress standardization handles heterogeneous schemas; YAML configs externalize field mappings |
| A-005 | Serviceable area can be derived reliably from public parcel data | High | Critical | Validated with caveatsvaluation and tier adjustments compensate for data gaps; area model semantics documented in CR-20260116 |
| A-006 | Public property valuation is a meaningful proxy for landscaping price positioning | Medium | High | Active: monitoringPer-tenant pricing profiles can override; ML pipeline designed to learn from CRM outcomes |
| A-007 | Counting events on our own servers, in Firestore and BigQuery, gives enough insight without a tracking library in the browser | High | High | ValidatedBigQuery views provide analytical capability; Mixpanel receives PII-stripped UX events for real-time needs |
| A-008 | Labelling every event as either checking the build or testing the idea stays maintainable as the number of events grows | Medium | High | Active: enforcedCI enforcement via enforce-pr-contract.yml; event registry test validates purpose on all events |
| A-009 | Three storage tiers: Firestore for hot data, BigQuery for warm, blob storage for cold: is cost-effective at this scale | High | Medium | Validated at current scaleCost monitoring in place; PostgreSQL migration plan documented in docs/tech-debt/MIGRATION_PLAN_OSS.md |
| A-010 | The people using this are homeowners on phones and crews on tablets, so mobile-first is the right default | High | High | Assumed: reasonableDesktop progressive enhancement ensures both work; Chromatic tests both viewports |
| A-011 | Three service tiers (Curb Appeal, Full Lawn, Dream Lawn) match how residential landscaping is actually sold | High | High | Validated operationallyPer-tenant tier labels configurable; telemetry tracks tier selection for Va |
| A-012 | Automated pricing is accurate enough for lead generation, though not for invoicing | Medium | Critical | Active: high priorityML pipeline (CR-20260209) designed to close the loop; CRM outcome detection (CR-20260306) will provide ground truth |
| A-013 | A one-time code by text or email is enough to gate estimate detail and capture a lead without driving people away | Medium | High | Active: monitoringreCAPTCHA as secondary layer; rate limiting prevents abuse; verification events track completion rate |
| A-014 | One source of truth for design tokens, compiled per tenant, is enough to theme every tenant | High | High | ValidatedCompile-time is acceptable for current tenant count; revisit if dynamic tenant provisioning is needed |
| A-015 | Moving address search to the server removes the slowness caused by sending a 6MB search index to the phone | High | High | ValidatedCase study documented in docs/case-studies/ADDRESS_INDEX_ARCHITECTURE.md |
| A-016 | A public mirror of lead data gives the sales team enough visibility without exposing personal details | High | High | Validated with gapmirrorLeadToPublic Cloud Function trigger handles sync; gap indicates timing or error: needs investigation |
| A-017 | Naming the allowed host per tenant is enough to secure embedding without managing origins dynamically | High | High | Validateddata-base-url attribute provides escape hatch for edge cases; TENANT_ORIGINS mapping (CR-20260311) handles CDN proxying |
| A-018 | Working out the tenant from the web address is enough for white-label hosting, without provisioning each one | High | High | Validated at current scaleAcceptable for current 3-tenant model; dynamic provisioning deferred to future EPIC |
| A-019 | Giving each tenant its own data path prevents one tenant reading another's leads, without row-level security | High | Critical | ValidatedCross-tenant regression suite in place |
| A-020 | A baseline with a bounded override is more robust than putting the model first | High | Critical | Active: partially validatedHeuristic baseline always computed; ML is additive not required; CRM outcome data will enable validation |
| A-021 | Absorbing the typical bore cost, about $750, into irrigation base pricing produces cleaner estimates without underpricing | Medium | Medium | Active: monitoring$750 based on typical one-bore scenarios; actual bore work priced correctly during installation |
| A-022 | Carrying CRM updates on events we already send avoids duplication and keeps the CRM interchangeable | High | High | Active: partially validatedGeneric crm_* events designed for future providers; HubSpot namespace isolation prevents vendor lock-in |
| A-023 | Pulling modules out behind interfaces lets them be tested without touching the filesystem or the cloud | High | High | ValidatedRepeatable pattern documented for future module extraction |
| A-024 | Keeping each region's field mappings in validated configuration files is more maintainable than writing them in code | High | Medium | ValidatedJSON Schema export from Pydantic for IDE autocomplete is future improvement |
| A-025 | Feeding real outcomes back from the CRM will improve pricing accuracy by 15 to 25 percent within six months | Low | Critical | Unvalidated: infrastructure readyAll infrastructure in place; blocking dependency is CRM deal outcome detection (CR-20260306); measurement via BigQuery ml_hypothesis_metrics view |
| A-026 | Deals that were lost are as valuable as deals that were won for calibrating pricing | Medium | High | Active: manual capture shipped; native subscription pendingCR-20260306 epic closed 2026-03-23 (#348); manual capture path shipped July 2026; native loss-reason capture in build (#890) |
| A-027 | Partner sites will rewrite our embed script's address, so detecting where it loaded from is unreliable | High | High | Validated (learned from incident)Hardcoded TENANT_ORIGINS mapping is deterministic and CDN-immune; data-base-url attribute for edge cases |
| A-028 | Dividing Phoenix, 500 square miles, by ZIP code is explainable to buyers and maintainable for exclusivity sales | Medium | High | Active: pre-productionConfig update process needed for ZIP changes; unmapped parcels route to parent maricopa region |
| A-029 | Creating the deal automatically improves close rate by at least 10 percent against keying it in by hand | Low | High | Unvalidated: measurable via BigQueryBigQuery fact_lead_ml.deal_won rate by source enables measurement |
| A-030 | Feeding real outcomes back will close the gap between the estimate and what the job finally sold for, by 15 to 25 percent within six months | Low | Critical | Unvalidated: infrastructure readyMeasurement query defined in hypothesis registry; requires sufficient closed-deal volume first |
| A-031 | Aligning 2026 pricing to the official spreadsheet produces correct estimates for Quiet Village customers | Medium | Critical | Active: under validationyarn test:regression:quiet-village guards against drift; qvl2026PricingValidated telemetry tracks sign-off |
| A-032 | The stored area figure is already adjusted, so anything downstream must not adjust it again | High | High | Active: fix in progressSemantic model documented; regression tests will validate fix |
Design
The homeowner measures nothing. You type your address and pick your house from a list. The app takes the lot, subtracts the buildings, and shows what is left as a green tag beside the address. The price is that area times the contractor's own rate per square foot.
It only prices addresses picked from the list. If yours is not there, it says so and offers a waitlist instead of guessing. The code that turns county records into that list is patent pending, not granted.

More screens






Iteration
The first version was install-first. You typed an address and picked one of three one-time packages, Curb Appeal, Full Lawn or Dream Lawn, and an isometric drawing of a house redrew its yard to show what you were buying. Full Lawn at one address came to $106,564, or $16.30 a square foot.
It priced the wrong job. Jeff Haring saw an early build and said: “This is great for maintenance; not quite ready for installation. That’s complicated as soon as you start digging.” Five months later my first pilot drew the same line unprompted. So the packages became monthly upkeep: Quiet Village rewrote its maintenance prices as one flat rate per square foot, its packages became the GEM monthly plans, and digging moved to its own opt-in tab.
Some lessons came the hard way. Sending the whole address list to a phone, six megabytes of it, made the page slow and broke on iPhones, so now the server answers each search. A partner's website rewrote the address of my code and broke the quote form, so now the app knows each partner by name instead of guessing.



A design that didn't work
The October 2025 calculator asked the homeowner to set how much of the yard needed work, with a slider. People got it wrong, and the price looked just as believable either way. A wrong answer that looks right is worse than no answer, because nobody notices it.
So for mowing and upkeep I stopped asking and measured the lot myself, from the county's property records. The area picker moved to the add-ons, where choosing an area is the right thing to ask.
Outcomes
TerraQuotes is live. A homeowner gets a priced maintenance package in about a minute, with no sales call, and the leads go to the pilot operator, Quiet Village Landscaping. On one contractor's sales view, all 38 leads got a number back the same minute; nine booked a visit and four became work.
What I cannot tell you yet is how close the price comes. I threw out my accuracy test before scoring it, because 215 of 347 old sales rows mixed a monthly price with two one-time prices, with nothing to tell them apart. On July 14, 2026 I set the bar the model must clear before it is worth building, from a simulation rather than a guess. A test model I trained in July picked the winner 47 times in 100, against a coin flip's 50 and my cutoff of 60, and no model has ever priced a real quote.
A training gate: 75 won and 225 lost jobs before the model may learn. Today it has 6 and 49.
63 requirements tracked, with the 19 that ship with a known gap listed.
For landscaping businesses
Instant prices, from your own rates
Homeowners type an address and see their yard priced. No sales call.




