Release Notes
What’s new, fixed, and changed in LawnLedger. Versions follow semantic versioning (major.minor.patch).
Improvements ship continuously — fixes and features go live as they’re finished, not on a release date. This page records the notable batches. A version number marks a milestone worth writing up, not every deploy.
v4.0.0 — 2026-06-09
A large release — roughly a month of work spanning a backend re-architecture, a security and multi-tenancy hardening pass, and several flagship feature epics. No breaking changes to the { data } API envelope or the auth model; all data remains tenant-scoped.
New
Reporting & finance
- Reports Suite — a redesigned
/reportshub with KPI snapshots and sparklines, a flagship Profit & Loss report with three layouts (Statement / Dashboard / Narrative) and period comparison, a drill-down drawer behind any figure, a custom report builder, and a scheduled-reports manager (recurring PDF/CSV email). Charts render from dependency-free SVG primitives. - A/R Reconciliation — the Payments page is now an accounts-receivable reconciliation workspace with totals, filters, sort, and CSV export.
- Purchase Orders — a full PO module (model, RLS, service/routes, Finance page) with “Build draft POs” wired into the new Service Catalog Almanac seasonal planner, plus a v2 catalog (accordion table, vendor SKU linking, photo upload).
- Billing Studio —
/estimates/newand/invoices/newrebuilt as a full-viewport composer with a high-fidelity live preview, a lossless Estimate ⇄ Invoice toggle, editable two-line line items, per-line and whole-document discounts/deposits, and a Review & Send drawer.
QuickBooks
- Sync outbox — QuickBooks sync moved onto a durable outbox with a drain worker, retiring inline auto-sync. Adds per-record sync-status badges across Invoices, Customers, Estimates, Services, and Expenses, a sync-health roll-up card, a Reconnect CTA for expired connections, CDC reconciliation, and proactive single-flight token refresh.
Security & access
- Two-factor authentication — org-enforced TOTP MFA with a post-login challenge gate.
- Session & device management — active-session list, new-device sign-in email alerts, and an org-configurable idle-logout timeout that revokes the server session.
- Role-based access control — a 95-permission catalog across 27 categories, enforced on backend financial/catalog/sub-resource endpoints and mirrored in the UI (greyed nav and lock cards on restricted screens).
- Row-Level Security — PostgreSQL RLS rolled out across tenant, portal, and public endpoints via a system-context mechanism.
- Multi-party contracts — a DocuSign-style prepare-and-send editor, per-recipient routing and reminders, per-recipient identity verification (email step-up), and multi-signature rendering on every PDF preset.
- Link & password hardening — payment and contract links now expire; breached-password (HIBP) check and reset-strength parity on passwords.
Operations
- Territories — a ZIP-based territory model with auto-assignment, wired across Locations and Settings.
- Crew — Crew Analytics V2 (leaderboard, on-time, period-aware drill-in), clock-on-behalf-of for tracked-only employees, and printable bilingual crew-onboarding guides.
- Redesigns — Dispatch (full-bleed map + exception bar), Winter Routes (storm mode), Locations (facets, PM portfolio, compliance docs, heatmap), Customers (KPI list + V1/V2 detail), and Settings (a 6-group information architecture with a discovery-first landing).
- Bulk operations — CSV bulk-update for customers/properties/services/equipment, bulk import on entity list pages, and bulk job reschedule from the jobs-list multiselect; server-side sorting for the core list pages.
Integrations
- Zapier — a managed Zapier integration with API-key authentication, a connect/disconnect settings panel, and a webhook surface for outgoing event subscriptions.
Mobile (crew app 1.1.0)
- A real-time job-costing card (margin, revenue, cost breakdown) and a Today route map (Leaflet/OpenStreetMap via WebView), offline clock-in/out with exactly-once replay, GPS-trail reliability fixes, and complete es-MX localization.
Notifications
- A block-based transactional-email system with an admin editor and live preview, customer notification preferences with a working unsubscribe link, a SendGrid event webhook, and deep-links from the bell and Notification Center straight to the underlying record.
Changed
- Backend service layer — ~50 route domains refactored into a
services/layer (thin route adapters over typed-error services) with a matching unit-test suite; backend coverage is now ~4,800 tests. - Sequential, human-readable ID numbers added across core entities.
- BusinessSettings money fields migrated from Float to Decimal.
- White-label and custom brand-color customization removed in favor of a consistent powered-by footer (brand color remains an ungated org setting).
- Skeleton loaders expanded across detail, list, dashboard, and map pages.
Fixed
- Payments & billing — Stripe payments recorded in serializable transactions (no double-record), invoice settlement computed on principal so tips no longer inflate paid status, refunds write a canonical PARTIAL status, idempotency keys forwarded to Stripe, and subscription/feature-gate repairs.
- Email & calendar — Gmail/Outlook OAuth callback repair, rotated-refresh-token persistence, auto-disable of revoked connections, and SendGrid click-tracking disabled on transactional links.
- Numerous mobile, accessibility (44px touch targets, semantic headings, keyboard operability), and rendering fixes; full es-MX translation parity with a CI guard.
Security
- Production-readiness and security-audit remediations across several rounds (estimate-token leak, crew-tracking authorization, CSV-injection, stored-XSS and cross-org foreign-key paths, contract signing-token leak).
- Circuit breakers and timeouts on Stripe / SendGrid / Twilio / QuickBooks / Storage / Redis, with Sentry alerting on breaker-open.
- A durable webhook queue with exponential-backoff retry and a dead-letter queue; Stripe and Twilio webhook signature verification.
- Sentry error tracking wired across the backend with secret scrubbing.
Infrastructure
- CI/CD: a diff-gated per-side pipeline, 3-way sharded E2E gated to touched sides and skipped on push-to-main, a Lighthouse budget on
/login, diff-aware Husky hooks, and a pre-push authenticated smoke. Backend deploys gate onprisma migrate deploybefore the traffic cutover.
v3.2.0 — 2026-05-10
A focused day of production-audit followups, render polish, and data-integrity work. No breaking changes.
New
- Customer + property entity decode helpers (
customerName,decodeText) applied across all 18 PDF preset renderers — fixes “null” appearing on entity-customer PDFs and&showing literally in property labels. - One-shot DB backfill script (
backend/scripts/backfill-html-entities.ts) to clean up any existing&literals in plaintext fields. Idempotent, dry-run by default. skipAuthRedirectoption on the API client — for soft auth checks like the token-based portal page that shouldn’t bounce visitors to/loginwhen they aren’t signed in.- Decoupled estimate notes/terms parity queued for next release — see handoff for details.
Fixed
- Dashboard customer count no longer includes soft-deleted records.
- Customer GET/PATCH/DELETE by-id no longer returns soft-deleted records.
- Portal user data no longer mirrored into
localStorage— removes an XSS exposure surface and avoids a stale auth signal after session expiry. - TeamAccessSettings v2 tabs now lazy-load — drops a 604 KB chunk from the initial Settings load.
- Webhook fan-out failures were silently swallowed; now logged with full context.
- Payment recording and refund flows now use
Serializabletransaction isolation — eliminates phantom-read race conditions that could over-pay or over-refund. - Dashboard query errors now capture to Sentry before swallowing — graceful “no data” UI preserved but prod outages are visible.
Schermerhorn & Co.in form pickers no longer renders asSchermerhorn & Co.—decodeEntitiesapplied at every customer/property display site.- Property picker label no longer doubles when the operator typed the address into the property name (e.g.
"1618 Sheridan Rd Condo"doesn’t show" - 1618 Sheridan Rd"appended). sanitizePlainTextnow decodes safe HTML entities after sanitize-html runs, so new writes don’t leave&literals in the DB.
Changed
- Contract form styles seeder no longer creates the 6 default visual presets for CONTRACT-type — contracts inherit the INVOICE default style automatically. New orgs see an empty “Contract form styles” tab; operators who want contract-specific chrome can create one explicitly.
- Login error capture: bare
catch {}replaced withSentry.captureExceptionso network errors, CORS issues, and Better Auth client throws now report. - Geocoding (
/lib/geocoding.ts) returns friendly messages on Nominatim 429 / 5xx instead of crashing JSON.parse on the HTML error page. - Customer substring search raised cap from 500 → 2000 rows with a
truncated: trueflag surfaced when older customers may be missing from results.
Security
- Middleware ordering in
backend/src/index.tsfixed sorateLimit("api")runs beforeautoSanitizeMiddleware. - Encryption key validation deferred to first use (no eager startup crash on malformed keys).
- ESLint rule added: raw
fetchis now banned inwebapp/src/pages/**andwebapp/src/components/**. Only@/lib/apican call the backend, with one legitimateeslint-disablefor an in-memorydata:URL conversion.
v3.1.0 — 2026-05-09
Twilio compliance, invitation flows, and audit hardening.
New
- SMS-AUTO invitation delivery with Twilio compliance prefix (
From LawnLedger:), STOP/HELP/START keyword handling on inbound webhooks, and SmsOptOut model. - Per-org invitation rate limits: 10/hour and 50/day, sliding-window counters.
- Username plugin (Better Auth v1.5.5) added —
User.usernameanddisplayUsernamefields, login-by-username flow.
Fixed
- Customer detail page no longer doubles entity names (“Chicagoland Community Management Chicagoland Community Management” → “Chicagoland Community Management”).
- Sidebar icons no longer clipped on collapsed sidebar — three sidebars (App, Platform, Notion) all patched.
- Bulk import path hardened: chunked transactions, real error messages surfaced, numeric Excel cells coerced to strings before
.trim(). - Property-import emailHash lookup — customers were no longer “not found” when emails contained encrypted PII.
Changed
- First name mandatory, last name optional on customer schema — better fit for entity customers (PMs, businesses, associations).
- Email mandatory, phone optional — matched the brother’s QuickBooks data shape.
v3.0.0 — 2026-03-18
Major release: PDF attachments on emails, portal authentication overhaul, equipment status tracking, crew schedule view, responsive dialogs, circuit-breaker pattern on external API calls, invoice property selector.
Older
See the GitHub Releases page for the full version history.