# 22 — Vendor Marketplace

## Context

Fogbreak sits at the center of every real estate transaction. It knows the deal phase, the jurisdiction, the compliance requirements, and the timeline. That means it knows exactly *when* a client needs a contractor, *what kind* of contractor, and *where* the property is — before the client even thinks to ask.

No existing platform connects transaction intelligence to vendor calendars. Homeowner.ai routes referrals post-close. HomeLight and Networx sell generic leads. Keller Williams' CommandMC is a Rolodex. None of them use deal phase data to auto-generate appointments and route them to paying vendors.

**Fogbreak's Vendor Marketplace** is a revenue-generating feature: service providers (handymen, architects, plumbers, inspectors, roofers, landscapers, stagers, cleaners, photographers, etc.) pay a subscription or per-appointment fee to receive auto-routed appointments from active Fogbreak transactions. The vendor's calendar fills itself.

### Why This Works

1. **Transaction phase triggers are the signal.** When a deal hits Phase 2 (inspection), Fogbreak knows the jurisdiction requires a sewer lateral inspection, a water cert, and a roof inspection. That's three vendor appointments generated automatically.
2. **Jurisdiction compliance creates guaranteed demand.** Carmel requires a CFO permit, CAWD sewer lateral, water efficiency cert, and water conservation cert. Those aren't optional — they're legally required. Vendors serving those needs have guaranteed deal flow.
3. **Agents stop being the phone book.** Instead of fielding "who's your plumber?" calls, agents point clients to the marketplace. The agent looks curated. The vendor gets qualified leads. Fogbreak earns revenue.
4. **Post-close revenue extends LTV.** After closing, homeowners still need painters, landscapers, cleaners. The marketplace keeps generating revenue long after the commission check clears.

---

## Revenue Model

### Vendor Pricing Tiers

| Tier | Monthly Fee | Per-Appointment Fee | Features |
|------|-------------|---------------------|----------|
| **Basic** | $49/mo | $15/appointment | Listed in marketplace, manual booking |
| **Pro** | $149/mo | $10/appointment | Priority routing, auto-calendar sync, profile badge |
| **Premium** | $299/mo | $0 (unlimited) | First-in-queue for compliance triggers, featured placement, analytics dashboard |

### Agent/Brokerage Revenue Share

- Tenants (brokerages) earn a configurable revenue share on vendor fees generated through their transactions
- Default: 15% of vendor subscription fees + 20% of per-appointment fees
- Configurable per tenant via `tenants.php` settings
- This incentivizes brokerages to onboard vendors and promote the marketplace

### Transaction-Triggered Appointments (The Moat)

These are appointments auto-generated from compliance requirements — not optional referrals:

| Trigger | Jurisdiction Example | Vendor Category | Estimated Volume |
|---------|---------------------|-----------------|-----------------|
| Deal enters Phase 2 (inspection) | All | Home Inspector | Every deal |
| Compliance: sewer lateral | Carmel, PG, Monterey | CAWD-certified plumber | ~60% of Peninsula deals |
| Compliance: water cert | All Peninsula | Water efficiency inspector | ~80% of deals |
| Compliance: CFO permit | Carmel, Pebble Beach | Arborist / permit expediter | ~25% of deals |
| Compliance: fire rating | Carmel, PG, Pebble Beach, County | Fire safety inspector | ~40% of deals |
| Compliance: roof inspection | Most jurisdictions | Roofing contractor | ~70% of deals |
| Deal enters Phase 5 (pre-close) | All | Cleaning service | ~50% of deals |
| Deal enters Phase 6 (closing) | All | Moving company | ~30% of deals |
| Post-close (30 days) | All | Handyman, landscaper, painter | Ongoing |

---

## Full Feature Set to Build

### 1. Vendor Management Engine

Create `app/api/vendors.php`:

