diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..cc8cbfa
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,22 @@
+PROPRIETARY SOFTWARE LICENSE
+
+Copyright (c) 2026 Property Management Network. All rights reserved.
+
+This software and its source code (the "Software") are proprietary and
+confidential. The Software is licensed, not sold.
+
+No permission is granted to any person or entity to use, copy, reproduce,
+modify, merge, publish, distribute, sublicense, sell, or create derivative
+works of the Software, in whole or in part, by any means, without the prior
+express written consent of the copyright holder.
+
+Unauthorized copying, distribution, or use of the Software, via any medium,
+is strictly prohibited.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING FROM, OUT OF,
+OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
index dfc6000..b6d531d 100644
--- a/README.md
+++ b/README.md
@@ -5,64 +5,99 @@
-# Property Management Network
+π Property Management Network
-**Property management SaaS for independent landlords.** Track properties, tenants, rent, maintenance, leases, and expenses β all in one clean dashboard.
+
+ The all-in-one property-management platform for independent landlords.
+ Properties, tenants, rent, maintenance, leases, expenses, AI insights, and integrations β in one clean dashboard.
+
-Built with Next.js 16, PostgreSQL (Drizzle ORM), Better Auth, Stripe, and OpenAI. Deploys to DigitalOcean App Platform (see [DIGITALOCEAN.md](DIGITALOCEAN.md)).
+
+
+
+
+
+
---
-## What it does
+## β¨ Overview
-Property Management Network replaces the spreadsheet + WhatsApp chaos that most small landlords live with. Key capabilities:
+Property Management Network replaces the spreadsheet-and-WhatsApp chaos that most small landlords live with. It gives a solo landlord or a small team a single source of truth for their whole portfolio β and the automation, AI, and integrations to run it hands-off.
-- **Properties & units** β manage your entire portfolio with occupancy tracking
-- **Tenant profiles** β contact info, lease history, payment records, and a private tenant portal
-- **Rent tracking** β log payments, send Stripe payment links, auto-mark overdue balances
-- **Maintenance requests** β status workflow (Open β In Progress β Resolved), tenant submissions via portal
-- **Lease management** β expiry countdowns, automated 60/30/7-day email alerts
-- **Expenses** β categorized logging with recurring expense support
-- **Documents** β file vault per property with drag-and-drop upload to local disk, served through an auth-gated route
-- **AI features** β AI-powered recommendations, predictions, and impact tracking (Pro+)
-- **Automated emails** β rent reminders, overdue alerts, lease expiry notifications via SMTP (SMTP2GO)
-- **Tenant portal** β token-based (no login), tenants can view rent history and submit maintenance
+Everything is **multi-tenant and team-aware**: each landlord operates on their own isolated portfolio, and Landlord/Lifetime accounts can invite teammates with scoped roles.
+
+### π§° What you can do
+
+**Core operations**
+- π’ **Properties & units** β manage your whole portfolio with live occupancy tracking and a map view (addresses are auto-geocoded).
+- π₯ **Tenants** β profiles, lease history, payment records, and a private **tenant portal** (token-based, no login required).
+- π΅ **Rent tracking** β log payments, send **Stripe or PayPal** payment links, and auto-mark balances overdue with automatic late fees.
+- π§ **Maintenance** β full status workflow (Open β In Progress β Resolved), with tenant-submitted requests from the portal.
+- π **Leases** β expiry countdowns, automated 60/30/7-day email alerts, and **e-signature** (DocuSign / Dropbox Sign).
+- π§Ύ **Expenses** β categorized logging with recurring-expense support.
+- ποΈ **Documents** β a per-property file vault stored in object storage and served through an auth-gated route.
+- π **Inspections & vendors** β move-in/out/routine inspection checklists and a vendor directory.
+- π **Reports & exports** β portfolio analytics with CSV export.
+- π
**Calendar** β an in-app calendar plus a read-only **iCal (ICS) feed** you can subscribe to.
+
+**Automation & AI**
+- π€ **AI features** β recommendations, predictions, impact tracking, and a portfolio assistant (OpenAI). *(Pro and up.)*
+- βοΈ **Automated email** β rent reminders, overdue notices, and lease-expiry alerts, plus a configurable **follow-up engine**.
+- π¨ **White-label branding** β put your own brand on the tenant portal. *(Landlord / Lifetime.)*
+- π‘οΈ **Admin dashboard** β superadmin tools with a full audit log.
---
-## Revenue model
+## π Integrations & developer platform
-| Plan | Price | Limits |
-|------|-------|--------|
-| Starter | Free | 1 property, 3 tenants, no AI |
-| Pro | $29/mo | 10 properties, unlimited tenants, AI (50 calls/mo) |
-| Landlord | $59/mo | Unlimited properties, team access, white-label, AI (200/mo) |
-| Lifetime | $199 one-time | Everything in Landlord, forever |
+| Capability | Details |
+|---|---|
+| π **Public REST API** | Versioned `/api/v1` endpoints (properties, tenants, payments, maintenance, webhooks) authenticated with Bearer **API keys**. See `/api-docs`. |
+| πͺ **Outbound webhooks / Zapier** | Subscribe to events (`tenant.created`, `payment.paid`, `maintenance.updated`, β¦). Deliveries are **HMAC-signed**, retried with backoff, and Zapier-compatible via the REST-hook subscribe/unsubscribe pattern. |
+| π³ **Payments** | Stripe (subscriptions + rent payment links) and PayPal. |
+| π **Accounting sync** | One-way push of income & expenses to **QuickBooks Online** or **Xero** (OAuth). |
+| βοΈ **E-signature** | Send leases for signature via **DocuSign** or **Dropbox Sign**. |
+| π **Auth** | Email/password and Google OAuth (Better Auth). |
-Subscription billing via Stripe. Lifetime deal is ideal for Flippa buyers who want to offer an LTD to early customers.
+Every integration is env-gated: unconfigured providers show a clean βnot configuredβ state instead of a broken button.
---
-## Tech stack
+## π³ Plans & pricing
-| Layer | Tech |
-|-------|------|
-| Framework | Next.js 16.2 (App Router, TypeScript) |
-| Styling | Tailwind CSS + Geist font |
-| Database | PostgreSQL (via Drizzle ORM) |
+| Plan | Price | Highlights |
+|------|-------|------------|
+| π **Starter** | Free | 1 property, 3 tenants, no AI |
+| π **Pro** | $29/mo | 10 properties, unlimited tenants, AI (50 calls/mo) |
+| π **Landlord** | $59/mo | Unlimited properties, team access, white-label, AI (200/mo) |
+| βΎοΈ **Lifetime** | $199 once | Everything in Landlord, forever |
+
+Billing runs through **Stripe** or **PayPal**. Stripe products/prices are resolved by stable lookup keys and auto-created on first checkout, so going live is just an API-key swap β no price IDs to wire up.
+
+---
+
+## π§± Tech stack
+
+| Layer | Technology |
+|-------|------------|
+| Framework | Next.js 16.2 (App Router, TypeScript, React 19) |
+| Styling | Tailwind CSS + Geist |
+| Database | PostgreSQL via **Drizzle ORM** |
| Auth | Better Auth (email/password + Google OAuth) |
-| Storage | DigitalOcean Spaces (S3-compatible, CDN, auth-gated) |
-| Payments | Stripe (subscriptions + payment links) |
-| AI | OpenAI (gpt-4o-mini) |
+| Object storage | DigitalOcean Spaces (S3-compatible, CDN, auth-gated) |
+| Payments | Stripe + PayPal |
+| AI | OpenAI (`gpt-4o-mini`) |
| Email | SMTP (SMTP2GO) |
+| Maps | Leaflet + OpenStreetMap / Nominatim geocoding |
| Cron | DigitalOcean Functions (scheduled triggers) |
| Deploy | DigitalOcean App Platform (Docker image via DOCR) |
---
-## Setup
+## π Getting started
-### 1. Clone and install
+### 1. Clone & install
```bash
git clone
@@ -70,155 +105,117 @@ cd property-management-network
npm install
```
-### 2. Configure environment variables
+### 2. βοΈ Configure environment
+
+Copy the template and fill in your own values:
```bash
cp .env.example .env.local
```
-Fill in `.env.local`:
+`.env.local` holds your database URL, auth secret, and credentials for Stripe/PayPal, OpenAI, SMTP, and object storage. **Every variable is documented inline in `.env.example`**, and the full production reference lives in **[DIGITALOCEAN.md](DIGITALOCEAN.md)**. Never commit real secrets.
-```env
-# Database (PostgreSQL via Drizzle ORM)
-DATABASE_URL=
+### 3. ποΈ Run migrations
-# Auth (Better Auth)
-BETTER_AUTH_URL=http://localhost:3000
-BETTER_AUTH_SECRET=your-random-secret-string
-GOOGLE_CLIENT_ID=
-GOOGLE_CLIENT_SECRET=
-
-# File storage (local disk)
-STORAGE_DIR=./storage
-
-# Stripe (no price IDs needed β resolved by lookup key, auto-created on first checkout)
-STRIPE_SECRET_KEY=
-STRIPE_WEBHOOK_SECRET=
-NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=
-
-# OpenAI
-OPENAI_API_KEY=
-
-# Email (SMTP β e.g. SMTP2GO)
-SMTP_HOST=mail.smtp2go.com
-SMTP_PORT=2525
-SMTP_USER=
-SMTP_PASS=
-EMAIL_FROM=postmaster@yourdomain.com
-
-# App
-NEXT_PUBLIC_APP_URL=http://localhost:3000
-CRON_SECRET=your-random-secret-string
-```
-
-### 3. Run database migrations
-
-The schema is managed with Drizzle ORM (see `drizzle.config.ts`). Point `DATABASE_URL` at your PostgreSQL instance in `.env.local`, then apply the migrations from `lib/db/migrations`:
+The schema is managed by Drizzle (see `drizzle.config.ts`). Point `DATABASE_URL` at your PostgreSQL instance, then:
```bash
-npm run db:migrate
+npm run db:migrate # apply migrations
+npm run db:generate # regenerate after schema changes
+npm run db:push # push schema directly (quick local prototyping)
```
-To regenerate migrations after changing the schema, use `npm run db:generate`. For quick local prototyping you can push the schema directly with `npm run db:push`.
+### 4. π Wire up services (as needed)
-### 4. Configure Stripe
+- **Stripe** β set the API keys, then add a webhook at `https://yourdomain.com/api/stripe/webhook` for `checkout.session.completed`, the `customer.subscription.*` events, `invoice.payment_failed`, and `payment_intent.succeeded`.
+- **Email** β verify a sending domain with your SMTP provider (e.g. SMTP2GO) and set the `SMTP_*` + `EMAIL_FROM` vars.
+- **Google / PayPal / OpenAI / accounting / e-sign** β each is optional and activates once its env vars are present.
-Add your API keys (`STRIPE_SECRET_KEY`, `NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY`) β that's it. Products and prices are resolved by stable **lookup keys** and auto-created on first checkout (Pro $29/mo, Landlord $59/mo, Lifetime $199, plus annual), so there are **no price IDs to configure** and going live is just an API-key swap. To pre-create the catalog, optionally run `node scripts/stripe-setup.mjs`.
-
-Set up a webhook at `https://yourdomain.com/api/stripe/webhook` listening to:
-- `checkout.session.completed`
-- `customer.subscription.created`
-- `customer.subscription.updated`
-- `customer.subscription.deleted`
-- `invoice.payment_failed`
-- `payment_intent.succeeded`
-
-### 5. Configure email (SMTP)
-
-Use any SMTP provider (e.g. SMTP2GO). Verify your sending domain with the provider, then set `SMTP_HOST`, `SMTP_PORT`, `SMTP_USER`, `SMTP_PASS`, and `EMAIL_FROM`.
-
-### 6. (Optional) Google OAuth
-
-Create OAuth credentials in the Google Cloud Console and set `GOOGLE_CLIENT_ID` / `GOOGLE_CLIENT_SECRET` to enable Google sign-in via Better Auth.
-
-### 7. Run locally
+### 5. βΆοΈ Run locally
```bash
npm run dev
```
-Open [http://localhost:3000](http://localhost:3000).
+Open **[http://localhost:3000](http://localhost:3000)**.
-### 8. Deploy (DigitalOcean App Platform)
+### 6. π’ Deploy
-The repo ships a production `Dockerfile` (Next.js standalone output), an App Platform spec at [`.do/app.yaml`](.do/app.yaml), DO Functions cron under [`functions/`](functions/), and a `/api/health` liveness probe. See **[DIGITALOCEAN.md](DIGITALOCEAN.md)** for the full walkthrough: build/push the image to DOCR, create the app, wire up Managed Postgres + Spaces, and deploy the scheduled cron functions.
+The repo ships a production `Dockerfile` (Next.js standalone), an App Platform spec at [`.do/app.yaml`](.do/app.yaml), DO Functions cron under [`functions/`](functions/), and a `/api/health` probe. Follow **[DIGITALOCEAN.md](DIGITALOCEAN.md)** for the full walkthrough.
---
-## Project structure
+## ποΈ Project structure
```
app/
-βββ (marketing)/ # Landing page, pricing, legal
+βββ (marketing)/ # Landing page, pricing, legal, API docs
βββ (auth)/ # Login, signup, password reset
-βββ (dashboard)/ # All dashboard pages (auth-gated)
-β βββ dashboard/ # Overview + stats
-β βββ properties/ # Property + unit management
-β βββ tenants/ # Tenant profiles
-β βββ rent/ # Payment tracking
-β βββ maintenance/ # Maintenance requests
-β βββ leases/ # Lease tracking
-β βββ expenses/ # Expense logging
-β βββ settings/ # Billing + profile
+βββ (dashboard)/ # Auth-gated app (properties, tenants, rent, maintenance,
+β # leases, expenses, inspections, vendors, reports,
+β # calendar, AI, onboarding, settings)
+βββ (admin)/ # Superadmin dashboard
βββ api/
-β βββ properties/ # CRUD
-β βββ tenants/ # CRUD + auto unit assignment
-β βββ rent/ # CRUD + Stripe payment links
-β βββ maintenance/ # CRUD + status workflow
-β βββ leases/ # CRUD
-β βββ expenses/ # CRUD
-β βββ documents/ # Document metadata (files on local disk)
-β βββ ai/ # Rent receipts + maintenance summaries
-β βββ notifications/ # Send emails via SMTP (SMTP2GO)
-β βββ stripe/ # Checkout, portal, webhook
-β βββ cron/ # Rent reminders + lease expiry alerts
-βββ tenant-portal/[token]/ # Public tenant portal (no login)
+β βββ v1/ # π Public REST API (Bearer API keys)
+β βββ webhooks + cron/ # πͺ Outbound webhook delivery + scheduled jobs
+β βββ stripe/ paypal/ # π³ Billing + payment links + provider webhooks
+β βββ integrations/ # π QuickBooks / Xero OAuth
+β βββ esign/ # βοΈ DocuSign / Dropbox Sign
+β βββ β¦ # Properties, tenants, rent, maintenance, documents, AI
+βββ tenant-portal/[token]/ # Public tenant portal (no login)
lib/
βββ db/ # Drizzle schema, queries, migrations
-βββ auth.ts # Better Auth config
-βββ storage.ts # Local-disk file storage helpers
-βββ stripe/ # Client, plans, payment links
+βββ auth.ts account.ts # Better Auth + team/account scoping
+βββ storage.ts # Object storage (Spaces) with local-disk dev fallback
+βββ webhooks/ # Event catalog, HMAC signing, SSRF guard, delivery
+βββ stripe/ paypal/ # Billing clients & plans
+βββ accounting/ esign/ # QuickBooks/Xero & DocuSign/Dropbox Sign
βββ ai/ # OpenAI client + prompts
βββ email/ # SMTP (SMTP2GO) client + HTML templates
βββ validations/ # Zod schemas for all entities
-
-drizzle.config.ts # Drizzle ORM config (DATABASE_URL, migrations dir)
```
---
-## Database schema
+## ποΈ Data model & isolation
-11 tables, managed via Drizzle ORM:
+The schema spans **~30 tables** managed via Drizzle ORM, grouped roughly as:
-`profiles` Β· `properties` Β· `units` Β· `tenants` Β· `rent_payments` Β· `maintenance_requests` Β· `leases` Β· `expenses` Β· `documents` Β· `notifications` Β· `usage_events`
+- **Core** β `profiles`, `properties`, `units`, `tenants`, `rent_payments`, `maintenance_requests`, `leases`, `expenses`, `documents`, `inspections`, `vendors`
+- **Automation & AI** β `notifications`, `follow_up_rules`, `follow_up_log`, `ai_recommendations`, `ai_predictions`, `activity_log`, `usage_events`
+- **Accounts & platform** β `account_members`, `api_keys`, `app_settings`, `admin_audit_log`, `accounting_connections`, `signature_requests`, `webhook_endpoints`, `webhook_deliveries`
+- **Auth (Better Auth)** β `user`, `session`, `account`, `verification`
-Data isolation is enforced in the application layer: every API route authenticates via `getSessionUser()` and scopes its queries by `user_id`. There is no database-level RLS, so this query scoping must be maintained carefully on every new route and query.
+> π **Tenancy is enforced in the application layer.** Every query scopes by the resolved **account owner id** (team-aware), never the raw session user. There is no database RLS, so this scoping must be preserved on every new route β see `lib/account.ts` (`getEffectiveOwnerId`).
---
-## Cron jobs
+## β° Scheduled jobs
-| Job | Schedule | What it does |
-|-----|----------|--------------|
-| Rent reminders | Daily 9am UTC | Marks overdue payments, sends 3-day reminder emails |
-| Lease expiry | Daily 10am UTC | Sends 60/30/7-day expiry alerts to landlord |
+Cron is driven by DigitalOcean Functions hitting `CRON_SECRET`-protected endpoints (`functions/project.yml`):
-Cron routes are protected with `CRON_SECRET` (Bearer token in `Authorization` header).
+| Job | Schedule (UTC) | What it does |
+|-----|----------------|--------------|
+| `daily` | 09:00 | Rent reminders, overdue marking, 60/30/7-day lease-expiry alerts |
+| `late-fees` | 08:00 | Applies late fees past the grace period |
+| `follow-ups` | 10:00 | Runs each account's active follow-up rules |
+| `webhooks` | every 5 min | Retries pending outbound webhook deliveries |
---
-## License
+## π Security highlights
-MIT
+- π API keys are stored as SHA-256 hashes; the plaintext is shown once.
+- πͺ Webhook payloads are **HMAC-SHA256 signed** (`X-PMN-Signature`); endpoint URLs are **SSRF-guarded** (private/loopback/metadata ranges blocked).
+- π Uploaded files are served only through an auth-gated route; object storage is required in production (uploads **fail loud** rather than silently hit ephemeral disk).
+- π’οΈ Verified TLS to Postgres in production (`DATABASE_SSL=require` + CA).
+- β±οΈ Cron endpoints use a constant-time bearer check and fail closed.
+
+---
+
+## π License
+
+**Proprietary β Β© 2026 Property Management Network. All rights reserved.**
+
+This source code is proprietary and confidential. No license or permission is granted to use, copy, modify, merge, publish, distribute, sublicense, or sell any part of it without the prior written consent of the copyright holder. See [LICENSE](LICENSE).
diff --git a/package.json b/package.json
index e3362a9..03bba16 100644
--- a/package.json
+++ b/package.json
@@ -2,6 +2,7 @@
"name": "property-management-network",
"version": "0.1.0",
"private": true,
+ "license": "UNLICENSED",
"scripts": {
"dev": "next dev",
"build": "next build",