One number is your minimum job price. GroundCut enforces it everywhere.
Every other lawn-care tool makes you tune a minimum job price AND a separate per-visit base for subscriptions. Two fields, same idea, both go out of sync. GroundCut collapses them into one: the Starting price floors small polygon quotes and anchors your recurring monthly bill.
A tenant-wide target hourly rate is the second safety net — it flags any quote that clears the floor but still wouldn’t hit your hour.
How the floor works in the pricing engine
Every step here is shipped code in packages/shared/src/pricing.ts and rides on the /api/quote response.
- 01
One number is your minimum AND your recurring anchor
Every other lawn-care tool makes you tune a per-service "minimum price" AND a separate "base price" for subscriptions. Two fields, same idea, both have to stay in sync or your math drifts. GroundCut collapses them. Each service has one number — Starting price. It floors the polygon quote when the area math comes in low, and it anchors the recurring monthly bill (× 4 weekly, × 2 biweekly, × 1 monthly). No twin fields, no drift.
services.base_price (NOT NULL column on services)
- 02
The floor wins over the area math, every time
The quote engine computes a price from `price_per_quarter_acre × (mowable_area_sqft / 10,890)`. Then it applies the overgrown surcharge if flagged, the distance fee, and any seasonal adjustment. Then `Math.max(price, base_price)` runs — the Starting price cannot be undercut. A customer who drew a 200-sqft lawn doesn’t pay $0.83; they pay your floor. The bump is absorbed into the area line item so the breakdown still sums cleanly on the customer-facing quote.
packages/shared/src/pricing.ts — Math.max(price, input.base_price)
- 03
Max price routes oversize lots away from auto-booking
Pricing rules also have a `max_price` ceiling. A customer outlining an absurd area gets clamped to the cap — the booking flow doesn’t produce a $4,000 instant quote that locks you into a job you can’t deliver. Big lots route to the in-person quote flow instead, where you visit the property, fill in line items, and send a real quote. Floor and ceiling are the only two guardrails you set per service.
services.pricing_rules.max_price — Math.min(price, rules.max_price)
- 04
Target hourly rate catches profitable-looking jobs that aren’t
A quote can clear your $50 Starting price and still be unprofitable — a 90-minute job that nets $48.55 after Stripe is $32/hr, not your $50 target. The engine divides `net_after_fees` by `estimated_time_mins / 60` and returns `below_target_rate: true` on the quote response. The target is one tenant-wide number set in `/admin/settings` — most operators have one $/hr they need to clear across the whole business, not different ones per service. The floor protects against $1 quotes; the target rate protects against floor-priced jobs that still won’t clear your hour.
tenants.settings.target_hourly_rate → below_target_rate on /api/quote response
- 05
Card-fee passthrough is opt-in and respects the floor
If you turn on `pass_card_fee_to_customer` in tenant settings, the engine adds Stripe’s 2.9% + 30¢ on top of the quoted price so you net the full quoted amount. The floor still applies to the pre-passthrough price — the passthrough is layered on after, never used to dodge the floor. `card_fee_included: true` rides on the quote response so the booking page can display "$X (card fee included)" without confusion.
pass_card_fee_to_customer flag; fee added after the base_price floor in pricing.ts
“What about customers who want a discount?”
The discount conversation comes up in every lawn business. A commercial account wants a per-job rate below your residential floor. A long-time customer wants a “loyalty rate.” Your neighbor wants the buddy price. The Starting price floor sits in the booking-page flow — it doesn’t have to apply to every customer.
Recurring price overrides take precedence. Set a recurring schedule with a lower price for that customer and the booking-page floor never enters the picture — the recurring schedule carries its own price snapshot through every generated job. The floor is for new-customer instant quotes, not for established relationships.
customer_price_override on a one-off job. From `/admin/jobs` you can create a job with a custom price; the engine clamps it at your Starting price so a typo can’t zero the job, but otherwise the override wins. The line breakdown shows “Custom rate” and the booking-page math is bypassed.
On-site adjustments are signed and auditable. The crew can adjust the price on arrival if the customer pushes back; increases require a signature, decreases don’t. Every adjustment writes to the audit log with the actor, the before/after, and the reason.
Where each field lives
/admin/services— each service has a Starting price, a per-¼-acre rate, a Max price, and an optional overgrown surcharge. That’s it for instant-quote pricing. Edit one service to test, save, and refresh the booking page to see the new math./admin/settings— the target hourly rate lives here (tenant-wide). When set, `below_target_rate` rides on every quote response; when blank, the flag is silenced.services.base_price+services.pricing_rules.max_price— the underlying columns. Both required. No separate `min_price` field.POST /api/quote— the runtime endpoint. Returns the full quote shape including the floor-applied price, the line-item breakdown, and the target-rate flag.
Minimum-job-price FAQs
Why is your "minimum price" the same field as your subscription price?+
What about a long-time customer who wants $10 off the minimum?+
How does the floor interact with a customer who draws a tiny lawn on purpose?+
Can I have a different floor for weekly vs one-time customers?+
Where does the floor live in the database vs the UI?+
Does the minimum apply to in-person quotes too?+
What if my market is so price-sensitive that the floor loses me bookings?+
How is this different from just setting a high price-per-quarter-acre?+
Stop quoting below your floor
14-day free trial. One Starting price per service plus a tenant target hourly rate is the whole setup. Run a few real bookings; see the engine clamp small lawns to your floor and flag the rest.
14-day free trial · No card required · Cancel any time