- Vendor registration with profile: business name, categories (multi-select), service area (zip codes or radius), license numbers, insurance verification, portfolio photos
- Subscription tier management (Basic/Pro/Premium)
- Availability calendar: weekly recurring hours + specific date overrides (blackouts, vacations)
- Service catalog: each vendor lists specific services with estimated duration and price range
- Verification workflow: license check, insurance upload, background check flag, tenant admin approval
- Vendor rating system: rolling average from client feedback + agent feedback
- Vendor suspension/deactivation (non-payment, poor ratings, complaint threshold)
- Public vendor profile page (shareable link for vendor's own marketing)

### 2. Marketplace Directory

Add "Vendors" tab to `fogbreak.html` (becomes Tab 9) or integrate into existing tabs:

- Searchable vendor directory: filter by category, jurisdiction, rating, availability, tier
- Map view: vendors plotted by service area (useful for showing which plumber covers Carmel vs Marina)
- Category pages: "Inspectors," "Plumbers," "Contractors," "Cleaning," etc.
- Featured vendors (Premium tier) pinned to top of results
- "Recommended for this deal" widget on transaction detail view — auto-populates based on jurisdiction compliance items
- One-click booking: select vendor → choose date/time from their availability → confirm
- Client-facing version: simplified directory accessible from client portal (portal.html) via magic link

### 3. Transaction-Triggered Auto-Routing

This is the core differentiator. Build into `app/api/transactions.php` and `app/api/vendors.php`:

- **Phase Change Hooks:** When a deal's phase changes, check `vendor_triggers` table for matching triggers
- **Compliance Item Hooks:** When a compliance item is created (from jurisdiction template), check for vendor category match
- **Auto-Match Algorithm:**
  1. Identify required vendor category from trigger
  2. Filter vendors by: category match, service area covers property zip, active subscription, available within deadline window
  3. Rank by: tier (Premium first), rating, distance to property, response time history
  4. Route to top vendor(s): create pending appointment, send notification
  5. If vendor doesn't confirm within configurable window (default: 4 hours), cascade to next vendor
- **Appointment Creation:** Auto-populate with deal data — property address, client name, client phone, compliance requirement, deadline date
- **Agent Override:** Agent can manually select a preferred vendor instead of auto-routing
- **Client Choice Mode:** Send client a short list of 3 matched vendors with ratings and availability, let them pick

### 4. Vendor Calendar Integration

- **Native Calendar:** Built-in calendar in vendor dashboard showing all appointments
- **Google Calendar Sync:** Two-way sync via Google Calendar API (extend existing `calendar.php`)
- **iCal Feed:** Generate .ics subscription URL for any calendar app (Outlook, Apple Calendar)
- **Availability Detection:** If vendor's Google Calendar shows conflict, mark that slot unavailable in Fogbreak
- **Auto-Block:** After booking, block the time slot + travel buffer on vendor's calendar
- **Mobile Push:** Notify vendor of new appointment via push notification (when mobile app is live, instruction 16)

### 5. Appointment Lifecycle

Track every appointment through its full lifecycle:

```
vendor_matched → appointment_proposed → vendor_confirmed → client_notified →
appointment_scheduled → reminder_sent → in_progress → completed →
feedback_requested → feedback_received → payment_processed
```

- **Proposed:** Fogbreak identifies vendor match, sends proposal notification
- **Confirmed:** Vendor accepts the appointment (one-click from email/SMS/app)
- **Scheduled:** Date/time locked, appears on all calendars
- **Reminder:** 24hr and 1hr reminders to vendor and client
- **Completed:** Vendor marks done (can attach photos, notes, invoice)
- **Feedback:** Client rates vendor (1-5 stars + text). Agent can also rate.
- **Payment:** Per-appointment fee charged to vendor (if applicable to their tier)

### 6. Vendor Dashboard

Vendor-facing interface (new page: `vendor-portal.html` or section in existing portal):

- **Upcoming Appointments:** Calendar view + list view of confirmed bookings
- **New Opportunities:** Pending proposals to accept/decline
- **Earnings:** Revenue from Fogbreak referrals, appointment count, average rating
- **Profile Editor:** Update services, availability, photos, pricing
- **Analytics (Premium tier):** Which transaction phases generate their appointments, seasonal trends, top referring agents, conversion rate (proposal → completed)
- **Reviews:** See all client and agent feedback, respond to reviews
- **Subscription Management:** Upgrade/downgrade tier, billing history, payment method

### 7. Payment & Billing

Create `app/api/vendor_billing.php`:

- **Stripe Integration:** Vendor subscribes via Stripe Checkout, recurring billing for monthly tier
- **Per-Appointment Charges:** Metered billing — track completed appointments, charge at end of billing period
- **Revenue Share Calculation:** Compute tenant's share of vendor fees, expose in tenant admin dashboard
- **Invoice Generation:** Monthly invoice to each vendor: subscription fee + per-appointment fees - any credits
- **Free Trial:** 30-day trial for new vendors (no subscription fee, per-appointment fees still apply)
- **Cancellation:** Vendor can cancel anytime, completes current billing period, pending appointments honored

### 8. Agent Tools

Integrated into existing agent workflow in `fogbreak.html`:

- **Deal Sidebar Widget:** On any transaction detail, show "Recommended Vendors" based on compliance items
- **One-Click Dispatch:** Agent clicks "Book Inspector" → top-matched vendor gets proposal → appointment created
- **Preferred Vendors List:** Each agent maintains a personal preferred vendor list (overrides auto-matching)
- **Vendor Notes:** Agent can add private notes to vendor profile ("slow but thorough," "great with sellers")
- **Commission Tracking:** If agent earns referral bonus from vendor, track it alongside deal commission

### 9. Client Experience

Via client portal (`portal.html`) and future mobile app:

- **Vendor Recommendations:** "Your transaction requires these services" — list of matched vendors with ratings
- **Self-Booking:** Client picks vendor and time slot directly
- **Appointment Tracker:** See all upcoming vendor appointments for their transaction
- **Post-Service Feedback:** Rate and review vendor after appointment
- **Post-Close Directory:** After deal closes, continued access to marketplace for home maintenance needs

### 10. AI Enhancements

Leverage self-hosted AI layer (instruction 03) for marketplace intelligence:

- **Smart Matching:** AI considers vendor specialization, past performance on similar properties, client preferences
- **Price Estimation:** AI estimates service cost based on property attributes (sqft, age, condition) and vendor pricing history
- **Review Summarization:** AI generates vendor summary from all reviews ("reliable, on-time, specializes in Victorian homes")
- **Demand Forecasting:** Predict vendor demand by category based on pipeline data (e.g., "12 deals entering inspection phase next week → alert inspectors")
- **Vendor Recommendations Email:** AI-generated weekly email to agents: "Based on your pipeline, you'll likely need these vendors in the next 2 weeks"
- **Fair Housing Audit:** All vendor-facing content and recommendations audited for discriminatory patterns (e.g., ensuring vendor routing isn't biased by neighborhood demographics)

---

## Database Tables

### New Tables

```sql
-- Vendor profiles
CREATE TABLE vendors (
    id INT AUTO_INCREMENT PRIMARY KEY,
    tenant_id INT DEFAULT NULL,
    business_name VARCHAR(255) NOT NULL,
    contact_name VARCHAR(255) NOT NULL,
    email VARCHAR(255) NOT NULL,
    phone VARCHAR(20),
    website VARCHAR(500),
    description TEXT,
    license_number VARCHAR(100),
    insurance_verified BOOLEAN DEFAULT FALSE,
    insurance_expiry DATE,
    background_check ENUM('pending','passed','failed','not_required') DEFAULT 'not_required',
    subscription_tier ENUM('basic','pro','premium','trial') DEFAULT 'trial',
    subscription_status ENUM('active','past_due','canceled','suspended') DEFAULT 'active',
    stripe_customer_id VARCHAR(100),
    stripe_subscription_id VARCHAR(100),
    rating_avg DECIMAL(3,2) DEFAULT 0.00,
    rating_count INT DEFAULT 0,
    response_time_avg INT DEFAULT NULL COMMENT 'Average minutes to respond to proposals',
    profile_photo VARCHAR(500),
    is_active BOOLEAN DEFAULT TRUE,
    approved_at DATETIME DEFAULT NULL,
    approved_by INT DEFAULT NULL,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    INDEX idx_tenant (tenant_id),
    INDEX idx_tier (subscription_tier),
    INDEX idx_rating (rating_avg DESC),
    INDEX idx_active (is_active, subscription_status)
);

-- Vendor service categories
CREATE TABLE vendor_categories (
    id INT AUTO_INCREMENT PRIMARY KEY,
    tenant_id INT DEFAULT NULL,
    vendor_id INT NOT NULL,
    category ENUM(
        'home_inspector','plumber','electrician','roofer','hvac',
        'pest_control','arborist','landscaper','painter','handyman',
        'cleaning','moving','stager','photographer','videographer',
        'architect','general_contractor','permit_expediter',
        'sewer_lateral','water_inspector','fire_inspector',
        'pool_inspector','chimney_inspector','mold_inspector',
        'foundation_inspector','septic_inspector','well_inspector',
        'surveyor','appraiser','locksmith','window_installer',
        'flooring','carpet_cleaning','junk_removal','other'
    ) NOT NULL,
    is_primary BOOLEAN DEFAULT FALSE,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    INDEX idx_vendor (vendor_id),
    INDEX idx_category (category),
    INDEX idx_tenant_cat (tenant_id, category)
);

-- Vendor service areas (zip codes or radius)
CREATE TABLE vendor_service_areas (
    id INT AUTO_INCREMENT PRIMARY KEY,
    vendor_id INT NOT NULL,
    zip_code VARCHAR(10) DEFAULT NULL,
    city VARCHAR(100) DEFAULT NULL,
    radius_miles INT DEFAULT NULL COMMENT 'If set, radius from vendor address',
    latitude DECIMAL(10,7) DEFAULT NULL,
    longitude DECIMAL(10,7) DEFAULT NULL,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    INDEX idx_vendor (vendor_id),
    INDEX idx_zip (zip_code)
);

-- Vendor availability (weekly recurring + overrides)
CREATE TABLE vendor_availability (
    id INT AUTO_INCREMENT PRIMARY KEY,
    vendor_id INT NOT NULL,
    day_of_week TINYINT DEFAULT NULL COMMENT '0=Sun, 6=Sat. NULL for specific date override',
    specific_date DATE DEFAULT NULL COMMENT 'For one-off availability or blackouts',
    start_time TIME NOT NULL,
    end_time TIME NOT NULL,
    is_available BOOLEAN DEFAULT TRUE COMMENT 'FALSE = blackout/vacation',
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    INDEX idx_vendor_day (vendor_id, day_of_week),
    INDEX idx_vendor_date (vendor_id, specific_date)
);

-- Vendor services (specific offerings with pricing)
CREATE TABLE vendor_services (
    id INT AUTO_INCREMENT PRIMARY KEY,
    vendor_id INT NOT NULL,
    service_name VARCHAR(255) NOT NULL,
    category ENUM('home_inspector','plumber','electrician','roofer','hvac','pest_control','arborist','landscaper','painter','handyman','cleaning','moving','stager','photographer','videographer','architect','general_contractor','permit_expediter','sewer_lateral','water_inspector','fire_inspector','pool_inspector','chimney_inspector','mold_inspector','foundation_inspector','septic_inspector','well_inspector','surveyor','appraiser','locksmith','window_installer','flooring','carpet_cleaning','junk_removal','other') NOT NULL,
    description TEXT,
    price_min DECIMAL(10,2) DEFAULT NULL,
    price_max DECIMAL(10,2) DEFAULT NULL,
    price_type ENUM('fixed','hourly','estimate','free') DEFAULT 'estimate',
    duration_minutes INT DEFAULT 60,
    is_active BOOLEAN DEFAULT TRUE,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    INDEX idx_vendor (vendor_id),
    INDEX idx_category (category)
);

-- Transaction-to-vendor trigger rules
CREATE TABLE vendor_triggers (
    id INT AUTO_INCREMENT PRIMARY KEY,
    tenant_id INT DEFAULT NULL,
    market_id INT DEFAULT NULL COMMENT 'From markets table (instruction 01)',
    trigger_type ENUM('phase_change','compliance_item','post_close','manual') NOT NULL,
    trigger_value VARCHAR(100) NOT NULL COMMENT 'Phase number, compliance_item name, or days_post_close',
    vendor_category ENUM('home_inspector','plumber','electrician','roofer','hvac','pest_control','arborist','landscaper','painter','handyman','cleaning','moving','stager','photographer','videographer','architect','general_contractor','permit_expediter','sewer_lateral','water_inspector','fire_inspector','pool_inspector','chimney_inspector','mold_inspector','foundation_inspector','septic_inspector','well_inspector','surveyor','appraiser','locksmith','window_installer','flooring','carpet_cleaning','junk_removal','other') NOT NULL,
    priority INT DEFAULT 0 COMMENT 'Higher = more urgent. Compliance triggers > convenience triggers',
    auto_route BOOLEAN DEFAULT TRUE COMMENT 'FALSE = suggest only, TRUE = auto-propose to vendor',
    deadline_offset_days INT DEFAULT NULL COMMENT 'Days from trigger to needed-by date',
    description TEXT,
    is_active BOOLEAN DEFAULT TRUE,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    INDEX idx_tenant (tenant_id),
    INDEX idx_trigger (trigger_type, trigger_value),
    INDEX idx_market (market_id)
);

-- Vendor appointments (the core transaction)
CREATE TABLE vendor_appointments (
    id INT AUTO_INCREMENT PRIMARY KEY,
    tenant_id INT DEFAULT NULL,
    vendor_id INT NOT NULL,
    transaction_id INT DEFAULT NULL COMMENT 'FK to transactions table',
    client_id INT DEFAULT NULL COMMENT 'FK to clients table',
    agent_id INT DEFAULT NULL COMMENT 'FK to admins table (requesting agent)',
    trigger_id INT DEFAULT NULL COMMENT 'FK to vendor_triggers (if auto-generated)',
    compliance_item_id INT DEFAULT NULL COMMENT 'FK to compliance_items (if compliance-driven)',
    vendor_service_id INT DEFAULT NULL COMMENT 'FK to vendor_services',
    property_address TEXT,
    property_zip VARCHAR(10),
    status ENUM(
        'vendor_matched','proposed','vendor_confirmed','client_notified',
        'scheduled','reminder_sent','in_progress','completed',
        'canceled_by_vendor','canceled_by_client','canceled_by_agent',
        'no_show','expired'
    ) DEFAULT 'vendor_matched',
    proposed_at DATETIME DEFAULT NULL,
    confirmed_at DATETIME DEFAULT NULL,
    scheduled_date DATE NOT NULL,
    scheduled_time_start TIME NOT NULL,
    scheduled_time_end TIME NOT NULL,
    actual_start DATETIME DEFAULT NULL,
    actual_end DATETIME DEFAULT NULL,
    needed_by_date DATE DEFAULT NULL COMMENT 'Compliance deadline',
    notes TEXT,
    vendor_notes TEXT COMMENT 'Notes from vendor after completion',
    attached_photos JSON DEFAULT NULL COMMENT 'URLs of photos vendor uploaded',
    invoice_amount DECIMAL(10,2) DEFAULT NULL,
    appointment_fee DECIMAL(10,2) DEFAULT NULL COMMENT 'Fee charged to vendor for this lead',
    fee_status ENUM('pending','charged','waived','refunded') DEFAULT 'pending',
    cascade_position INT DEFAULT 1 COMMENT 'Which vendor in the cascade chain',
    cascade_deadline DATETIME DEFAULT NULL COMMENT 'When to cascade to next vendor',
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    INDEX idx_tenant (tenant_id),
    INDEX idx_vendor (vendor_id),
    INDEX idx_transaction (transaction_id),
    INDEX idx_client (client_id),
    INDEX idx_status (status),
    INDEX idx_date (scheduled_date),
    INDEX idx_needed_by (needed_by_date)
);

-- Vendor reviews
CREATE TABLE vendor_reviews (
    id INT AUTO_INCREMENT PRIMARY KEY,
    tenant_id INT DEFAULT NULL,
    vendor_id INT NOT NULL,
    appointment_id INT NOT NULL,
    reviewer_type ENUM('client','agent') NOT NULL,
    reviewer_id INT NOT NULL,
    rating TINYINT NOT NULL COMMENT '1-5 stars',
    review_text TEXT,
    vendor_response TEXT DEFAULT NULL,
    vendor_responded_at DATETIME DEFAULT NULL,
    is_public BOOLEAN DEFAULT TRUE,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    INDEX idx_vendor (vendor_id),
    INDEX idx_appointment (appointment_id),
    INDEX idx_rating (vendor_id, rating)
);

-- Vendor billing / payment events
CREATE TABLE vendor_billing (
    id INT AUTO_INCREMENT PRIMARY KEY,
    tenant_id INT DEFAULT NULL,
    vendor_id INT NOT NULL,
    billing_type ENUM('subscription','appointment_fee','refund','credit','revenue_share') NOT NULL,
    amount DECIMAL(10,2) NOT NULL,
    description VARCHAR(500),
    stripe_invoice_id VARCHAR(100) DEFAULT NULL,
    stripe_charge_id VARCHAR(100) DEFAULT NULL,
    appointment_id INT DEFAULT NULL,
    billing_period_start DATE DEFAULT NULL,
    billing_period_end DATE DEFAULT NULL,
    status ENUM('pending','paid','failed','refunded') DEFAULT 'pending',
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    INDEX idx_vendor (vendor_id),
    INDEX idx_status (status),
    INDEX idx_period (billing_period_start, billing_period_end)
);

-- Agent preferred vendors
CREATE TABLE agent_preferred_vendors (
    id INT AUTO_INCREMENT PRIMARY KEY,
    tenant_id INT DEFAULT NULL,
    agent_id INT NOT NULL,
    vendor_id INT NOT NULL,
    category ENUM('home_inspector','plumber','electrician','roofer','hvac','pest_control','arborist','landscaper','painter','handyman','cleaning','moving','stager','photographer','videographer','architect','general_contractor','permit_expediter','sewer_lateral','water_inspector','fire_inspector','pool_inspector','chimney_inspector','mold_inspector','foundation_inspector','septic_inspector','well_inspector','surveyor','appraiser','locksmith','window_installer','flooring','carpet_cleaning','junk_removal','other') NOT NULL,
    notes TEXT COMMENT 'Private agent notes about this vendor',
    priority INT DEFAULT 0 COMMENT 'Agent ranking within category',
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    UNIQUE KEY uk_agent_vendor_cat (agent_id, vendor_id, category),
    INDEX idx_agent_cat (agent_id, category)
);
```

### Seed Data — Monterey Peninsula Vendor Triggers

```sql
-- Phase-based triggers (universal)
INSERT INTO vendor_triggers (trigger_type, trigger_value, vendor_category, priority, auto_route, deadline_offset_days, description) VALUES
('phase_change', '2', 'home_inspector', 90, TRUE, 5, 'Home inspection due within contingency period'),
('phase_change', '5', 'cleaning', 30, FALSE, 3, 'Pre-close cleaning recommended'),
('phase_change', '6', 'moving', 20, FALSE, 7, 'Moving service for closing'),
('post_close', '7', 'handyman', 10, FALSE, NULL, 'Post-close: handyman services available'),
('post_close', '14', 'landscaper', 10, FALSE, NULL, 'Post-close: landscaping services available'),
('post_close', '30', 'painter', 10, FALSE, NULL, 'Post-close: painting services available');

-- Carmel-by-the-Sea compliance triggers
INSERT INTO vendor_triggers (trigger_type, trigger_value, vendor_category, priority, auto_route, deadline_offset_days, description, market_id) VALUES
('compliance_item', 'cfo_permit', 'arborist', 80, TRUE, 15, 'Carmel CFO permit requires arborist assessment', NULL),
('compliance_item', 'cfo_permit', 'permit_expediter', 75, TRUE, 20, 'CFO permit application and processing', NULL),
('compliance_item', 'cawd_sewer_lateral', 'sewer_lateral', 85, TRUE, 10, 'CAWD sewer lateral certification required', NULL),
('compliance_item', 'water_efficiency_cert', 'water_inspector', 80, TRUE, 10, 'Water efficiency certificate inspection', NULL),
('compliance_item', 'water_conservation_cert', 'water_inspector', 80, TRUE, 10, 'Water conservation certificate (separate from efficiency)', NULL),
('compliance_item', 'fire_rating', 'fire_inspector', 85, TRUE, 14, 'Fire rating inspection required', NULL);

-- Pacific Grove compliance triggers
INSERT INTO vendor_triggers (trigger_type, trigger_value, vendor_category, priority, auto_route, deadline_offset_days, description, market_id) VALUES
('compliance_item', 'rzrr', 'permit_expediter', 80, TRUE, 10, 'PG Residential Zoning Requirements Review (10 biz days)', NULL),
('compliance_item', 'water_cert_pg', 'water_inspector', 80, TRUE, 7, 'PG water certificate inspection', NULL),
('compliance_item', 'fire_rating_pg', 'fire_inspector', 80, TRUE, 10, 'PG fire rating inspection', NULL);

-- Roof inspection (most jurisdictions)
INSERT INTO vendor_triggers (trigger_type, trigger_value, vendor_category, priority, auto_route, deadline_offset_days, description) VALUES
('compliance_item', 'roof_inspection', 'roofer', 70, TRUE, 7, 'Roof inspection per jurisdiction requirements');
```

---

## API Endpoints for vendors.php

```
# Vendor Profile Management
GET    /api/vendors.php?action=list                          # List vendors (filterable)
GET    /api/vendors.php?action=detail&id=X                   # Vendor detail + reviews
POST   /api/vendors.php?action=register                      # Vendor self-registration
POST   /api/vendors.php?action=update&id=X                   # Update vendor profile
POST   /api/vendors.php?action=approve&id=X                  # Admin: approve vendor
POST   /api/vendors.php?action=suspend&id=X                  # Admin: suspend vendor
POST   /api/vendors.php?action=set_availability              # Set weekly hours / blackouts
GET    /api/vendors.php?action=availability&id=X&date=Y      # Get available slots for date

# Marketplace & Search
GET    /api/vendors.php?action=search                        # Search vendors (category, zip, rating)
GET    /api/vendors.php?action=recommended&transaction_id=X  # AI-matched vendors for a deal
GET    /api/vendors.php?action=categories                    # List all vendor categories

# Appointments
POST   /api/vendors.php?action=create_appointment            # Book or propose appointment
POST   /api/vendors.php?action=confirm_appointment&id=X      # Vendor confirms
POST   /api/vendors.php?action=decline_appointment&id=X      # Vendor declines (cascades)
POST   /api/vendors.php?action=cancel_appointment&id=X       # Cancel appointment
POST   /api/vendors.php?action=complete_appointment&id=X     # Mark completed + attach docs
GET    /api/vendors.php?action=appointments                  # List appointments (filterable)
GET    /api/vendors.php?action=appointment_detail&id=X       # Full appointment detail

# Reviews
POST   /api/vendors.php?action=submit_review                 # Client or agent submits review
POST   /api/vendors.php?action=respond_review&id=X           # Vendor responds to review
GET    /api/vendors.php?action=reviews&vendor_id=X           # Get vendor reviews

# Vendor Dashboard
GET    /api/vendors.php?action=dashboard                     # Vendor's own dashboard data
GET    /api/vendors.php?action=earnings                      # Revenue breakdown
GET    /api/vendors.php?action=analytics                     # Premium: detailed analytics

# Triggers & Auto-Routing
GET    /api/vendors.php?action=triggers                      # List vendor triggers
POST   /api/vendors.php?action=create_trigger                # Admin: create trigger rule
POST   /api/vendors.php?action=update_trigger&id=X           # Admin: update trigger
POST   /api/vendors.php?action=process_triggers              # Cron: process pending triggers

# Billing
GET    /api/vendors.php?action=billing_history               # Vendor billing history
POST   /api/vendors.php?action=subscribe                     # Start/change subscription
POST   /api/vendors.php?action=cancel_subscription           # Cancel subscription
GET    /api/vendors.php?action=revenue_share                 # Tenant admin: revenue share report

# Agent Tools
GET    /api/vendors.php?action=preferred&agent_id=X          # Agent's preferred vendors
POST   /api/vendors.php?action=set_preferred                 # Add/remove preferred vendor
POST   /api/vendors.php?action=dispatch                      # One-click dispatch from deal view
```

---

## Cron Jobs (add to cron.php)

- **vendor_trigger_processor:** On every cron run (15 min), check for deals with recent phase changes or new compliance items. Match against `vendor_triggers` table. Create `vendor_appointments` with status `vendor_matched`. Route to highest-priority matching vendor.

- **vendor_cascade:** Check appointments in `proposed` status past their `cascade_deadline`. If vendor hasn't confirmed, move to next vendor in match ranking. After 3 cascades with no confirmation, notify agent for manual intervention.

- **vendor_appointment_reminders:** 24hr and 1hr before scheduled appointments, send reminders to both vendor and client via email (and SMS when instruction 19 is live).

- **vendor_feedback_requests:** After appointment status changes to `completed`, wait 2 hours then send feedback request to client and agent.

- **vendor_billing_cycle:** Monthly: calculate per-appointment fees for each vendor, generate invoice records, trigger Stripe charges. Calculate revenue share per tenant.

- **vendor_rating_update:** After new review, recalculate vendor's `rating_avg` and `rating_count`. If rating drops below 3.0, flag for admin review.

- **vendor_demand_forecast:** Weekly: analyze pipeline data (deals in early phases) to predict vendor demand by category for next 2 weeks. Send "demand alert" email to relevant vendors.

---

## Integration Points

### With Existing Modules

| Module | Integration |
|--------|------------|
| `transactions.php` | Phase change fires vendor trigger check. Deal detail shows recommended vendors sidebar. |
| `calendar.php` | Vendor appointments sync to agent's calendar. Extend Google Calendar sync for vendor calendars. |
| `email.php` | Appointment notifications, feedback requests, vendor onboarding emails. All pass Fair Housing audit. |
| `drips.php` | Post-close drip campaigns include vendor marketplace links ("Need a painter? See our trusted vendors"). |
| `documents.php` | Vendor uploads (inspection reports, invoices, certificates) stored via document system. |
| `commissions.php` | Vendor referral revenue tracked alongside deal commissions. |
| `tenants.php` | Per-tenant vendor marketplace settings: revenue share %, featured categories, approval requirements. |
| `cron.php` | 7 new cron jobs added (see above). |
| `properties.php` | Property attributes (sqft, age, type) inform AI vendor matching and price estimation. |

### With Future Modules

| Instruction | Integration |
|-------------|------------|
| `03-SELF-HOSTED-AI` | AI vendor matching, review summarization, demand forecasting, price estimation |
| `05-SHOWINGTIME-REPLACEMENT` | Pre-showing vendor services (stager, photographer, cleaner) triggered by listing activation |
| `15-NEXTJS-WEB-APP` | Vendor portal as Next.js page, real-time appointment updates via Socket.io |
| `16-REACT-NATIVE-MOBILE` | Vendor mobile app: accept appointments, upload photos, mark complete from job site |
| `17-CLIENT-PORTAL` | Client-facing vendor directory, self-booking, appointment tracker |
| `19-SMS-TWILIO` | Vendor appointment confirmations via SMS (reply Y/N), client reminders |
| `20-ADVANCED-ANALYTICS` | Vendor ROI metrics, category performance, revenue forecasting |

---

## UI Changes

### fogbreak.html — Agent View

1. **Transaction Detail — Vendor Sidebar:** Below compliance checklist, show "Recommended Vendors" panel with top matches per compliance item. One-click "Dispatch" button.

2. **New Tab or Sub-Tab — Vendor Directory:** Searchable, filterable marketplace. Agent can browse, book, add to preferred list.

3. **Dashboard Widget — Vendor Activity:** "X vendor appointments this week" with upcoming list. Click to expand.

### vendor-portal.html — Vendor View (New Page)

1. **Login:** Email + password or magic link
2. **Dashboard:** Upcoming appointments, new proposals, earnings summary, rating
3. **Calendar:** Full calendar view of booked and available slots
4. **Profile:** Edit business info, services, availability, photos
5. **Reviews:** All reviews with option to respond
6. **Billing:** Subscription tier, payment history, upcoming charges
7. **Analytics (Premium):** Charts showing referral sources, seasonal trends, revenue growth

### portal.html — Client View (Extend Existing)

1. **Vendor Recommendations:** "Services needed for your transaction" with matched vendors
2. **Booking:** Select vendor, pick time slot, confirm
3. **My Appointments:** List of upcoming and past vendor appointments
4. **Rate Vendor:** Post-appointment feedback form

---

## Acceptance Criteria

- [ ] Vendor registration, profile management, and admin approval workflow
- [ ] Subscription tiers (Basic/Pro/Premium) with Stripe billing
- [ ] Searchable vendor directory with category, location, and rating filters
- [ ] Transaction phase triggers auto-generate vendor appointment proposals
- [ ] Compliance item triggers route to jurisdiction-specific vendor categories
- [ ] Cascade routing: if vendor doesn't confirm, auto-escalate to next match
- [ ] Vendor calendar with Google Calendar two-way sync and iCal feed
- [ ] Full appointment lifecycle tracking (proposed → completed → reviewed)
- [ ] Client and agent review system with vendor response capability
- [ ] Revenue share calculation per tenant
- [ ] Per-appointment fee tracking and monthly billing
- [ ] Agent preferred vendor list with manual override of auto-routing
- [ ] Client-facing vendor recommendations in portal
- [ ] Post-close vendor recommendations via drip campaigns
- [ ] Vendor dashboard with earnings, appointments, and analytics
- [ ] All features tenant-aware (vendor_triggers, vendors, appointments filtered by tenant_id)
- [ ] All vendor-facing content passes Fair Housing audit
- [ ] 7 cron jobs operational: trigger processor, cascade, reminders, feedback, billing, ratings, demand forecast
- [ ] Monterey Peninsula seed data: Carmel, PG, Monterey, Marina, Pebble Beach compliance triggers populated

---

## Implementation Notes

1. **Start with the database tables and trigger engine.** The auto-routing logic is the core value. Get triggers → vendor matching → appointment creation working before building the UI.

2. **Stripe integration can be stubbed initially.** Track subscription tiers and per-appointment fees in the database. Wire up Stripe webhooks when ready for production billing.

3. **Vendor self-registration should be simple.** Name, email, phone, categories, service area. Don't require 20 fields upfront — vendors can fill out their full profile after registration.

4. **Cascade timeout is configurable per tenant.** Default 4 hours. High-urgency compliance items (sewer lateral with 5-day deadline) might cascade after 1 hour.

5. **The "Recommended for this deal" widget is the killer feature for agent adoption.** When an agent opens a transaction and sees exactly which vendors they need, pre-matched and one click away — that's the moment they understand the value.

6. **Post-close revenue is the long tail.** The compliance-driven appointments happen during the transaction. But the post-close handyman/landscaper/painter referrals generate revenue for years. Build the post-close trigger system to keep sending curated vendor recommendations via drip campaigns.

7. **Fair Housing applies here too.** Vendor routing must not create discriminatory patterns — e.g., routing premium vendors only to high-value neighborhoods. The matching algorithm considers vendor service area and availability, not neighborhood demographics.

8. **Vendor categories are extensible.** The ENUM list covers common categories but can be expanded via ALTER TABLE. Future: move categories to a reference table for dynamic management without schema changes.

---

## Dependencies

- **Hard dependency on instruction 01 (Geographic Extraction):** Vendor triggers use `market_id` to associate with specific markets. Market templates must exist in the database.
- **Hard dependency on instruction 07 (Paperless Pipeline):** Compliance items are the primary trigger source. The TC workflow engine must be generating compliance items per jurisdiction.
- **Soft dependency on instruction 03 (Self-Hosted AI):** AI matching, review summarization, and demand forecasting require the AI layer. Basic matching works without AI (tier + rating + distance).
- **Soft dependency on instruction 19 (SMS/Twilio):** SMS confirmations and reminders. Email-only works without Twilio.
- **Soft dependency on instruction 15 (Next.js):** Vendor portal can be built as a standalone PHP page initially, migrated to Next.js later.

---

## Revenue Projection (Monterey Peninsula — Tenant #1)

Conservative estimate for first 12 months:

| Metric | Estimate |
|--------|----------|
| Active vendors (year 1) | 30-50 |
| Average tier | Pro ($149/mo) |
| Monthly subscription revenue | $4,470 - $7,450 |
| Deals per month (Matthams Group) | 4-6 |
| Vendor appointments per deal | 3-5 |
| Per-appointment fees (non-Premium) | $10-15 avg |
| Monthly appointment fee revenue | $120 - $450 |
| **Monthly total (Tenant #1)** | **$4,590 - $7,900** |
| **Annual total (Tenant #1)** | **$55,000 - $95,000** |

Scale factor: Each new brokerage tenant onboarded multiplies this. A 10-agent brokerage doing 8-12 deals/month generates 2-3x the vendor demand.

---

**End of Instruction 22**

Next: Update `00-MASTER-PLAN.md` to include instruction 22 in the execution order (Phase 4: Scale, after instruction 21). Dependencies: runs after instructions 01 and 07, benefits from 03, 15, 19.
