# Storage Valet Wiki — Full Content
> Generated: Mar 17, 2026
> Auto-generated from sv-wiki HTML pages.
> Canonical source: https://wiki.mystoragevalet.com
---
# Storage Valet Wiki
---
# Business Overview
*Last updated: Feb 27, 2026*
---
## Executive Summary
Storage Valet is a **premium, white-glove storage concierge** built for dense, high-income urban markets where space is scarce and convenience is highly valued. The service replaces traditional self-storage—an inconvenient, vehicle-dependent utility—with a planned, service-oriented model that prioritizes reliability, clarity, and trust.
> **Core Value Proposition**
>
> SV picks up, stores, and returns items **as needed** with a 48-hour lead time that encourages planned, reliable scheduling. Customers photograph and manage their own digital inventory in the portal.
>
---
## What Storage Valet Is / Is Not
#### ✓ IS
- A **premium, white-glove storage concierge** for dense urban households
- A **service + data platform** combining logistics reliability with item-level inventory intelligence
- A **planning-friendly system** (48-hour lead time) designed for batching, reliability, and cost control
#### ✗ IS NOT
- Traditional self-storage
- On-demand or instant retrieval
- Itemized or cubic-foot pricing presented to customers
- A moving company or warehouse rental
---
## Why Storage Valet Wins
| Advantage | Description |
| ----------------------- | -------------------------------------------------------------------------------------------------------------------- |
| **Price Arbitrage** | Lease low-cost storage on Hudson County perimeter; serve high-rent neighborhoods |
| **Density Economics** | Concentrate ops in luxury buildings to batch service days, reduce cost per stop |
| **Inventory Layer** | Item-level tracking + search makes stored goods usable, not buried |
| **AI-First Operations** | Automations + AI handle scheduling, customer comms, and ops management—minimal manual overhead keeps margins at 80%+ |
### The Problem: Space-Cost Gap
Urban households face a structural mismatch: apartments are expensive per square foot, families accumulate "overflow" faster than apartments can expand, and traditional self-storage is inconvenient, vehicle-dependent, and time-intensive.
#### Hoboken Rent Progression (March 2025 data)
| Upgrade | Monthly Increase | Percentage |
| --------- | ---------------- | ---------- |
| 1BR → 2BR | +$805/month | +21.4% |
| 2BR → 3BR | +$1,626/month | +35.6% |
| Avg 1BR | $3,762/month | — |
> **Implication**
>
> SV at $299/month is positioned as the "cheaper-than-upsizing" alternative.
>
---
## Competitive Positioning
> **The Estuary Benchmark**
>
> The Reserve at Estuary (Weehawken, NJ) on-site locker pricing is used as the primary competitive benchmark for validating SV's pricing model.
>
### Head-to-Head Comparison
| Feature | On-Site Locker (Estuary) | Storage Valet |
| ---------------------- | ------------------------ | ----------------------------- |
| **Monthly Rate** | $185 | $299 |
| **Dimensions** | 3' x 4' x 8' | ~5' x 5' x 8' |
| **Total Volume** | 96 cu ft | ~200 cu ft |
| **Raw Cost per cu ft** | $1.93 | $1.50 |
| **Access Method** | DIY (basement cage) | Full-service valet |
| **Insurance** | Not included | $2,000 included |
| **Inventory Mgmt** | None | Digital inventory |
| **Labor** | Self-serve | White-glove pickup & delivery |
### True Value Comparison (Service-Adjusted)
To achieve a comparable service level to Storage Valet, a building locker user would need:
| Hidden Cost | Basis | Monthly Value |
| --------------------------- | -------------------------------------------- | ------------- |
| **Labor** | 4x yearly moves at $50/hr x 2 hrs, amortized | $33/mo |
| **Insurance** | Standalone $2,000 coverage policy | $12/mo |
| **Inventory Software** | Digital home inventory app | $10/mo |
| **SV Net Cost for Space** | $299 - $33 - $12 - $10 | **$244/mo** |
| **Adjusted Cost per cu ft** | $244 / 200 cu ft | **$1.22** |
> **The Verdict**
>
> **On-site locker:** $1.93/cu ft (no services). **Storage Valet:** $1.22/cu ft (after service value adjustment). SV delivers a **36% lower cost per cubic foot** with double the capacity and full concierge service included.
>
### Qualitative Differentiators
- **Climate controlled** — Professional facility vs. wire cages in humid basements
- **Visual inventory** — Digital catalog vs. "black box" locker
- **Active rotation** — Valet service encourages seasonal swaps; lockers become static junk drawers
---
## Pricing & Packaging (Authoritative)
> **⚠️ Critical Information**
>
> **$299/month, single plan** — No setup fee (eliminated for simplicity). 14-day complimentary trial on all new subscriptions.
>
### What's Included
- Storage capacity (200 cubic feet — typical city apartment overflow)
- Pickup & return with **48-hour standard lead time**
- Advance delivery scheduling (weeks/months in advance)
- Baseline insurance ($2,000 included coverage)
- Digital inventory management via customer portal
### Why 14-Day Trial (vs. Setup Fee)
Our 48-hour lead time creates a gap between signup and first service. Even in the best case — inventory ready, first slot booked immediately — customers wait at least two days before their first pickup. Asking for significant upfront payment ($299+) before they can experience the service created friction.
The trial solves this: customers explore the portal, create their digital inventory, and complete their first pickup before billing begins. By the time they're charged, they've already experienced the service.
*Note: We may revisit the setup fee once we have established trust and reviews. See for full rationale.*
### Why a Single Flat Plan
- Seasonal utilization would create churn if downgrades were allowed
- Customers may **add capacity or coverage** but do not downgrade during slower periods
- Tiered/bin pricing created decision paralysis and incremental churn in testing
### Policy Guardrails
| Scenario | Policy |
| ----------------------------- | ------------------------------------------------------------------------------------- |
| High-volume households | Add an additional $299 subscription (simple, consistent) |
| Special handling / exclusions | Oversized furniture, hazardous items, high-value categories without declared value |
| Scheduling standard | Retrievals and returns **as needed** with 48-hour lead time (bookable far in advance) |
---
## Service Areas — Authoritative Data
### Active Service ZIPs (13 Total)
07020Edgewater07030Hoboken07047North Bergen07086Weehawken07087Union City07093West New York07302Jersey City07304Jersey City07305Jersey City07306Jersey City07307Jersey City (Heights)07310Jersey City (Newport)07311Jersey City (Exchange Place)
### Expansion Reference (Ops/Marketing Planning)
- **NJ Regional Expansion:** Broader Hudson County coverage and adjacent high-density NJ markets
- **Manhattan Expansion:** Financial District to Upper West Side, with strong emphasis on west side neighborhoods (West Village, Chelsea, Hell's Kitchen, Lincoln Square, Upper West Side)
---
## Unit Economics
### Per-Customer Profitability (Monthly)
**Revenue:** $299/month
#### Variable Cost Drivers
1. Pickup/return labor + vehicle
2. Storage footprint allocation (sqft leased)
3. Payment processing (~$9)
4. Insurance/claims reserve
5. Overhead/CapEx amortization (technology, business insurance, other fixed costs)
#### Illustrative Ranges
| Cost Category | Low | High |
| --------------------------- | ----- | ----- |
| Processing | $9 | $9 |
| Ops cost per pickup/return | $25 | $55 |
| Storage rent allocation | $8 | $20 |
| Claims reserve | $3 | $8 |
| Overhead/CapEx amortization | $5 | $15 |
| **Total Variable** | ~$50 | ~$105 |
| **Contribution Margin** | ~$249 | ~$194 |
| **Margin %** | 83% | 65% |
> **🤖 Why We Hit the Low End**
>
> **AI-First Operations** is how Storage Valet achieves 83% margins instead of 65%. By running on automations (Zapier, Calendly, Stripe) and AI-assisted management (Claude Code, ChatGPT, Gemini), we minimize manual overhead. Every system must be API-enabled and AI-accessible—if it can't be automated, it's not a viable solution.
>
### CAC : LTV
| Metric | Range | Notes |
| ------------------- | --------------- | ---------------------------------------------- |
| Contribution Margin | $200–$250/month | — |
| Customer Lifetime | 8–36 months | — |
| LTV | $1,600–$9,000 | — |
| CAC | $0–$250 | Partnerships (low) vs. paid acquisition (high) |
| LTV:CAC | 6×–∞ | $0 CAC = infinite ratio |
| Payback | 0–2 months | — |
### Storage Economics
**Current model:** Leased storage space scales proportionately with customer count. No exponential margin improvement from space utilization alone — costs grow with revenue.
**Long-term opportunity:** At scale, transition to a purpose-built warehouse facility designed for:
- Optimized vertical storage and density
- Robotics-ready infrastructure (e.g., Tesla Optimus, automated retrieval)
- Lower per-sqft costs through ownership vs. leasing
> **Takeaway**
>
> Near-term constraint is operational density and scheduling discipline. Long-term margin expansion comes from purpose-built facilities and automation, not from cramming more customers into leased units.
>
---
## Target Customer & Market Entry
### Launch Geography (Phase 1)
**Hudson County, NJ and the surrounding area** — 7 municipalities across 13 serviceable ZIP codes: Hoboken, Jersey City, Weehawken, West New York, Union City, North Bergen, and Edgewater.
### Primary Wedge: Luxury Renters
- Driven by demographics and partnership landscape (leasing offices, amenity positioning)
- High time-cost households who value convenience over price
### Secondary: Homeowners
- Condo owners, townhome owners, high-income households
- Distribution: Condo boards, HOAs, parent networks, private schools, neighborhood associations
> **Investor Framing**
>
> "Renters first because of distribution efficiency; homeowners broaden TAM and strengthen retention and LTV."
>
### Expansion Thesis (Phase 2 — Future Evaluation)
Logistically adjacent, high-rent markets reachable from NJ perimeter storage:
- Manhattan West Side & FiDi
- Long Island (Western Nassau)
- Southern Westchester
- NJ (Fort Lee, Montclair)
---
## Insurance & Protection
### Recommended Coverage Stack
| Coverage | Recommended Limit |
| ------------------------------ | ---------------------------------- |
| General Liability | $1M per occurrence / $2M aggregate |
| Commercial Auto | $1M CSL |
| Hired/Non-Owned Auto | $1M |
| Motor Truck Cargo (in transit) | $100k–$250k per vehicle |
| Warehouse/Bailee's Liability | $250k–$1M+ per location |
| Umbrella/Excess | $1M–$3M |
| Workers' Comp | Statutory (NJ) |
| Cyber / Tech E&O | $250k–$1M |
| Crime / Dishonesty | $25k–$100k |
### Customer Protection Policy
- **Included:** Up to $2,000 standard protection per subscription
- **Optional:** Additional coverage available (transparent, not predatory)
- **Required:** Declared value for high-value items; special handling categories defined
---
## Key Metrics to Track
### Survival Metrics (Rolling 6 Months)
- Subscribers (active count)
- Contribution margin
- Pickup/return cost per stop
- Founder support draw ($15k–$20k target)
- Exception rate
### Defensibility Metrics (12–18 Months)
| Metric | Definition |
| ------------------- | ------------------------------- |
| **CUM** | Containers Under Management |
| **IUM** | Items Under Management |
| **DVUM** | Declared Value Under Management |
| Retrieval frequency | p50/p90 per customer/month |
| Storage utilization | % of leased capacity in use |
| Customer retention | Monthly/annual churn rate |
### Milestones
| Milestone | Target | Validation |
| ---------------------------- | ------------------ | --------------------------------- |
| First 20 customers | — | Validates $299 willingness-to-pay |
| Founder salary + growth | TBD customer count | Self-funded 30–40% YoY growth |
| Contribution margin positive | Per-customer | Unit economics proven |
---
## Key Risks & Mitigations
| Risk | Mitigation |
| --------------------- | ------------------------------------------------------------------------- |
| Flat plan overuse | Policy guardrails + batching + additional subscription for high-volume |
| Loss/damage trust | Documentation, chain-of-custody, clear protection policy, proper coverage |
| Operational scaling | Building density first, SOPs, inventory system, scheduling discipline |
| Brand differentiation | Premium service + inventory layer; avoid commodity "space rental" framing |
### Anti-Patterns (Rejected)
These approaches were evaluated and explicitly rejected:
- Tiered cubic-foot pricing
- On-demand retrieval (no lead time)
- Pre-density ops dashboards
- Treating storage as space rental
---
# Customer Experience
*Last updated: Feb 27, 2026*
---
## Customer Promise
Residents can rely on:
- **White-glove pickup and return** — we handle the logistics
- **Searchable inventory** — find any item instantly via photos, tags, descriptions
- **Predictable scheduling** — 48-hour lead time, bookable weeks/months in advance
- **Clear chain-of-custody** — always know where your items are
> **Never have to...**
>
> Drive to storage or guess item status.
>
---
## Service Model
### What Customers Get
| Feature | Description |
| ---------------------- | ---------------------------------------------------------- |
| **Pickup Service** | We come to you, pick up items with 48-hour notice |
| **Storage** | Climate-controlled facility, documented location |
| **Inventory Portal** | Photos, descriptions, tags — searchable digital catalog |
| **Delivery Service** | Items returned to you with 48-hour notice |
| **Advance Scheduling** | Book returns weeks/months ahead (seasonal, holidays) |
| **Baseline Insurance** | $2,000 included coverage |
| **14-Day Trial** | Complimentary trial to explore portal and create inventory |
### New Customer Flow
1. **Sign up** — Customer subscribes via Stripe Checkout
2. **14-day trial begins** — No charge during trial period
3. **Create inventory** — Customer adds items with photos, descriptions, tags
4. **Schedule first pickup** — Book appointment via Calendly
5. **First billing** — Trial ends, monthly billing begins ($299/month)
### What Customers Create
> **Important**
>
> Customers create their own inventory. Storage Valet is logistics, not cataloging.
>
- Customer adds item names, descriptions, keywords
- Customer uploads photos (1-5 per item)
- Customer tags items for easy search (holiday, fragile, kid1, etc.)
- Search "holiday decorations" → find Christmas ornaments instantly
### Language Guidelines
| Use ✓ | Don't Use ✗ |
| ------------------- | ----------------------- |
| "as needed" | "on-demand" |
| "48-hour lead time" | "instant" or "same-day" |
| "schedule returns" | "request delivery" |
| "planned pickup" | "on-demand retrieval" |
---
## Portal Overview
### Purpose (Non-Negotiable)
**For Customers:**
- Manage inventory (add, edit, delete items)
- View item state (home, stored, scheduled)
- Schedule services (pickups, deliveries)
- Recover items if booking canceled
**For SV Ops:**
- Capture item/service data
- Enable batching and density
- Produce operational metrics
**Excluded:** Real-time tracking, complex pricing dashboards, ops micromanagement UIs
### Customer Routes (4 Only)
| Route | Purpose |
| ------------ | ----------------------------------- |
| `/login` | Magic link authentication |
| `/dashboard` | Item inventory + booking management |
| `/schedule` | Service scheduling flow |
| `/account` | Profile, billing, settings |
### Staff/Admin Routes (Not Customer-Facing)
| Route | Purpose |
| ------------------ | ---------------------------------- |
| `/ops` | Operations dashboard (staff-gated) |
| `/admin/waitlist` | Waitlist management (staff-gated) |
| `/admin/customers` | Customer management (staff-gated) |
---
## Item Lifecycle
### Item States
| State | Meaning | Customer Actions |
| ---------- | ----------------------------------------------------------------------------------------------------------------------- | ------------------------------------ |
| home | Item is at customer's address | Schedule Pickup |
| scheduled | Item is scheduled for pickup or delivery (type determined by booking context: `pickup_item_ids` or `delivery_item_ids`) | Cancel / Revert, Remove from Booking |
| stored | Item is in SV facility | Schedule Delivery |
| in_transit | Item is currently being moved (pickup or delivery in progress) | — |
### State Transitions
```
[Created] → HOME ←──────────────────────────┐
│ │
│ schedule_pickup │ delivery_completed
▼ │
SCHEDULED (pickup) ──────────────────┤
│ cancel/fail │
│ (revert → HOME) │
│ │
│ pickup_completed │
▼ │
STORED ←─────────────────────────┤
│ │
│ schedule_delivery │
▼ │
SCHEDULED (delivery) ────────────────┘
│ cancel/fail
│ (revert → STORED)
Note: DB status is `scheduled` in both cases.
Pickup vs delivery is determined by booking arrays
(pickup_item_ids / delivery_item_ids).
```
> **Critical Rule**
>
> **Scheduled states are NEVER terminal.** Every scheduled item MUST have a customer-visible revert action.
>
---
## Booking Workflow (Schedule-First)
### Overview
Customers book the appointment FIRST (via Calendly), then select items. If items were pre-selected on the Dashboard before booking, they are auto-attached and the manual selection step is skipped.
### Flow
1. **Book Appointment** — Customer picks date/time via Calendly (optionally with items pre-selected on Dashboard)
2. **Calendly Webhook** — Creates `action` record with `pending_items` status
3. **Attach Items** — Either auto-attached (pre-selected) or customer manually selects via "Add Items"
4. **Status Update** — Action moves to `pending_confirmation`
5. **Ops Confirms** — Action confirmed, ready for execution
6. **Service Completed** — Items transition to new state
### Booking Statuses
| Status | Meaning | Customer Action |
| ---------------------- | ----------------------------------------- | ------------------ |
| `pending_items` | Appointment booked, needs item selection | Add Items |
| `pending_confirmation` | Items selected, awaiting ops confirmation | Edit Items, Cancel |
| `confirmed` | Ops confirmed, scheduled for execution | Cancel |
| `in_progress` | Service being executed | — |
| `completed` | Service finished | — |
| `canceled` | Booking canceled | — |
> **Cancellation Behavior**
>
> Canceling a confirmed booking reverts scheduled pickup items back to `home` and scheduled delivery items back to `stored`.
>
> Customers can also remove individual items from pending bookings using "Remove from Booking"; items revert based on original booking type (pickup items → `home`, delivery items → `stored`).
>
> **Auto-Attach: Pre-Selected Items**
>
> When customers select items via checkbox on the Dashboard before opening the booking modal, those items are **automatically attached** to the booking after the Calendly appointment completes. The booking skips the `pending_items` step and moves directly to `pending_confirmation`. Customers who book without pre-selecting items still use the manual "Add Items" flow. Shipped in (Feb 27, 2026).
>
---
## Inventory Features
### Item Creation
Customers create items with:
- **Label** — Item name (required)
- **Description** — Details about the item (required)
- **Photos** — 1-5 photos per item (at least 1 required)
- **Category** — Optional categorization
- **Tags** — Searchable keywords
### Search & Filter
Customers can search by: Label (item name), Description text, Tags/keywords, Status (home, stored, scheduled), Category
### Tag Taxonomy (Recommended)
| Group | Example Tags |
| --------------- | -------------------------------------------------------------- |
| **Category** | clothing, shoes, toys, baby, kitchen, decor, sports, documents |
| **Seasonality** | winter, summer, holiday, back_to_school |
| **Room** | closet, nursery, kitchen, entryway |
| **Owner** | zach, spouse, kid1, kid2, kid3 |
| **Handling** | fragile, heavy, electronics, keep_upright |
| **Use-case** | donate_later, keepsake, hand_me_down |
| **Value** | high_value, declared_value_required |
### Example Item Record
```
Title: "Holiday décor — ornaments + stockings"
Description: "2 ornament boxes, 6 stockings, 1 table runner; deliver after Thanksgiving."
Tags: holiday, decor, fragile, seasonal
Declared value: $500
Notes: "Customer prefers Friday delivery window."
```
---
## Subscription & Billing
### Pricing
- **$299/month** — Single plan, no tiers
- **14-day trial** — Complimentary on all new subscriptions
- **No setup fee** — Eliminated for simplicity
### Billing States
| Status | Meaning | Portal Access |
| ---------- | ---------------------------- | ------------------------- |
| `active` | Subscription current | Full access |
| `past_due` | Payment failed, grace period | Limited (no new bookings) |
| `canceled` | Subscription ended | Read-only inventory |
| `trialing` | Trial period | Full access |
### Billing Management
Customers manage billing via **Stripe Customer Portal** (hosted by Stripe):
- Update payment method
- View invoices
- Cancel subscription
---
## Design Principles for Customer Experience
1. **Customer clarity > internal convenience** — Always show clear item state
2. **Items > bookings** — Items are persistent; bookings are transient
3. **No dead ends** — Every state has a clear next action
4. **Revert paths are mandatory** — Customers can always recover from scheduled states
---
# Operations
*Last updated: Feb 27, 2026*
---
## Operations Reality
> **Core Principle**
>
> Humans execute; software enforces discipline.
>
### Key Constraints
| Constraint | Value | Rationale |
| ------------------- | ----------------- | ------------------------------------------- |
| **Lead Time** | 48 hours | Enables batching, reliability, cost control |
| **Advance Booking** | Weeks/months | Seasonal items, holidays, planned rotations |
| **Service Days** | Building-specific | Batch efficiency |
### What Software Does
- Captures item and service data
- Enforces scheduling rules (lead time)
- Produces operational metrics
- Maintains audit trails
### What Software Does NOT Do
- Real-time location tracking
- Route optimization (future)
- Ops micromanagement dashboards (pre-scale)
---
## Scheduling Discipline
### 48-Hour Lead Time
The 48-hour lead time is a **deliberate policy** that:
1. **Promotes planned usage** — Customers think ahead (seasonal rotations, events)
2. **Improves reliability** — Reduces last-minute failures
3. **Enables batching** — Multiple stops per trip
4. **Reduces cost volatility** — Predictable operations
### Advance Scheduling
Customers can book returns **weeks or months in advance**:
- Holiday decorations scheduled in October for December delivery
- Seasonal clothes rotations planned quarterly
- Event-based items (party supplies) scheduled around dates
### Why NOT On-Demand
| On-Demand Risk | SV Approach |
| ----------------- | -------------------------- |
| Cost volatility | Predictable scheduling |
| Operational chaos | Batched service days |
| Customer anxiety | Planned, reliable delivery |
| Low density | Building-specific routes |
### Service Availability Hours
Published on the live website and enforced via Calendly scheduling. All times Eastern.
| Day | Hours (ET) |
| ------------------- | ------------------ |
| **Monday – Friday** | 8:00 AM – 8:00 PM |
| **Saturday** | 9:00 AM – 9:00 PM |
| **Sunday** | 10:00 AM – 4:00 PM |
### Calendly Scheduling Configuration
Settings applied to both event types (Building Partnership Discussion and Pickup & Delivery Service).
| Setting | Value |
| --------------------- | ---------------------------------- |
| **Minimum Notice** | 2 days (48 hours) |
| **Scheduling Window** | 365 days into the future |
| **Shared Schedule** | Storage Valet Operations (default) |
---
## Operational Workflows
### Pickup Flow
```
1. Customer schedules pickup (Calendly) — 48hr+ notice
(optionally with items pre-selected on Dashboard)
2. Webhook creates action (pending_items)
3. Items attached — auto-attached if pre-selected,
or customer selects manually in portal
4. Action status → pending_confirmation
5. Ops confirms booking
6. Ops executes pickup
7. Items scanned/verified at facility
8. Item status → stored
9. Action status → completed
```
### Delivery Flow
```
1. Customer schedules delivery (Calendly) — 48hr+ notice
(optionally with items pre-selected on Dashboard)
2. Webhook creates action (pending_items)
3. Items attached — auto-attached if pre-selected,
or customer selects stored items manually
4. Action status → pending_confirmation
5. Ops confirms booking
6. Ops retrieves items from storage
7. Ops delivers to customer
8. Customer confirms receipt
9. Item status → home
10. Action status → completed
```
### Cancellation Flow
Customers can cancel bookings in **pending** or **confirmed** status directly from the portal.
```
1. Customer cancels in portal (pending or confirmed)
2. Action status → canceled
3. Item status reverts (based on booking type):
- Pickup items (in pickup_item_ids) → home
- Delivery items (in delivery_item_ids) → stored
4. No penalty (within policy)
```
> **Item Removal**
>
> Customers can also remove individual items from pending bookings without canceling the entire booking. Removed items revert based on booking type (pickup → `home`, delivery → `stored`).
>
### Service Types
| Service | Code | Description |
| ---------------------- | -------------------- | ------------------------------------------------- |
| **Pickup** | `pickup` | Collect items from customer, transport to storage |
| **Redelivery** | `redelivery` | Return stored items to customer |
| **Container Delivery** | `container_delivery` | Deliver empty containers (future) |
---
## Storage Operations
### Facility Model
- **Micro-hub footprint** — Scales via adjacent units / nearby sites
- **Climate-controlled** — Protects customer items
- **Mapped locations** — Every container has documented position
### Container Management
- **Standard containers** — Predictable capacity
- **QR codes** — Every item labeled: `SV-YYYY-NNNNNN`
- **Scanning workflow** — Chain-of-custody documentation
### Scaling Pattern
```
Phase 1: Single unit (e.g., 280 sqft)
Phase 2: Adjacent units (840 sqft, same location)
Phase 3: Nearby micro-hub (new location, same market)
Phase 4: New market micro-hub
```
---
## Metrics
### Survival Metrics (Rolling 6 Months)
| Metric | Target | Notes |
| ----------------------- | --------------------- | --------------------- |
| **Active Subscribers** | Track growth | Core revenue driver |
| **Contribution Margin** | $200–$250/customer/mo | Unit economics health |
| **Pickup/Return Cost** | Minimize | Density dependent |
| **Founder Support** | $15k–$20k/mo | Sustainability draw |
| **Exception Rate** | Minimize | Service quality |
### Defensibility Metrics (12–18 Months)
| Metric | Definition |
| ----------------------- | ------------------------------------------ |
| **CUM** | Containers Under Management |
| **IUM** | Items Under Management |
| **DVUM** | Declared Value Under Management |
| **Retrieval Frequency** | p50/p90 pickups/returns per customer/month |
| **Storage Utilization** | % of leased capacity in use |
| **Customer Retention** | Monthly/annual churn rate |
---
## Go-to-Market Operations
### Primary Wedge: Luxury Rentals
- **Amenity framing** — Storage Valet as building amenity
- **Leasing office alignment** — Partnership with property management
- **Move-in kits** — Onboarding new residents
- **Seasonal campaigns** — Holiday, back-to-school promotions
- **Building service days** — Batch efficiency per building
- **Referral loops** — Word-of-mouth in dense communities
### Secondary: Homeowners
- **Condo/HOA partnerships** — Building-level deals
- **Parent networks** — School and activity group outreach
- **Neighborhood associations** — Community positioning
- **Seasonal rotation positioning** — Strollers, skis, holiday décor, baby gear
### Partnership Model
> **Zero-Footprint Amenity**
>
> Lightweight in-building activations, always-on asset placement (flyers, digital signage). Storage lockers are utilities; SV reclaims space while improving resident experience.
>
---
## Operational Risks & Mitigations
| Risk | Mitigation |
| ----------------------- | ------------------------------------------------------------------------- |
| **Flat plan overuse** | Policy guardrails + batching + additional subscription for high-volume |
| **Loss/damage trust** | Documentation, chain-of-custody, clear protection policy, proper coverage |
| **Operational scaling** | Building density first, SOPs, inventory system, scheduling discipline |
| **Last-minute changes** | 48-hour policy, clear cancellation rules |
| **Route inefficiency** | Building-specific service days, batching |
### Current Ops Tooling
**What Exists:**
- Customer Portal — Item and booking management
- Ops Dashboard — Staff-only view of all actions with customer details (gated by `sv.is_staff()`, uses `get_ops_actions` RPC)
- Orphan Guardrail — `fn_revert_orphaned_scheduled` runs on every dashboard load, automatically reverting items stuck in scheduled state with no active booking
- Calendly — Appointment scheduling
- Stripe — Payment processing
- Supabase — Database and auth
**What Does NOT Exist (Pre-Scale):**
- Ops item manifests
- Automated reconciliation
- Route optimization
---
# Finance
*Last updated: Feb 10, 2026*
---
## Pricing Model
Monthly Subscription$299/monthPricing TiersSingle tier (no complexity)Trial Period14-day complimentary trialSetup FeeEliminated (was $99, removed to reduce friction)
### Pricing Philosophy
- **Single tier simplicity:** One price, one service level. No decision fatigue for customers.
- **No upfront charge:** The 2-week trial and $0 setup fee remove all friction from onboarding.
- **Premium positioning:** $299/month reflects concierge-level pickup, storage, and delivery — not self-storage.
---
## Stripe Integration
ModeLIVEProduct$299/month subscriptionWebhook Versionv4.1 (async verification)Trial2-week complimentary (no upfront charge)
### Billing Flow
1. Customer signs up via portal (portal.mystoragevalet.com)
2. Stripe Checkout creates a subscription with a 14-day trial
3. Webhook confirms subscription status back to Supabase
4. After trial, $299/month auto-charges
### Known Issues
- **Resolved (Feb 10, 2026):** Payment timestamps were NULL for all customer profiles. Fixed by backfill migration `20251219000001` + stripe-webhook v4.1. Verified: 0 NULL `last_payment_at` values remain.
- Legacy schema fields (`setup_fee_paid`, `setup_fee_amount`) exist but are unused since setup fee elimination
---
## Tax Strategy
Tax strategy documentation covers equipment deductions, home office considerations, and vehicle/mileage tracking for the storage logistics operation. Detailed strategy is maintained in Documents (iCloud-synced) under `Finance/Tax/`.
### Key Considerations
- Equipment and storage supplies as business deductions
- Vehicle/mileage tracking for pickup and delivery operations
- Software subscriptions (Supabase, Vercel, Stripe, Calendly, etc.)
- Home office deduction (if applicable)
---
## Financial Model
The canonical financial model lives in Google Drive as a Google Sheets-compatible spreadsheet. It covers 24-month projections with unit economics for the Hudson County launch market.
### Key Metrics
Revenue per Customer$299/monthLaunch MarketHudson County, NJ (13 ZIPs)Target: Beta3-5 customers
---
## Accounting & Invoicing
Financial records are organized in Documents (iCloud-synced) across three top-level folders:
- **`Finance/`** — All monetary records:
- `Accounting/` — General accounting documents
- `Receipts/` — Purchase receipts
- `Statements/` — Bank and card statements
- `Invoices/` — Vendor invoices
- `Tax/` — Tax strategy and filings
- `Stripe/` — Stripe-specific billing documentation
- **`Legal/`** — Entity filings, payment structure options, EIN documentation
- **`Insurance/`** — Business and personal insurance policies, warranties
---
# Legal
*Last updated: Feb 27, 2026*
---
## Entity Structure
Legal EntityStorage Valet LLCState of FormationDelawareEIN33-4260757Operating StateNew Jersey
---
## Compliance Roadmap
16-item checklist covering federal, state (Delaware + New Jersey), and other business compliance requirements. Originally compiled May 2025.
### Federal
| Task | Details | Status |
| --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | ------- |
| **Obtain EIN** | Employer Identification Number via IRS Form SS-4. Separates federal tax account from SSN. | Done |
| **BOI e-Filing (FinCEN)** | Beneficial Ownership Information report. Filed within 30 days of formation (3/18/25). | Done |
| **IRS Federal Tax Returns** | File annually by April 15: Form 1065 for partnerships/disregarded; 1120 or 1120-S if electing corp status. Even $0 income triggers a return. | Pending |
### Delaware
| Task | Details | Status |
| ---------------------------------- | --------------------------------------------------------------------------------------- | ------- |
| **Certificate of Formation** | Filed 03/18/25 with Delaware SOS. Must maintain good standing via annual taxes. | Done |
| **Annual Franchise Tax** | $300 flat fee due June 1 each year. Non-payment = $200 penalty + 1.5% monthly interest. | Pending |
| **Maintain Registered Agent (DE)** | Delaware-based agent year-round to receive legal service of process. | Done |
> **Delaware LLC Note**
>
> Delaware LLCs do not file an annual report. Only the $300 flat franchise tax applies.
>
### New Jersey (Foreign LLC)
| Task | Details | Status |
| ------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | ------- |
| **Certificate of Authority** | File online via NJ Division of Revenue (Form C113), $125 fee. Requires Delaware Good-Standing Certificate dated within 30 days. | Pending |
| **Business Registration (NJ-REG)** | Register for NJ tax, withholding, and unemployment accounts. No fee. | Pending |
| **Business Registration Certificate** | After NJ-REG, request BRC for contracting & licensing. Free, auto-issued online. | Pending |
| **Annual Report & Fee (NJ)** | File annual report by anniversary month via NJ portal. $75 fee. | Pending |
| **Maintain Registered Agent (NJ)** | Appoint & retain NJ-resident or commercial agent. Update records if changed. | Pending |
### Other
| Task | Details | Status |
| ---------------------------- | --------------------------------------------------------------------------------------------- | ------- |
| **Operating Agreement** | Adopted and current. Reflects member roles, voting, capital contributions. | Done |
| **Business Bank Account** | Dedicated business bank account with bank resolution authorizing signers. | Done |
| **Local Permits & Licenses** | Check Hoboken/Hudson County for pickup/delivery or storage permits (haulage, waste handling). | Pending |
| **Insurance & Bonding** | Commercial general liability & inland marine (storage-in-transit) insurance. See . | Pending |
> **Progress Summary**
>
> **7 of 16 items complete.** Remaining 9 items are primarily NJ foreign LLC registration (5 items), federal tax returns, Delaware franchise tax, local permits, and insurance.
>
---
## Customer-Facing Legal Documents
The and are published on the main website. Key provisions documented here for internal reference. Last substantive review: Feb 2026.
### Terms of Service — Key Provisions
| Provision | Value | Notes |
| ------------------------------ | ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Declared Value Threshold** | $2,000 | Items exceeding $2,000 require prior written approval. Aligned with included insurance coverage. |
| **Post-Termination Retrieval** | 45 days | Customer must retrieve items within 45 days of membership termination. Matches Clutter industry standard. NJ lien law (N.J.S.A. 2A:44-187) requires ~75-90 days minimum statutory process before disposal. |
| **Insurance Claim Window** | 30 days from discovery | Standard across traditional and valet storage. Must include documentation of item value. |
| **Missed Appointment Fee** | $50 (may apply) | Discretionary. "May apply" language gives enforcement flexibility. Not routinely enforced. |
| **Delivery Lead Time** | 48-hour minimum | Booking notice requirement for all pickups and deliveries. |
### Privacy Policy — Key Provisions
| Provision | Value | Notes |
| -------------------------- | --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **General Data Retention** | 7 years | Covers NJ 6-year statute of limitations (contract, fraud, property) plus 1-year buffer. Also satisfies IRS record-keeping requirements. |
| **Photo Deletion** | Purpose-based (reasonable period) | Photos deleted within reasonable period after final retrieval or cancellation, unless retention required for active insurance claim, legal dispute, or regulatory obligation. No hard calendar deadline — avoids premature evidence destruction. |
| **Privacy Contact** | privacy@mystoragevalet.com | Active alias routing to primary inbox. Used for data access, correction, and deletion requests. |
> **Additional Insurance Coverage**
>
> ToS references availability of additional coverage for high-value items. Currently handled manually (direct customer contact). Automated upsell planned for portal when customers approach $2,000 inventory threshold.
>
---
# Insurance
*Last updated: Feb 14, 2026*
---
## Customer Coverage
Included Coverage$2,000 per customer
Every Storage Valet subscription includes $2,000 of coverage for stored items. This is a key differentiator from traditional self-storage and a trust signal for premium customers.
---
## Business Insurance Requirements
Cost estimates and coverage requirements for operating a pickup-storage-delivery business in New Jersey. Based on May 2025 research.
> **Current State**
>
> An initial general liability quote ($42/month from Liberty Mutual via Bolt.tech) was evaluated and found **insufficient** — it lacks bailee's coverage, commercial auto, and customer property protection. A comprehensive package is needed before scaling operations.
>
### Startup Phase ($4,000-$8,000/year)
| Coverage Type | What It Covers | Recommended Limit |
| ------------------------------- | ----------------------------------------------------------------------------- | ---------------------------------- |
| **General Liability** | Third-party bodily injury, property damage, advertising injury | $1M per occurrence / $2M aggregate |
| **Bailee's Customer Insurance** | Customer property while in our possession or storage (critical for our model) | $1M coverage |
| **Commercial Auto** | Vehicles used for pickups and deliveries | Endorsement or limited policy |
| **Business Personal Property** | Our own equipment, office contents, storage facility contents | $100,000 coverage |
### Growth Phase ($10,000-$20,000/year)
Additional coverage needed when hiring employees or scaling operations:
| Coverage Type | What It Covers | Notes |
| ----------------------------- | ---------------------------------------------------- | ------------------------------ |
| **Workers' Compensation** | Employee injuries on the job | Required by NJ law once hiring |
| **Cyber Liability** | Data breaches, customer data exposure | $1M recommended |
| **Increased Bailee's Limits** | Higher coverage for growing inventory volume | $2-5M coverage |
| **Business Interruption** | Income replacement if operations disrupted | Based on revenue |
| **Umbrella Policy** | Additional liability protection above primary limits | $1-2M recommended |
> **Bailee's Insurance Detail**
>
> **This is the most critical coverage for Storage Valet's model.** Bailee's insurance (also called Warehouseman's Legal Liability) specifically covers customer property while in our possession. Cost range: $2,500-$15,000/year depending on operations size and coverage limits ($100K to $5M).
>
### Commercial Auto Alternatives
For the startup phase when using personal/family vehicles or renting:
- **Hired and Non-Owned Auto (HNOA):** $500-$1,000/year — covers liability when using rented or personal vehicles for business
- **Usage-based insurance:** For infrequent pickups/deliveries
- **Full commercial auto:** $1,500-$3,000 per vehicle/year — needed when owning a dedicated van
---
## Recommended Providers
### Specialized Storage Industry Insurers
- **MiniCo Insurance Agency** — Self-storage specialists, understand bailee's coverage
- **The Hartford** — Storage business packages
- **Nationwide** — Warehouse/storage tailored coverage
### New Jersey Commercial Insurance Brokers
- **HUB International** — NJ offices
- **Marsh McLennan Agency** — NJ offices
- **Otterstedt Insurance Agency** — Hoboken/Bergen County (local)
> **Next Steps**
>
> Contact specialized storage industry insurers for comprehensive quotes. Work with an experienced commercial broker familiar with storage industry risks. Budget $4,000-$8,000/year for essential coverage in the startup phase.
>
---
# Strategic Partnerships
*Last updated: Mar 17, 2026*
---
## Value Proposition
> **The Zero-Footprint Amenity**
>
> Storage Valet offers property managers a premium amenity that enhances resident satisfaction without capital investment, staff burden, or on-site storage requirements.
>
### For Buildings
- **No capital investment required** — Zero upfront costs
- **No additional staff burden** — We handle all logistics, billing, and resident communication
- **No on-site square footage needed** — Reclaim basement cages for revenue-generating amenities
- **Enhances leasing packages** — Amenity differentiator for prospective residents
- **Reduces storage-related complaints** — Overflow solution for waitlisted storage units
- **Potential referral revenue** — Commission programs available
### For Residents
- **White-glove pickup and delivery** — To their door, on their schedule
- **Visual inventory via app** — Search and browse stored items with photos
- **Climate-controlled storage** — Protection for valuables and seasonal items
- **$2,000 included coverage** — Insurance protection at no extra cost
- **No self-storage hassle** — No driving, no loading, no facility access codes
### Measurable Impact
| Metric | Expected Impact |
| ---------------------------- | --------------------------- |
| Resident satisfaction scores | 15-20% increase |
| Rent premium justification | $200-400 additional monthly |
| Storage-related complaints | 25% reduction |
---
## Partnership Models
| Model | Description | Building Benefit |
| ----------------------- | --------------------------------------- | ------------------------------------------- |
| **Exclusive Discounts** | Residents get preferred pricing | Amenity differentiator, no cost to building |
| **Referral Incentives** | Commission for staff/agent referrals | Revenue share opportunity |
| **Co-Branded Events** | Storage Drives, seasonal campaigns | Resident engagement, community building |
| **White-Label Service** | Branded as building's exclusive amenity | Premium positioning, can include in rent |
### Co-Branded Partnership
**Best for:** Buildings seeking a turnkey amenity
- Shared branding with co-branded materials and resident communications
- Building gains credit for offering the service at no cost
- Deploys within one week
### White-Label Service
**Best for:** Luxury properties wanting exclusive positioning
- Storage appears as building's exclusive amenity
- Can be included in rent or offered as premium add-on
- Storage Valet operates invisibly behind the scenes
---
## Promo Landing Pages
> **Primary Outreach Tool**
>
> Every target property has a dedicated landing page at `mystoragevalet.com/promo/{slug}` with an exclusive resident discount, share tools, and QR code. These pages are the primary outbound tool for partnership sales — replacing the need for PDFs, custom emails, or verbal pitches.
>
### What Each Page Includes
- **Property-specific headline** — e.g., “Haus25 Residents”
- **Exclusive discount offer** — $100 off for 4 months ($199/mo instead of $299/mo)
- **Unique promo code** — Per-property Stripe promotion code for attribution (e.g., `HAUS25`)
- **Share toolkit** — Pre-written newsletter snippet (one-click copy), direct link (one-click copy), and print-ready QR code
- **Full offer details** — How-it-works steps, benefit highlights, trust signals (insurance, no setup fee, 14-day trial)
### Why They Work
- **Zero friction for PMs** — No contracts, no integration, no IT involvement. The page exists whether or not the PM has formally agreed to a partnership.
- **Conversation starter** — Share the URL during outreach. The PM can preview it, evaluate the offer, and decide to forward to residents on their own timeline.
- **Built-in distribution** — The share section gives PMs a copy-paste snippet for newsletters, a link for building apps, and a QR code for lobby signage — distribution takes 30 seconds.
- **Per-building attribution** — Each property has its own Stripe promo code. Signups are tracked back to the specific building.
### Current Inventory
**70 properties** have live promo pages. All 70 slugs, property names, and promo codes are listed on the .
URL pattern: `mystoragevalet.com/promo/{slug}` (e.g., `mystoragevalet.com/promo/haus25`)
### Sample Demo Page
**`mystoragevalet.com/promo/sample`** is a demo-only version of the promo page, used during partnership outreach to show property managers what their residents would see. It displays a teal banner (“This is a sample resident offer page”), hides the promo code block (SAMPLE is not a real Stripe code), and routes the CTA to `/partnerships#partnership-contact` instead of checkout. The partnerships page “View a Sample Resident Offer” CTA links here.
### Offer Terms (Locked)
| Parameter | Value |
| --------------- | ---------------------------------- |
| Discount | $100 off per month |
| Duration | First 4 months |
| Resulting price | $199/mo for 4 months, then $299/mo |
| Total savings | $400 (33% off for 4 months) |
| Setup fee | None |
| Trial | 14-day complimentary |
| Insurance | $2,000 included |
| Stripe coupon | `W8K6JWCH` |
**Do not change offer terms without Zach's explicit approval.** These are reflected in the page copy, footer fine print, and Stripe coupon configuration.
### Current Limitation
The “Get Started” CTA links to `mystoragevalet.com` where residents sign up through the standard flow and manually enter the promo code. A future phase will add an embedded signup form with the discount auto-applied. See for technical details.
---
## Outreach Execution Runbook
> **Step-by-step process for using promo pages in partnership outreach**
>
> This runbook covers the full lifecycle: preparation, initial contact, PM engagement, resident distribution, and tracking results.
>
### Step 1: Prepare
1. **Identify the property** from the
2. **Locate the URL:**`mystoragevalet.com/promo/{slug}`
3. **Preview the page** yourself to confirm it renders correctly with the right property name
4. **Check CRM status** in HubSpot — verify outreach history and current pipeline stage
### Step 2: Initial Contact (Email, LinkedIn, In-Person)
Share the promo URL as a conversation starter. Example outreach message:
> *Hi [Name], I wanted to share something we put together specifically for [Property Name] residents — a dedicated landing page with an exclusive storage offer:*
>
> ***mystoragevalet.com/promo/[slug]***
>
> *It includes everything residents need to know about the service, plus a unique promo code ([CODE]) for $100 off their first 4 months. The page also has ready-to-use share tools — a copy-paste snippet for your newsletter and a QR code for lobby signage — so distributing to residents takes about 30 seconds.*
>
> *Would you be open to sharing this with your residents?*
>
### Step 3: Key Talking Points
- **Zero cost to the property.** No contracts, no integration, no IT involvement.
- **Zero effort to distribute.** Built-in share section with pre-written message, one-click copy link, and print-ready QR code.
- **Exclusive resident pricing.** $199/mo for 4 months (normally $299/mo). 14-day complimentary trial. No setup fee.
- **Amenity positioning.** Positions the building as offering a premium storage amenity at no cost to the property.
- **Per-building attribution.** Each property has its own promo code — we can track exactly which buildings drive signups.
### Step 4: After the PM Agrees to Share
Suggest these distribution channels:
| Channel | How |
| --------------------------------- | ------------------------------------------------------------------------------------ |
| **Email blast / newsletter** | Use the copy-paste snippet from the “Share with Residents” section on the promo page |
| **Building app** | Share the direct URL — the page is mobile-optimized and self-explanatory |
| **Lobby flyer / elevator screen** | Save or screenshot the QR code from the page — it links directly to the promo page |
| **Move-in packet** | Include the QR code or URL in welcome materials for new residents |
| **Concierge / front desk** | Give staff the URL to share verbally or via text when residents ask about storage |
### Step 5: Track Results
| Metric | Where to Check |
| ----------------------- | --------------------------------------------------------- |
| Page visits by property | Vercel Analytics: filter `promo_page_view` events by slug |
| Page views over time | GA4: Engagement reports for `/promo/{slug}` |
| Share snippet copies | Vercel Analytics: `promo_share_copy` events |
| Link copies | Vercel Analytics: `promo_link_copy` events |
| Signups by building | Stripe: filter subscriptions by promotion code |
### PM Expectations
**Property managers are NOT expected to:**
- Sign a contract or formal agreement
- Install anything or provide API access
- Create accounts or log into any system
- Handle billing, support, or logistics
**Property managers ARE expected to:**
- Review the promo page to understand the offering
- Distribute the link or QR code to residents through their existing channels
- Optionally: provide feedback on the page or offer terms
### Adding a New Property
If a property is not in the current 70-property inventory:
1. **Choose a slug** — lowercase, no spaces, no hyphens, no special characters
2. **Create a Stripe promotion code** on coupon `W8K6JWCH` (confirm LIVE mode with Zach first)
3. **Add to the PROPERTIES object** in `sv-website/promo/index.html`
4. **Commit and push** to main — Vercel auto-deploys within ~60 seconds
5. **Verify** the new page renders correctly
Full technical details:
---
## Storage Drive Events
> **Primary Acquisition Hook**
>
> Storage Drives are community events where residents can donate to charity AND store items with Storage Valet — all in one convenient visit.
>
### How It Works
1. **Setup:** Branded station in building lobby with donation bins, refreshments, iPad signup
2. **Duration:** 4-6 hours (typically Saturday 10am-4pm)
3. **Staffing:** 3 uniformed Storage Valet team members
4. **Cost to Building:** $0 — Storage Valet covers everything
### Why Storage Drives Work
| Benefit | Impact |
| -------------------- | ------------------------------------------------------------- |
| **Lower CAC** | Group events reduce per-customer acquisition cost by 60-70% |
| **Build Trust** | Face-to-face interaction + building endorsement = credibility |
| **Create Density** | Building-by-building approach enables efficient routing |
| **Generate Content** | Photos, testimonials, social proof for marketing |
### Typical Event Results
| Metric | Target |
| ------------------- | -------- |
| Attendees | 40 |
| Conversion rate | 15% |
| New signups | 6 |
| Donations collected | 500+ lbs |
---
## Target Market
### Market Size
- **Service area:** 13 ZIP codes across 7 municipalities in Hudson County, NJ and the surrounding area
- **CRM-tracked properties:** 250 properties, ~68,000 units (verified CRM data for ~79% of properties; conservative estimates for remainder)
- **Units under construction:** 12,100+ in Jersey City alone; 2,412 new apartments delivered in 2024
- **Vacancy rate:** 2.8% despite construction surge; 14:1 prospective renters per vacant unit
- **Average household income:** $150,000+ in target buildings
### Priority Building Characteristics
1. **50+ units** — Economies of scale for events
2. **Limited or no resident storage** — Pain point creates demand
3. **Concierge presence** — Partnership execution point
4. **Luxury positioning** — Residents can afford $299/month
### Key Management Companies
Top management groups from the 250-property CRM, organized by portfolio size in the service area.
| Company | CRM Properties | Notable Buildings |
| --------------------- | -------------- | ----------------------------------------------------------------------------- |
| **Ironstate** | 19 | 70/90 Columbus, 333 River Street, The Shipyard, Hoboken Urby |
| **Newport/LeFrak** | 16 | The Bisby, Ellipse, East Hampton, Parkside (all contact-form only) |
| **Veris Residential** | 15 | Sable (762 units), Haus25, Liberty Towers, BLVD Collection, RiverHouse 9 & 11 |
| **Greystar** | 12 | The Greyson (622 units), Hamilton Cove, 7 Seventy House, VINE, 1000 Jefferson |
| **Bozzuto** | 11 | Hudson House Lofts, The Declan, Harlow, Park + Garden, Vantage |
| **Willow Bridge** | 4 | The Jordan, Artisan Series, 1125 Jefferson, Mariners Landing (warm pipeline) |
---
## Development Pipeline
New construction represents opportunity for early partnerships before residents move in.
### Active Lease-Up (Pitch Now)
- **One Journal Square Tower 1** (Kushner) — 1,723 units, leasing now
- **Journal Squared Tower 3** (KRE) — Completed Spring 2025
- **The Reserve at Estuary** (Hartz) — 218 units
### Under Construction (Pre-Leasing Targets)
- **505 Summit** (Spitzer) — 605 units, Spring 2026
- **Imperial Tower** — 542 units, Kennedy Blvd
- **One Journal Square Tower 2** — Rising now
### Planning Phase (Design Influence)
- **Hoboken North End** (Rockefeller) — 729 units, approved Dec 2025
- **615 River Road Edgewater** (Maxal) — 381 units Phase 1, late 2027
- **Artwalk Towers** (KRE) — 49 stories, $175M loan secured
> **Developer Pitch**
>
> "Don't build basement cages. Partner with Storage Valet and reclaim that square footage for revenue-generating amenities."
>
---
## Objection Handling
> **Sales Playbook**
>
> Common objections from property managers and leasing teams, with data-backed responses. See for the full benchmark analysis.
>
### "The locker downstairs is only $185."
**Response:** "I totally get that $185 sounds lower on paper. But that's for a 3x4 cage that fits about half of what we handle. Plus, the resident does all the heavy lifting themselves. When you factor in that we provide 200 cubic feet (vs. 96) and we handle the pickups, deliveries, and insurance, our cost per cubic foot is actually about 30% cheaper than the building. Your residents get double the space and concierge service, rather than just a wire cage."
### "Our residents already have storage."
**Response:** "That's actually our ideal scenario. Buildings with storage typically have waitlists or residents who've outgrown their lockers. Storage Valet handles the overflow — seasonal items, sporting gear, baby equipment — without the building needing to allocate more basement space. It's a complement, not a replacement."
### "What does this cost the building?"
**Response:** "Zero. No capital investment, no staff burden, no on-site space required. We handle all logistics, billing, and resident communication. The building gets a premium amenity for free."
### Messaging Guidelines
- **Never say "cheaper"** — Say "better value" (more space + services included)
- **Never say "storage unit"** — Say "valet service" or "closet extension"
- **Always specify** — $2,000 insurance coverage, 200 cu ft capacity, 48-hour delivery
- **Lead with the building benefit** — Zero cost, zero footprint, premium amenity
---
## Growth Strategy
### Partnership-Led Expansion
If partnership sales performs as planned, Storage Valet may launch new service areas based on growth from individual partnerships with major property management companies.
### Expansion Triggers
- **Multi-property deals** — Single relationship with Greystar, Bozzuto, or Ironstate could open 5-10+ buildings
- **Geographic expansion** — Follow management companies beyond current 7-municipality service area into Bergen County, Essex County, and NYC boroughs
- **Developer relationships** — Partner early with new construction for move-in day activation
### Current Status
- **HubSpot CRM live** with 225 prospective partners, 20 management companies, pipeline stages, custom properties, and Gmail integration (see )
- CRM tracking **250 properties (~68,000 units)** across Hudson County, fully enriched with outreach statuses
- Google My Maps sales tool built with 10 color-coded layers organizing all 250 properties by management group
- **5 outreach waves completed** (4 initial waves + 1 follow-up campaign on Feb 27): 195 total emails sent (119 initial + 76 follow-ups), reaching 142 unique properties (56.8% of market)
- 2 warm pipeline relationships active: Willow Bridge (4 properties, 508 units) and Grace Settembre / Westside Realty Group (The Devan, 336 units)
- Phone outreach list compiled for 58 properties with no email on file, prioritized by unit count
- Outreach generating consumer-side interest: 90 unique devices visited from emails, 3 visitor signups attributed to partnership outreach
- Storage Drive event playbook complete
- Partnership materials and investor-facing outreach report ready
- **70 property-specific promo landing pages live** at `mystoragevalet.com/promo/{slug}` — each with exclusive $100/mo × 4-month discount, per-building Stripe promo code, and built-in PM share tools (copy-paste snippet, QR code)
**Learn more:**
---
# Promo Codes
*Last updated: Mar 17, 2026*
---
## Revenue Tiers (6-Month)
Quick reference for revenue impact by discount level:
| 6-Mo Revenue | Discount Type | Example Codes |
| ------------ | -------------------------------- | ------------------------------------------ |
| **$1,794** | Full price (no promo) | — |
| **$1,744** | One-time $50 off | SAVE50 |
| **$1,644** | $50/mo × 3 months | 50OFF3, ZB50, MB50, BUNNYHIVE |
| **$1,569** | $75/mo × 3 months | BUNNYHIVEVIP |
| **$1,494** | Multiple paths | 100OFF3, 150OFF2, TAKE50, ZACHVIP, MARYVIP |
| **$1,396** | $100/mo × 4 months (partnership) | 70 property codes (HAUS25, BEACON, etc.) |
---
## Setup Fee Codes — Legacy / Do Not Use
> **Legacy Codes**
>
> Setup fee has been eliminated. **Do not issue setup-fee promo codes going forward.** Only honor them if an old link is already circulating. These codes remain active in Stripe but are not part of the current onboarding flow.
>
*Usage counts may be stale; check Stripe for current redemption totals.*
| Code | Discount | Duration | 6-Mo Rev | 12-Mo Rev | Notes |
| ------------- | -------- | -------- | -------- | --------- | ----------------------------- |
| `SETUPFREE` | 100% off | Once | $1,794 | $3,588 | Waives $99 setup fee; 23 uses |
| `WAIVEDSETUP` | 100% off | Once | $1,794 | $3,588 | Legacy code; 0 uses |
---
## Monthly Discount Codes — General
*Usage counts may be stale; check Stripe for current redemption totals.*
| Code | Discount | Duration | 6-Mo Rev | 12-Mo Rev | Notes |
| ------------ | -------- | --------- | -------- | --------- | ------------------------- |
| `SAVE50` | $50 off | 1 month | $1,744 | $3,538 | One-time discount; 3 uses |
| `50OFF3` | $50/mo | 3 months | $1,644 | $3,438 | General use |
| `100OFF3` | $100/mo | 3 months | $1,494 | $3,288 | General use |
| `150OFF2` | $150/mo | 2 months | $1,494 | $3,288 | Aggressive hook |
| `TAKE50` | $50/mo | 6 months | $1,494 | $3,288 | General use |
| `50OFF6` | $50/mo | 6 months | $1,494 | $3,288 | Alias for TAKE50 |
| `Mommies50` | $50/mo | 12 months | $1,494 | $2,988 | Hoboken Mommies; 6 uses |
| `MOMMIES-50` | $50/mo | 12 months | $1,494 | $2,988 | Alias for Mommies50 |
---
## Partner / Referral Codes
| Code | Discount | Duration | 6-Mo Rev | 12-Mo Rev | Notes |
| -------------- | -------- | -------- | -------- | --------- | --------------------- |
| `BUNNYHIVE` | $50/mo | 3 months | $1,644 | $3,438 | BunnyHive partnership |
| `BUNNYHIVEVIP` | $75/mo | 3 months | $1,569 | $3,363 | BunnyHive VIP |
| `THEJORDAN` | $50/mo | 6 months | $1,494 | $3,288 | Jordan referral |
---
## Partnership Property Codes (70 Properties)
> **Promo Landing Page Codes**
>
> These 70 codes are tied to property-specific promo landing pages at `mystoragevalet.com/promo/{slug}`. Each code is a Stripe promotion code on coupon `W8K6JWCH` (“Partnership Launch — $100 Off × 4 Months”). See for the outreach strategy.
>
| Slug | Property Name | Promo Code |
| ----------------- | ---------------------------- | ----------------- |
| `jefferson1000` | 1000 Jefferson | `JEFFERSON1000` |
| `seventy7` | 7 Seventy House | `SEVENTY7` |
| `provost10` | 10 Provost | `PROVOST10` |
| `cmpndcottage` | 26 Cottage | `CMPNDCOTTAGE` |
| `fiftyseven11` | 57 Eleven | `FIFTYSEVEN11` |
| `newark245` | 245 Newark | `NEWARK245` |
| `riverroad615` | 615 River Road | `RIVERROAD615` |
| `avenue1800` | 1800 Avenue | `AVENUE1800` |
| `harbor1500` | Harbor 1500 | `HARBOR1500` |
| `jefferson1125` | 1125 Jefferson | `JEFFERSON1125` |
| `1404willow` | 1404 Willow | `1404WILLOW` |
| `1417adams` | 1417 Adams | `1417ADAMS` |
| `alexander` | The Alexander | `ALEXANDER` |
| `artisan` | Artisan Series | `ARTISAN` |
| `avalon` | Avalon Cove | `AVALON` |
| `beacon` | The Beacon | `BEACON` |
| `bela` | BELA and The Bedford | `BELA` |
| `bexley` | The Bexley | `BEXLEY` |
| `blvd` | The BLVD Collection | `BLVD` |
| `capstone` | The Capstone | `CAPSTONE` |
| `castiron` | Cast Iron Lofts | `CASTIRON` |
| `cmpnd` | CMPND | `CMPND` |
| `courtyard` | Courtyard at Jefferson | `COURTYARD` |
| `declan` | The Declan | `DECLAN` |
| `devan` | The Devan | `DEVAN` |
| `duchess` | Duchess Apartments | `DUCHESS` |
| `dvora109` | DVORA 109 Columbus | `DVORA109` |
| `dvora` | Hamilton House | `DVORA` |
| `edgelofts` | Edge Lofts | `EDGELOFTS` |
| `elementflats` | Element Flats | `ELEMENTFLATS` |
| `embankment` | Embankment House | `EMBANKMENT` |
| `estuary` | Estuary Apartments | `ESTUARY` |
| `gallery` | Gallery Lofts | `GALLERY` |
| `grandview` | Grandview I | `GRANDVIEW` |
| `greyson` | The Greyson | `GREYSON` |
| `hamiltoncove` | Hamilton Cove | `HAMILTONCOVE` |
| `harlow` | The Harlow | `HARLOW` |
| `haus25` | Haus25 | `HAUS25` |
| `hobokenconnect` | Charlie at Hoboken Connect | `HOBOKENCONNECT` |
| `hobokenpoint` | Hoboken Point | `HOBOKENPOINT` |
| `infinity` | Infinity Edgewater | `INFINITY` |
| `ironstate` | Ironstate Development | `IRONSTATE` |
| `jsquared` | Journal Squared | `JSQUARED` |
| `juliana` | Juliana Apartments | `JULIANA` |
| `jordan` | The Jordan | `JORDAN` |
| `leleo` | Le Leo | `LELEO` |
| `libertyharbor` | Liberty Harbor | `LIBERTYHARBOR` |
| `libertytowers` | Liberty Towers | `LIBERTYTOWERS` |
| `lincolnharbor` | Lincoln Harbor | `LINCOLNHARBOR` |
| `marinerslanding` | Mariners Landing | `MARINERSLANDING` |
| `modera` | Modera Lofts | `MODERA` |
| `moorings` | Moorings | `MOORINGS` |
| `morgan` | The Morgan at Provost Square | `MORGAN` |
| `observerpark` | Observer Park | `OBSERVERPARK` |
| `onenj` | The One NJ | `ONENJ` |
| `overlook` | Overlook Flats | `OVERLOOK` |
| `parkgarden` | Park + Garden | `PARKGARDEN` |
| `riello` | Riello Edgewater | `RIELLO` |
| `riverhouse11` | RiverHouse 11 | `RIVERHOUSE11` |
| `riverhouse9` | RiverHouse 9 | `RIVERHOUSE9` |
| `rivertrace` | RiverTrace | `RIVERTRACE` |
| `rivet` | Rivet | `RIVET` |
| `sable` | Sable | `SABLE` |
| `soholofts` | Soho Lofts | `SOHOLOFTS` |
| `solo` | Solo | `SOLO` |
| `swiftco` | Swift & Co | `SWIFTCO` |
| `thejames` | The James | `THEJAMES` |
| `urby` | Hoboken Urby | `URBY` |
| `vermella` | Vermella | `VERMELLA` |
| `vine` | VINE Apartments | `VINE` |
**All codes:** $100/mo off × 4 months (repeating). Stripe coupon `W8K6JWCH`. 6-month revenue: $1,396. 12-month revenue: $3,188.
**Slug rules:** Lowercase, no spaces, no hyphens, no special characters. Must be unique across all entries.
**Demo page:**`mystoragevalet.com/promo/sample` exists as a demo-only preview for partnership outreach. `SAMPLE` appears in the website’s PROPERTIES object but is **not** a real Stripe promotion code — the code block is hidden and the CTA routes to the partnerships contact form. See .
---
## Founder Personal Codes — Zach
| Code | Discount | Duration | 6-Mo Rev | 12-Mo Rev | Notes |
| --------- | -------- | -------- | -------- | --------- | ------------------- |
| `ZB50` | $50/mo | 3 months | $1,644 | $3,438 | Zach personal share |
| `ZB100` | $100/mo | 3 months | $1,494 | $3,288 | Zach personal share |
| `ZB150` | $150/mo | 2 months | $1,494 | $3,288 | Zach personal share |
| `ZACHVIP` | $75/mo | 4 months | $1,494 | $3,288 | Zach VIP referral |
---
## Founder Personal Codes — Mary
| Code | Discount | Duration | 6-Mo Rev | 12-Mo Rev | Notes |
| --------- | -------- | -------- | -------- | --------- | ------------------- |
| `MB50` | $50/mo | 3 months | $1,644 | $3,438 | Mary personal share |
| `MB100` | $100/mo | 3 months | $1,494 | $3,288 | Mary personal share |
| `MB150` | $150/mo | 2 months | $1,494 | $3,288 | Mary personal share |
| `MARYVIP` | $75/mo | 4 months | $1,494 | $3,288 | Mary VIP referral |
---
## Redemption Tracking
> **Coming Soon**
>
> Future enhancements will include: redemption counts, conversion rates, revenue attribution by code, and campaign performance metrics.
>
Planned metrics for each code:
- **Total redemptions** — How many times the code has been used
- **Active subscribers** — Current customers who used this code
- **Churned subscribers** — Former customers who used this code
- **Revenue generated** — Total and average revenue per redemption
- **Conversion rate** — Checkout starts vs. completions with code
- **Campaign attribution** — Which marketing channel drove usage
---
# Technology
*Last updated: Mar 17, 2026*
---
## Architecture Overview
### Tech Stack
| Layer | Technology | Purpose |
| -------------- | -------------------------------------- | --------------------------- |
| **Frontend** | React + Vite + TypeScript | Customer portal |
| **Backend** | Supabase (PostgreSQL + Edge Functions) | Database, auth, serverless |
| **Hosting** | Vercel | Frontend deployment |
| **Payments** | Stripe | Billing, subscriptions |
| **Scheduling** | Calendly | Appointment booking |
| **CRM** | HubSpot (Free) | Partnership sales pipeline |
| **Auth** | Supabase Auth (Magic Links) | Passwordless authentication |
| **Storage** | Supabase Storage | Item photos |
### Infrastructure
| Service | Details |
| -------------------- | ------------------------------------------ |
| **Supabase Project** | `gmjucacmbrumncfnnhua` |
| **Supabase URL** | `https://gmjucacmbrumncfnnhua.supabase.co` |
| **Region** | us-east-1 |
| **Database** | PostgreSQL 17.6.1 |
| **Vercel Team** | StorageValet |
### Vercel Projects
| Project | URL | Repo |
| -------------- | --------------------------- | ---------- |
| **sv-website** | `www.mystoragevalet.com` | sv-website |
| **sv-portal** | `portal.mystoragevalet.com` | sv-portal |
| **sv-wiki** | `wiki.mystoragevalet.com` | sv-wiki |
### Development Tools
| Category | Tools |
| ------------------ | ----------------------------------------------------------------------------------------------------- |
| **Runtime** | Node.js 24.14.0 LTS (Homebrew `node@24`, native ARM64) — npm 11.11.0 bundled |
| **AI Development** | Claude Code (native installer — Homebrew cask on Mac Studio, `~/.local/bin` on MacBook Air) + plugins |
| **Database** | Supabase CLI, Supabase MCP |
| **Hosting** | Vercel CLI |
| **Payments** | Stripe CLI (shell alias defaults to live mode; use `command stripe` for test mode) |
| **Analytics** | gcloud CLI (Homebrew cask `gcloud-cli`) — Application Default Credentials for GA4 MCP |
| **Python** | pipx (Homebrew formula `pipx`) — isolated Python app runner for GA4 MCP server |
| **Secrets** | 1Password CLI |
| **Workflow** | Raycast (app switching, shortcuts, clipboard history, custom scripts) |
### MCP Servers (Claude Code Integrations)
Claude Code connects to 17 MCP servers, organized by type. These give Claude direct, tool-based access to external services without manual API calls or dashboard navigation.
#### User-Scoped MCPs (local processes, available in all Claude Code sessions)
| Server | Tool Prefix | Purpose | Auth Method |
| -------------------- | -------------------------- | ------------------------------------------------------------------------- | ------------------------------------------------- |
| **Supabase** | `mcp__supabase__*` | Direct database access, schema exploration, SQL execution | OAuth (remote HTTP) |
| **Calendly** | `mcp__calendly__*` | List/cancel events, view invitees, manage scheduling | PAT from 1Password via launcher script |
| **Google Workspace** | `mcp__google-workspace__*` | Google Docs, Sheets, and Drive — read, write, format, search | OAuth tokens in `~/.config/mcp-google-workspace/` |
| **Resend** | `mcp__resend__*` | Send/manage emails, contacts, domains, broadcasts — debug delivery issues | API key from 1Password via launcher script |
| **GA4 Analytics** | `mcp__analytics-mcp__*` | Query website traffic, run reports, realtime visitors — read-only | Application Default Credentials via gcloud |
#### Cloud Connectors (managed by Anthropic, available in Claude Code + Desktop + Web)
| Connector | Tool Prefix | Purpose |
| --------------- | ----------------------------------- | -------------------------------------------- |
| Canva | `mcp__claude_ai_Canva__*` | Design creation and management |
| Excalidraw | `mcp__claude_ai_Excalidraw__*` | Diagramming and whiteboarding |
| Gmail | `mcp__claude_ai_Gmail__*` | Read/draft/search email |
| Google Calendar | `mcp__claude_ai_Google_Calendar__*` | Event management, free time, scheduling |
| HubSpot | `mcp__claude_ai_HubSpot__*` | CRM pipeline management (sales only) |
| Jotform | `mcp__claude_ai_Jotform__*` | Form creation and submission management |
| Stripe | `mcp__claude_ai_Stripe__*` | Customers, subscriptions, invoices, products |
| Vercel | `mcp__claude_ai_Vercel__*` | Deployments, projects, build logs |
| Zapier | `mcp__claude_ai_Zapier__*` | Automation workflows and integrations |
#### Built-in MCPs (bundled with Claude Code)
| Server | Tool Prefix | Purpose |
| ----------------- | ---------------------------------- | ----------------------------------------------------------- |
| Claude in Chrome | `mcp__claude-in-chrome__*` | Browser automation, screenshots, form filling, page reading |
| Context7 | `mcp__plugin_context7_context7__*` | Live API documentation lookup for any library |
| Supabase (plugin) | `mcp__plugin_supabase_supabase__*` | Backup/fallback Supabase connection |
#### MCP Launcher Script Pattern
Three MCP servers use launcher scripts to inject secrets from 1Password at runtime, avoiding plaintext credentials on disk:
```
# Pattern: ~/Documents/storagevalet/Technology/Utility_Scripts/{name}-mcp-launcher.sh
# Each script fetches its secret via op read, then exec's the MCP server process.
# Calendly: calendly-mcp-launcher.sh → fetches PAT → npx calendly-mcp-server
# Resend: resend-mcp-launcher.sh → fetches API key → npx resend-mcp
# GA4: ga4-mcp-launcher.sh → sets ADC path → pipx run analytics-mcp
```
#### GA4 MCP Infrastructure
The GA4 Analytics MCP requires a Google Cloud project for OAuth credentials:
| Item | Value |
| ------------------------ | ------------------------------------------------------- |
| **Google Cloud Project** | `storage-valet-analytics` |
| **OAuth Client** | SV Analytics MCP (Desktop app type) |
| **Client JSON** | `~/.config/gcloud/ga4-oauth-client.json` |
| **ADC Token** | `~/.config/gcloud/application_default_credentials.json` |
| **APIs Enabled** | Analytics Admin API, Analytics Data API |
| **1Password Item** | Google Cloud - GA4 Analytics MCP |
| **Scopes** | `analytics.readonly`, `cloud-platform` (read-only) |
If the ADC token expires, re-run: `CLOUDSDK_PYTHON=/opt/homebrew/bin/python3.13 gcloud auth application-default login --scopes=https://www.googleapis.com/auth/analytics.readonly,https://www.googleapis.com/auth/cloud-platform --client-id-file=~/.config/gcloud/ga4-oauth-client.json`
---
## Repository Structure
### Integrated System (4 repos, tightly coupled)
These repos share a common Supabase backend and must be considered together:
```
~/code/sv-portal # Customer-facing React app
~/code/sv-edge # Supabase Edge Functions (Deno/TypeScript)
~/code/sv-db # Database migrations (SQL)
~/code/sv-docs # System documentation
```
### Standalone Projects
| Repo | Purpose | URL |
| ------------ | ------------------------------------ | ----------------------- |
| `sv-website` | Marketing site, signup flow | www.mystoragevalet.com |
| `sv-wiki` | Internal docs (AI + human reference) | wiki.mystoragevalet.com |
These repos have NO Supabase integration, NO shared code with the portal.
---
## sv-portal (Customer Portal)
### Overview
- **Tech:** Vite + React + TypeScript + Tailwind CSS
- **Auth:** Supabase magic links (email-based, no passwords)
- **State:** React Query for server state
- **Customer routing:** React Router (4 routes only)
### Customer Routes (Non-Negotiable)
| Route | Purpose |
| ------------ | ----------------------------------- |
| `/login` | Magic link authentication |
| `/dashboard` | Item inventory + booking management |
| `/schedule` | Service scheduling flow |
| `/account` | User settings |
### Staff/Admin Routes (Not Customer-Facing)
| Route | Purpose |
| ------------------ | ---------------------------------- |
| `/ops` | Operations dashboard (staff-gated) |
| `/admin/waitlist` | Waitlist management (staff-gated) |
| `/admin/customers` | Customer management (staff-gated) |
### Key Components
```
src/
├── components/
│ ├── ProtectedRoute.tsx # Auth guard (all protected routes)
│ ├── ItemCard.tsx # Individual item display
│ ├── BookingsList.tsx # Booking management
│ ├── AddItemModal.tsx # Item creation
│ └── EditItemModal.tsx # Item editing
├── pages/
│ ├── Login.tsx # Auth page
│ ├── Dashboard.tsx # Main inventory view
│ ├── Schedule.tsx # Booking flow
│ └── Account.tsx # User settings
└── lib/
└── supabase.ts # Supabase client
```
### Commands
```
cd ~/code/sv-portal
npm run dev # Start development server (localhost:5173)
npm run build # Build for production (tsc + vite build)
npm run typecheck # TypeScript validation (canonical command)
npm run lint # Alias for typecheck (until ESLint is added)
vercel # Preview deploy (Zach promotes to production)
```
---
## sv-edge (Edge Functions)
### Overview
- **Runtime:** Deno (Supabase Edge Functions)
- **Auth:** JWT verification + RLS
- **Deployment:**`supabase functions deploy`
### Functions (16 deployed)
#### Stripe Integration
| Function | Purpose | Trigger |
| ----------------------- | ---------------------------------------- | ------------ |
| `create-checkout-trial` | Stripe checkout with 14-day trial (LIVE) | Landing page |
| `create-checkout` | Stripe checkout (legacy, no trial) | Deprecated |
| `create-portal-session` | Stripe billing portal | Portal |
| `stripe-webhook` | Process Stripe events | Webhook |
#### Calendly Integration
| Function | Purpose | Trigger |
| ------------------ | ------------------------------------ | ------- |
| `calendly-webhook` | Create bookings from Calendly events | Webhook |
#### Booking Operations
| Function | Purpose | Trigger |
| ---------------------- | ------------------------------------ | -------------- |
| `bookings-list` | List all bookings (ops dashboard) | Ops UI |
| `booking-get` | Get single booking with items | Ops UI |
| `booking-cancel` | Cancel booking, revert item statuses | Portal, Ops UI |
| `complete-service` | Mark service completed | Ops UI |
| `update-booking-items` | Modify items on pending booking | Portal |
#### Customer & Email
| Function | Purpose | Trigger |
| ----------------------- | -------------------------------- | ------------ |
| `admin-create-customer` | Create customer bypassing Stripe | Ops/admin |
| `signup-webhook` | Pre-register, check service area | Landing page |
| `send-email` | Transactional emails via Resend | System |
#### Data Export
| Function | Purpose | Trigger |
| ------------- | ------------------------ | -------------------------------------- |
| `full-export` | Export all customer data | Admin (deployed directly, not in repo) |
#### Utility
| Function | Purpose | Trigger |
| -------------- | ------------------------------------------------------- | ------- |
| `health-check` | Returns status + timestamp (edge function health probe) | System |
### Deployment (CRITICAL)
> **⚠️ ALWAYS use `--no-verify-jwt` flag for webhooks**
>
```
cd ~/code/sv-edge
# Individual function
supabase functions deploy stripe-webhook --no-verify-jwt
supabase functions deploy calendly-webhook --no-verify-jwt
# Or use the deploy script (recommended)
./deploy-and-test.sh
```
### Webhook URLs
| Webhook | URL |
| ------------ | ------------------------------------------------------------------------ |
| **Stripe** | `https://gmjucacmbrumncfnnhua.supabase.co/functions/v1/stripe-webhook` |
| **Calendly** | `https://gmjucacmbrumncfnnhua.supabase.co/functions/v1/calendly-webhook` |
### Incident Log
| Date | Incident | Root Cause | Resolution |
| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Feb 18-19, 2026** | `update-booking-items` and `booking-cancel` returning CORS errors and HTTP 401 from portal. Item attachment and booking cancellation completely blocked for customers. | Two bugs: (1) CORS preflight `Access-Control-Allow-Headers` missing `apikey` and `x-client-info` — browser blocked POST after OPTIONS. (2) Auth called `getUser()` without token argument on a stateless Supabase client, so JWT was never verified (always 401). | Commit `a32c6d3`: added `apikey, x-client-info` to CORS headers; switched to `getUser(token)` pattern (matching `create-portal-session`). Both functions redeployed. Verified via production logs: POST 200 on v44/v16. |
| **Feb 11-15, 2026** | `stripe-webhook` returning HTTP 500 for all event types. 6 unique events failed (34 total retry attempts). 1 subscription stuck as `past_due`. | Two overloaded versions of `update_subscription_status` existed in the database (one accepting `text`, one accepting `subscription_status_enum`). PostgREST could not disambiguate (error `PGRST203`). Created by Nov 25 + Jan 7 migrations using `CREATE OR REPLACE` with different param types (PostgreSQL treats as separate functions). | Migration `20260215000001` drops the `subscription_status_enum` overload, leaving only the `text` version. No edge function changes needed. Failed events replayed from Stripe Dashboard. |
> **Lesson Learned: Function Overloading + PostgREST**
>
> PostgreSQL's `CREATE OR REPLACE FUNCTION` does NOT replace a function when the parameter types differ — it creates a second overload. PostgREST cannot disambiguate overloaded functions with the same parameter names but different types. Always `DROP FUNCTION` the old signature before creating a new one with different parameter types.
>
---
## Promo Page Architecture
> **Property-Specific Promo Landing Pages**
>
> 70 property-specific promo pages are served from a single HTML file at `sv-website/promo/index.html`. No build step, no framework, no external CSS dependencies. See for the business context.
>
### How Routing Works
A Vercel rewrite rule in `sv-website/vercel.json` maps all promo slugs to a single file:
```
{ "source": "/promo/:code", "destination": "/promo" }
```
Vercel's `cleanUrls: true` setting automatically serves `promo/index.html` at the `/promo` path. When the page loads, client-side JavaScript:
1. Reads the URL slug (e.g., `beacon` from `/promo/beacon`)
2. Looks it up in a `PROPERTIES` object embedded in the HTML (70 entries)
3. If found: populates the page with the property name, promo code, share links, and QR code
4. If not found: hides the promo content and shows a “Page Not Found” message with a link back to the main site
### File Structure
| File | Purpose |
| --------------------------------------- | ----------------------------------------------------------------- |
| `sv-website/promo/index.html` | The promo page (single file, all 70 properties — inline CSS + JS) |
| `sv-website/vercel.json` | Rewrite rule: `/promo/:code` → `/promo` |
| `sv-docs/specs/promo-landing-pages.md` | Technical implementation spec (future phases) |
| `sv-docs/guides/promo-landing-pages.md` | Comprehensive operational guide |
### Design Characteristics
- **Brand-consistent:** Uses the same color palette (Charcoal Blue, Berry, Parchment, Stormy Teal), fonts (DM Serif Display + Inter), and design language as the main website and partnerships page
- **Mobile-first responsive:** Tested at 390px (iPhone), 600px (tablet), and desktop widths. Two-column layouts collapse to single-column on mobile.
- **Performance:** All styles inline (no external CSS). GPU-accelerated scroll animations (`translate3d`, `will-change` lifecycle management). SVG noise texture instead of heavy images.
- **Accessibility:**`prefers-reduced-motion` support, `focus-visible` outlines, semantic HTML, sufficient color contrast
- **Animation:**`translate3d(0, 28px, 0)`, `0.8s ease-out`, IntersectionObserver with `threshold: 0.1` and `rootMargin: '0px 0px -40px 0px'` — matches main site patterns
### Analytics Events
Promo pages fire events to both Vercel Web Analytics and GA4:
| Event Name | Trigger | Data |
| ------------------ | ---------------------------------------------------- | -------------------------- |
| `promo_page_view` | Page loads with a valid slug | `slug`, `property`, `code` |
| `promo_share_copy` | User clicks “Copy to clipboard” on the share snippet | `slug` |
| `promo_link_copy` | User clicks the copy icon on the direct link | `slug` |
Events use the `window.va?.('event', ...)` script-tag API pattern (not `.track()`). GA4 captures page views and scroll depth automatically via Enhanced Measurement.
### QR Code Generation
QR codes are generated dynamically via `api.qrserver.com` (external API). Each encodes the full promo URL (e.g., `https://mystoragevalet.com/promo/beacon`). The QR image uses Charcoal Blue (#213C47) for brand consistency. Suitable for lobby flyers, elevator screens, move-in packets, and digital signage.
### Dual-View: Resident vs Property Manager
Promo pages support two view modes controlled by a URL parameter:
| View | URL | Behavior |
| ---------------------- | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| **Resident (default)** | `/promo/beacon` | Clean page with property name, promo code, and signup CTA. Share tools are hidden. |
| **Property Manager** | `/promo/beacon?pm=true` | Adds “Share with Residents” section: copy-paste email snippet, direct link with copy button, and printable QR code for lobby signage. |
When PM mode activates, the `?pm=true` parameter is stripped from the URL bar via `history.replaceState` so the clean URL can be shared. PM-originated share links include `?src=pm` for analytics attribution. The toggle is implemented client-side — the `.share-section` element is hidden by default and displayed only when `isPM` is true.
### Slug Convention
- Lowercase only, no spaces, no hyphens, no special characters
- Must be unique across all entries in the `PROPERTIES` object
- Should be recognizable (e.g., `beacon`, `greyson`, `sable`)
### Adding a New Property (Technical Steps)
1. **Create a Stripe promotion code** on coupon `W8K6JWCH` (LIVE mode — confirm with Zach):
```
stripe promotion_codes create \
-d coupon=W8K6JWCH \
-d code=NEWPROPERTY \
-d metadata[property_slug]=newproperty \
-d metadata[property_name]="New Property Name"
```
2. **Add to the PROPERTIES object** in `sv-website/promo/index.html` (alphabetical by slug):
```
"newproperty": { name: "New Property Name", code: "NEWPROPERTY" },
```
3. **Commit and push** to main. Vercel auto-deploys within ~60 seconds.
4. **Verify:** Visit `mystoragevalet.com/promo/newproperty` and confirm property name, promo code, QR code, and share tools render correctly.
### Removing a Property
1. Remove the entry from the `PROPERTIES` object in `promo/index.html`
2. Deactivate the corresponding Stripe promotion code
3. Commit and push — the URL will then show the “Page Not Found” fallback
### Planned Enhancements
Future phases (documented in `sv-docs/specs/promo-landing-pages.md`) will add:
- Embedded signup form with address fields pre-filled from property data
- Promo code auto-application at Stripe Checkout (no manual entry)
- Database attribution (`promo_code` column on `pre_customers` table)
- Additional analytics events: `promo_signup_submit`, `promo_checkout_redirect`, `promo_signup_waitlist`
---
## Integrations
### Stripe
| Item | Value |
| -------------------- | -------------------------------------------------------------------------------------------------------------------- |
| **Mode** | LIVE |
| **Product** | $299/month subscription |
| **Trial** | 14-day complimentary on all new subscriptions |
| **Setup Fee** | None (eliminated) |
| **Webhook Version** | v4.1 |
| **Events Processed** | `checkout.session.completed`, `customer.subscription.updated`, `invoice.payment_succeeded`, `invoice.payment_failed` |
### Calendly
| Item | Value |
| ------------------- | ----------------------------------------- |
| **Events** | `invitee.created`, `invitee.canceled` |
| **Integration** | Webhook only (no outbound API calls) |
| **Signal vs Truth** | Calendly is signal; database is canonical |
### Supabase Storage
| Item | Value |
| ----------------- | --------------------------------- |
| **Bucket** | `item-photos` (private) |
| **Access** | Signed URLs (1-hour expiry) |
| **Max Size** | 20MB per file |
| **Allowed Types** | image/jpeg, image/png, image/webp |
| **Max Photos** | 5 per item |
### Vercel Web Analytics
| Item | Value |
| -------------------- | ---------------------------------------------------------------------------------------------------------------- |
| **Plan** | Web Analytics Plus ($10/month add-on) |
| **Pages** | All 8 sv-website pages (index, partnerships, faq, privacy, terms, testimonials, signup/success, signup/canceled) |
| **Integration** | Script tags: `/_vercel/insights/script.js` (Speed Insights) + `/_vercel/analytics/script.js` (Web Analytics) |
| **Reporting Window** | 24 months (Plus tier) |
#### Custom Events Tracked
| Event Name | Page | Data |
| -------------------------- | ------------ | ----------------------------- |
| `signup_submit` | Landing | - |
| `signup_checkout` | Landing | - |
| `signup_waitlist` | Landing | `city`, `zip` |
| `cta_click` | Both | `text`, `location` |
| `faq_open` | Both | `question` |
| `scroll_depth` | Both | `depth` (25%, 50%, 75%, 100%) |
| `partnership_form_submit` | Partnerships | `property` |
| `partnership_form_success` | Partnerships | `property` |
| `partnership_form_error` | Partnerships | `property`, `error` |
| `promo_page_view` | Promo | `slug`, `property`, `code` |
| `promo_share_copy` | Promo | `slug` |
| `promo_link_copy` | Promo | `slug` |
#### Script-Tag API Pattern (Important)
> **Do NOT use `window.va.track()`**
>
> The `.track()` method only exists in the npm package (`@vercel/analytics`). The script-tag integration sets `window.va` as a **function**, not an object. Using `.track()` throws `TypeError: window.va?.track is not a function`.
>
```
// CORRECT — script-tag API
window.va?.('event', { name: 'signup_submit' });
window.va?.('event', { name: 'cta_click', data: { text: 'Get Started', location: 'hero' } });
// WRONG — npm package API (will throw TypeError)
window.va?.track('signup_submit');
window.va?.track('cta_click', { text: 'Get Started' });
```
### Google Analytics (GA4)
Added Feb 28, 2026. Provides deeper behavioral analytics beyond what Vercel Web Analytics captures — user flows, engagement time, acquisition channels, and conversion events.
| Item | Value |
| ------------------------- | --------------------------------------------------------------------------------- |
| **Account** | My Storage Valet LLC |
| **Property** | mystoragevalet.com |
| **Measurement ID** | `G-9SBYJ8QEC1` |
| **Stream ID** | 13680726133 |
| **Stream Name** | Storage Valet Website |
| **Cost** | Free (included with Google account) |
| **Pages** | All 8 sv-website pages (same as Vercel Analytics) |
| **Integration** | gtag.js snippet placed before Vercel scripts in each HTML file |
| **Dashboard** | (sign in with zach@mystoragevalet.com) |
| **Timezone** | Eastern (US) |
| **Industry** | Business & Industrial |
| **Data Collection Start** | Feb 28, 2026 (no retroactive data — only captures from snippet deployment onward) |
#### Enhanced Measurement (Auto-Tracked)
These events are captured automatically by GA4 with no additional code:
| Event | What It Captures |
| --------------- | ----------------------------------------------------------------------------- |
| Page views | Every page load and client-side navigation |
| Scrolls | When user scrolls past 90% of page |
| Outbound clicks | Clicks to external domains (e.g., portal.mystoragevalet.com, Stripe checkout) |
| Site search | Query parameters in URL (not currently used) |
| File downloads | Clicks on document/file links |
#### Vercel Analytics vs GA4 — When to Use Which
| Question | Use |
| -------------------------------------------------------- | -------------------------------- |
| How many visitors this week? | Either (Vercel is simpler) |
| Which pages get the most views? | Either |
| Where is traffic coming from? (referrers, UTM campaigns) | GA4 (more detail) |
| How long do visitors spend on the page? | GA4 |
| What % of visitors scroll to the signup form? | GA4 (scroll events) |
| Are partnership emails driving traffic? | GA4 (UTM breakdown) |
| Custom event tracking (signup_submit, cta_click)? | Vercel Analytics (already wired) |
| Core Web Vitals / page speed? | Vercel Speed Insights |
#### Still To Build Out
- **GA4 conversion events:** Mark key actions (e.g., signup form submission, Stripe checkout redirect) as conversions in GA4 for funnel tracking
- **Google Ads linkage:** When Google Ads is set up, link to this GA4 property for ad performance tracking
- **Google Business Profile:** Not yet created — needed for local search visibility, Google Maps listing, and review collection
- **Meta (Facebook) Pixel:** Not yet installed — needed if running Facebook/Instagram ads
### HubSpot CRM
Added Mar 2026. Partnership sales pipeline management — tracks prospective property partners from research through signed partnership. Separate from product infrastructure (Supabase, Stripe, Calendly handle customer-facing operations).
| Item | Value |
| ---------------------- | ---------------------------------------------------------- |
| **Account ID** | `245438948` |
| **Plan** | Free (Sales Hub) |
| **Pipeline** | “Property Partnerships” (internal name: `default`) |
| **Records** | 225 deals (prospective partners), 20 companies, 6 contacts |
| **Chrome Extension** | HubSpot Sales for Gmail (installed Mar 8, 2026) |
| **Email Integration** | Gmail — emails sent from Gmail, tracked/logged by HubSpot |
| **Claude Code Access** | HubSpot MCP tools (`mcp__claude_ai_HubSpot__*`) |
#### Pipeline Stages
| Stage | Internal ID | Probability |
| ------------------ | ------------ | ----------- |
| Research | `3313102548` | 10% |
| Outreach Sent | `3313102549` | 20% |
| Follow-up | `3313102550` | 30% |
| Meeting Scheduled | `3313102551` | 50% |
| Negotiation | `3313102552` | 75% |
| Signed Partnership | `closedwon` | 100% |
| Not Interested | `closedlost` | 0% |
#### Custom Deal Properties
`property_address`, `unit_count`, `property_type`, `city`, `outreach_status`, `data_source`, `date_sent`, `followup_date`, `response_date`, `promo_code`, `promo_url`, `activity_log`
#### Scope & Boundaries
- **HubSpot is sales pipeline ONLY** — tracks B2B partnership outreach to property management companies
- **Not connected to product infrastructure** — Stripe handles billing, Supabase handles customer data, Calendly handles scheduling
- **Promo tracking:** 69 deals have `promo_code` + `promo_url` linking to property-specific promo landing pages
- **Company associations:** 81 deals linked to 18 management companies; 144 deals pending association
### SEO & Performance (Feb 2026)
| Item | Detail |
| ------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **JSON-LD Structured Data** | Organization schema on all 6 main pages; LocalBusiness on index; FAQPage on faq. Enables Google rich snippets. |
| **Canonical URLs** | `` on all 6 main pages. No trailing slash (matches `cleanUrls: true`). |
| **Cache Headers (vercel.json)** | Images: 1 year immutable. CSS: `max-age=0, must-revalidate` (browser always revalidates; Vercel returns 304 if unchanged). Favicon: 1 year immutable. |
| **WCAG Text Contrast** | `--text-muted` darkened from `#88989A` (pre-v2.0 Cool Steel, ~3.2:1) to `#5F6E72` (~4.7:1) for WCAG AA compliance against parchment and white backgrounds. v2.0 uses Slate Grey (#6A7F83) for secondary text. |
| **Skip-to-Content** | Visually-hidden skip link on all 6 main pages (WCAG 2.4.1 Level A). |
| **robots.txt** | Permits all crawlers. Points to `/sitemap.xml`. |
| **sitemap.xml** | Lists all 8 HTML pages with `` and `` dates. Auto-deployed via Vercel. |
| **Heading Hierarchy** | Footer headings converted from `
`/`
` to `
` on all pages. Eliminates skipped heading levels flagged by Lighthouse without changing visual appearance. |
| **Main Landmark** | All subpages (terms, privacy, faq, testimonials, partnerships, signup/*) wrapped in `` element. Landing page already had one. Enables screen reader "skip to main content" navigation. |
| **Footer Contrast** | Footer text/link color overridden from `#6A7F83` (2.76:1) to `#9BB0B5` (5.14:1 on `#213C47` background). Passes WCAG AA. Portal feature highlight text on partnerships page also adjusted to `#566A6E` (5.70:1). |
| **Lighthouse Scores** | Accessibility 100/100 on all pages (up from 93 landing, 91 partnerships). Best Practices 100/100 after adding `favicon.ico`. |
---
## Security
### Authentication
- **Method:** Magic links (email-based, no passwords)
- **Provider:** Supabase Auth
- **Token:** JWT with user ID
- **Session:** Persisted to localStorage; survives new tabs, refresh, and browser restart. Auth guard (`ProtectedRoute`) gates on Supabase `INITIAL_SESSION` event before rendering redirects.
### Row Level Security (RLS)
All customer tables are protected by RLS:
- Users can only see/modify their own data
- `auth.uid()` used in all policies
- Service role bypasses RLS (edge functions only)
### Billing Protection
Billing fields on `customer_profile` are protected:
- Users CANNOT update `subscription_status`, `stripe_customer_id`, `subscription_id`
- Updates only via SECURITY DEFINER functions from webhooks
### Secrets Management
> **Policy (Non-Negotiable)**
>
> Never paste secrets into chat, docs, or commits. Never ask a human to paste secrets into chat. 1Password CLI (`op`) is the approved source of truth for all credentials.
>
#### Secret Inventory
| Secret | 1Password Item | Field | Consumers |
| ---------------------------- | -------------------------------- | --------------------- | --------------------------------------------------------------------------------------------------- |
| Supabase DB Password | Supabase Production | `password` | Local `psql` only |
| Stripe Live Secret Key | Stripe Live | `secret_key` | Supabase secrets (`STRIPE_SECRET_KEY`), local Stripe CLI |
| Stripe Webhook Secret | Stripe Live | `webhook_secret` | Supabase secrets (`STRIPE_WEBHOOK_SECRET`) |
| Resend API Key | Resend API | `credential` | Supabase secrets (`RESEND_API_KEY`); Resend MCP (via `resend-mcp-launcher.sh`, 1Password-backed) |
| GA4 OAuth Client ID | Google Cloud - GA4 Analytics MCP | `client_id` | GA4 MCP (via `ga4-mcp-launcher.sh`; ADC at `~/.config/gcloud/application_default_credentials.json`) |
| Calendly PAT | Calendly | `PAT` | Calendly MCP (via `calendly-mcp-launcher.sh`, 1Password-backed); manual API calls |
| Calendly Webhook Signing Key | Calendly | `webhook_signing_key` | Supabase secrets (`CALENDLY_WEBHOOK_SIGNING_KEY`) |
| Notion API Key | Notion API | `api_key` | Notion MCP plugin (currently disabled) |
| Supabase Service Role Key | Supabase Production | `service_role_key` | Supabase-managed; used by all edge functions |
#### Approved Patterns
```
# Fetch a secret (never prints to terminal)
OP_BIOMETRIC_UNLOCK_ENABLED=true op read 'op://Storage Valet/Stripe Live/secret_key'
# Push to Supabase without exposing the value
OP_BIOMETRIC_UNLOCK_ENABLED=true op read 'op://Storage Valet/Stripe Live/secret_key' \
| xargs -I {} supabase secrets set STRIPE_SECRET_KEY="{}" --project-ref gmjucacmbrumncfnnhua
# Shell env vars — lazy-loaded via sv-env function (run once per session)
# Secrets are NOT resolved at shell startup to avoid macOS permission popups
sv-env # loads SUPABASE_ACCESS_TOKEN, STRIPE_SECRET_KEY, RESEND_API_KEY
# also caches to ~/.sv-session (chmod 600) for sub-agent inheritance
# session file auto-deletes on shell exit; run sv-env-clear to wipe manually
```
> **Never Do This**
>
> Never run secrets inline in commands (e.g., `PGPASSWORD="actual_password" psql ...`). Claude Code's permission caching stores the literal command string, persisting the secret to disk. Always use `op read` or `$ENV_VAR` references instead.
>
#### Rotation Procedures
1. Generate/rotate the credential in the service's dashboard
2. Update the value in 1Password (Storage Valet vault)
3. If the secret is in Supabase secrets, push via: `op read '...' | xargs -I {} supabase secrets set KEY="{}"`
4. Verify the dependent service still works (e.g., test checkout, test email)
**Deferred:** Supabase service role key rotation. High blast radius (breaks all 16 edge functions). Requires planned maintenance window. See .
#### Tripwire (Automated Detection)
`sv-final-audit.sh` includes a secrets tripwire that scans `~/.zshrc`, `~/.claude/settings.json`, and `~/.claude/settings.local.json` for plaintext credential patterns at the end of every session. It also flags any `.env` files in `~/code/` repos. Additionally, the audit script includes a GitHub hygiene layer that detects stale PRs (open >7 days) and orphan remote branches (>30 days with no associated PR) across all 6 repositories.
---
## Deployment
### Portal (Vercel)
```
cd ~/code/sv-portal
npm run build # Verify build passes
vercel # Preview deploy (NOT --prod)
```
Auto-deploys from GitHub main branch. Use preview deploys for testing; Zach promotes to production manually. Do not run `vercel --prod` directly.
**PR validation:** GitHub Actions workflow (`.github/workflows/pr-validation.yml`) runs `npm run typecheck` and `npm run build` on all PRs to main. Both must pass before merge.
### Edge Functions
```
cd ~/code/sv-edge
./scripts/deploy-customer-facing.sh # Deploy all 14 customer-facing functions
./scripts/smoke-edge.sh # Post-deploy smoke test
# OR manually:
supabase functions deploy --no-verify-jwt
```
Always run `./scripts/check-deps.sh` before deploying. Deploy ALL customer-facing functions together to avoid version drift.
### Database Migrations
```
cd ~/code/sv-db
supabase db push --linked
```
**Requires:** Docker Desktop running
### Git Configuration
| Setting | Value |
| ---------------- | ------------------------- |
| **Author Email** | `zach@mystoragevalet.com` |
| **Author Name** | Zachary Brown |
Use only real, verified email addresses. Fabricated emails cause Vercel permission failures.
### Architecture Constraints (Non-Negotiable)
1. **4 customer routes only:**`/login`, `/dashboard`, `/schedule`, `/account`
2. **Staff routes** permitted under `/ops` and `/admin/*` but must remain staff-gated (`sv.is_staff()`)
3. **Supabase backend only** — no custom API servers
4. **Stripe Hosted flows only** — no custom card UI
5. **Single pricing tier:** $299/month
6. **Magic links only** — no password auth
7. **Portal is authentication-only** — account creation happens exclusively through the website registration flow (Stripe Checkout). The portal login does not create new users.
8. **RLS on all tables** — zero cross-tenant access
9. **Private storage bucket** — signed URLs, 1h expiry
---
## Architecture Notes
> **Insurance Coverage**
>
> Included insurance coverage ($2,000 per customer) is enforced server-side via the `v_user_insurance` database view. All portal UI elements (coverage progress bar, remaining coverage text) derive their values from this view; no insurance limits are hardcoded in the frontend.
>
---
## Common Issues & Solutions
| Issue | Solution |
| -------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Webhook 401 | Deploy with `--no-verify-jwt` |
| CORS blocking POST (OPTIONS 200, POST never fires) | Ensure `Access-Control-Allow-Headers` includes `apikey, x-client-info` alongside `authorization, content-type` |
| Edge function auth 401 despite valid JWT | Use `getUser(token)` with explicit token arg, not `getUser()` on a stateless client |
| Migration not applied | Run `supabase db push --linked` |
| Docker not running | Start Docker Desktop |
| RLS blocking query | Check `user_id` matches `auth.uid()` |
| Preview auth redirects fail | Supabase Auth requires preview domains to be allowlisted in URL Configuration (Redirect URLs). If not allowlisted, magic links may redirect to production or fail. |
| React Query key mismatch | Bookings list cache uses query key `['bookings-list']`. Do not invalidate `['bookings']` expecting it to refresh the dashboard; ensure key alignment. |
---
## Platform Operations Toolkit
A tiered toolkit for session integrity, repository hygiene, and infrastructure observability. Introduced March 2026 after an orphaned PR went undetected for 35 days. See for rationale.
### Commands
| Command | Purpose | When to Use |
| ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------- |
| `sv-audit` | Session integrity and repo hygiene detection. Checks git status, stale PRs (>7d), orphan branches (>30d), secrets tripwire, root directory policing, 1Password socket cleanup. Non-destructive. | End of every session (mandatory) |
| `sv-hygiene` | Deletes remote branches already merged into main. Never deletes unmerged work. | Weekly, or after audit flags stale branches |
| `sv-health` | Platform observability: Supabase migration drift, Vercel deployment failures, GitHub Actions failures, Stripe webhook backlog. | Weekly, or when verifying production health |
| `sv-check` | Unified command: runs audit, auto-triggers hygiene if ATTENTION, then runs health checks. | The "is everything OK?" command |
### All Shell Shortcuts
Defined in `~/.zshrc`. Run `sv-help` to see this list in the terminal.
| Command | What It Does |
| -------------- | ---------------------------------------------------------------------------- |
| `sv ` | Navigate to a repo (e.g., `sv portal`) |
| `sv-status` | Git status across all 6 repos |
| `sv-pull` | Pull latest across all 6 repos |
| `sv-audit` | Run the end-of-session audit script |
| `sv-hygiene` | Weekly repo cleanup (merged branches) |
| `sv-health` | Platform health check (migrations, deploys, webhooks) |
| `sv-check` | Full platform check (audit + hygiene + health) |
| `sv-issues` | Open GitHub issues across all repos |
| `sv-brew` | Update Homebrew packages |
| `sv-env` | Load secrets from 1Password (cached for sub-agents) |
| `sv-env-clear` | Clear cached secrets and remove session file |
| `sv-help` | Show all available commands |
| `title ` | Set iTerm2 tab title (no args = reset to auto directory name) |
| `stripe` | Aliased to `stripe --live` (use `command stripe` or `\stripe` for test mode) |
#### iTerm2 Tab Auto-Naming
Tabs automatically show the current directory name via a `precmd` hook in `~/.zshrc`. Use `title "My Topic"` to set a custom title, or `title` (no args) to reset to auto. Claude Code also sets the tab title to the chat name when running.
**Setup (one-time per machine):** Disable custom tab titles in iTerm2’s plist (quit iTerm2 first, run via Terminal.app), then add the `_update_tab_title` precmd function and `title()` helper to `~/.zshrc`. Both machines were configured March 2026.
#### 1Password SSH Agent
Both machines use 1Password’s SSH agent for Git authentication and commit signing. Three configuration files work together:
| File | Purpose |
| ------------------------------------ | ------------------------------------------------------------------------------------------ |
| `~/.zshrc` | Exports `SSH_AUTH_SOCK` pointing to the 1Password agent socket |
| `~/.ssh/config` | Sets `IdentityAgent` to the same socket for all hosts |
| `~/.config/1Password/ssh/agent.toml` | Tells the 1Password SSH agent which key to serve (required — auto-discovery is unreliable) |
| `~/.ssh/allowed_signers` | Maps email to public key for Git commit signature verification |
**SSH_AUTH_SOCK** (in `~/.zshrc`):
```
export SSH_AUTH_SOCK="$HOME/Library/Group Containers/2BUA8C4S2C.com.1password/t/agent.sock"
```
**agent.toml** (in `~/.config/1Password/ssh/`):
```
[[ssh-keys]]
item = "Github SSH Key (SV)"
vault = "Storage Valet"
```
**Key details:** The SSH key is a single ed25519 key named “Github SSH Key (SV)” stored in the Storage Valet vault. Both machines share the same key via 1Password vault sync. The key is registered on GitHub for both authentication and commit signing.
**Git signing config** (set via `git config --global` on both machines):
```
gpg.format = ssh
commit.gpgsign = true
tag.gpgsign = true
gpg.ssh.allowedSignersFile = ~/.ssh/allowed_signers
```
### Script Locations
All scripts are canonical in `~/code/sv-docs/scripts/` (git-tracked). A convenience symlink at `~/code/sv-final-audit.sh` points to the canonical audit script for manual use, but no script depends on this symlink.
| Script | Canonical Path |
| --------------- | ------------------------------------------------- |
| Session audit | `~/code/sv-docs/scripts/sv-final-audit.sh` |
| Repo hygiene | `~/code/sv-docs/scripts/repo-hygiene.sh` |
| Platform health | `~/code/sv-docs/scripts/platform-health-check.sh` |
| Unified check | `~/code/sv-docs/scripts/sv-check.sh` |
### Design Principles
- **Non-destructive audits:**`sv-audit` reads only (except 1Password socket cleanup). It never modifies git state, GitHub, or infrastructure.
- **Explicit cleanup:**`sv-hygiene` is a separate, opt-in command. It only deletes branches confirmed merged into main.
- **Canonical paths:** Scripts reference `~/code/sv-docs/scripts/` directly. Symlinks exist for convenience but are not dependencies.
- **Dual observability:** Stale PRs are detected at session start (bootstrap snippet) and session end (audit script).
---
## Developer Setup
### Code Repositories
```
~/code/
├── sv-portal/ # Customer-facing React app (Vite + React + TypeScript + Tailwind)
├── sv-edge/ # Supabase Edge Functions (Deno/TypeScript)
├── sv-db/ # Database migrations (SQL)
├── sv-docs/ # Operational scripts, runbooks, and archive
├── sv-website/ # Landing page (www.mystoragevalet.com)
└── sv-wiki/ # Internal wiki (wiki.mystoragevalet.com)
```
> **Sync Strategy**
>
> **Mac Studio (primary):** iCloud Desktop & Documents sync ON, all files stored locally.
**MacBook Air (secondary):** iCloud sync ON + "Optimize Mac Storage" ON (auto-offloads when space is tight).
**Dropbox:** Redundant — subscription lapses ~Sept 2026. Not used for active files.
>
### New Machine Setup
When setting up Claude Code on a new machine:
1. Ensure iCloud Desktop & Documents sync is enabled (System Settings → Apple ID → iCloud → iCloud Drive)
2. Create symlinks:
```
ln -sf ~/Documents/storagevalet/Technology/Reference/CLAUDE.md ~/.claude/CLAUDE.md
ln -sf ~/Documents/storagevalet/Technology/Reference/code-CLAUDE.md ~/code/CLAUDE.md
```
3. Clone repos to `~/code/`: `sv-portal`, `sv-edge`, `sv-db`, `sv-docs`, `sv-website`, `sv-wiki`
4. Install Node.js via Homebrew: `brew install node@24` (do NOT use .pkg installer)
5. Install CLI tools: `brew install supabase gh stripe deno pipx`
6. Install Homebrew-managed casks: `brew install --cask 1password-cli appcleaner bettermouse claude-code cleanshot codex cursor font-jetbrains-mono font-source-code-pro framer gcloud-cli github hazel loom miro notion obsidian rectangle-pro spotify superwhisper textexpander typora visual-studio-code whimsical wispr-flow`
**Note:**`claude-code` cask is used on Mac Studio. On MacBook Air, Claude Code is installed via the native installer (`claude install`, installs to `~/.local/bin/claude`). Do NOT install via npm — the npm package `@anthropic-ai/claude-code` is deprecated in favor of native installers.
7. Install remaining apps directly: Docker Desktop (required for Supabase CLI), 1Password (biometric agent for `op` CLI), iTerm2 (custom Dock icon; has reliable built-in updater), Google Chrome, Google Drive — these must NOT be installed via Homebrew as replacement can break system integrations
8. Run `op signin` to authenticate 1Password
> **App Update Strategy (as of March 2026)**
>
> Desktop apps are managed in three tiers:
>
> 1. **Homebrew casks (25 packages):** Updated via `brew upgrade --greedy`. The `--greedy` flag is required because most casks are marked `auto_updates`, which plain `brew upgrade` skips. Includes desktop apps, CLI tools, and fonts.
> 2. **App Store apps:** Updated via `mas upgrade` (requires password in terminal).
> 3. **Direct-install apps (1Password, Docker, iTerm2, Google Chrome, Google Drive):** Self-update or update via their own mechanisms. These are excluded from Homebrew to avoid breaking system-level integrations (biometric auth, Docker VM, Drive sync daemon).
> The Raycast script **SV Update All** (`sv-update-all.sh`) is the all-in-one daily maintenance command. It runs Homebrew (`--greedy` + `cleanup`), App Store checks, Docker cleanup (`docker system prune -f`, guarded), and Claude Code plugin updates. The npm section was removed in March 2026 after Claude Code migrated to native installers. This script replaces the need for the separate `sv-brew` shell function.
>
> A shared **Brewfile** at `~/Documents/storagevalet/Technology/Brewfile` (iCloud-synced) is the canonical source for all Homebrew-managed packages. On a new machine, run `brew bundle install --file=~/Documents/storagevalet/Technology/Brewfile` then skip machine-specific packages as needed (see per-machine notes above).
>
> **Portability Rule**
>
> `~/.claude/CLAUDE.md` must never contain absolute paths starting with `/Users/` or `/Library/`. Use `~/` and symlink resolution instead. Both CLAUDE.md files are iCloud-synced via symlinks:
>
> - `~/.claude/CLAUDE.md` → `~/Documents/storagevalet/Technology/Reference/CLAUDE.md` (global context)
> - `~/code/CLAUDE.md` → `~/Documents/storagevalet/Technology/Reference/code-CLAUDE.md` (cross-repo session startup)
### Session Startup Checklist
Run these steps at the beginning of each Claude Code session (human or agent):
1. **Check for CLAUDE.md sync conflicts:**
```
CLAUDE_DIR="$(cd "$(dirname "$(readlink ~/.claude/CLAUDE.md || echo ~/.claude/CLAUDE.md)")" && pwd)"
ls "$CLAUDE_DIR" 2>/dev/null | grep -i conflict && echo "CLAUDE.md CONFLICT — resolve before continuing"
```
2. **Pull latest for all repos:**
```
for repo in sv-portal sv-edge sv-db sv-docs sv-website sv-wiki; do (cd ~/code/$repo && git pull); done
```
3. **Verify migrations:**`supabase migration list --linked`
4. **Read context:**`~/.claude/CLAUDE.md` and `~/code/CLAUDE.md`
5. **Docker Desktop** must be running for Supabase CLI
### Session End Protocol
1. Run `sv-audit` (or `sv-check` for full platform verification) — do not declare clean until `Audit: CLEAN`
2. If the audit flags GitHub hygiene issues (stale PRs, orphan branches): review and resolve
3. If session was significant (deploy, E2E verification, architecture decision): update relevant SV-Wiki pages and commit to sv-wiki repo
See for the full command reference.
---
# Database Schema
*Last updated: Mar 13, 2026*
---
## Schema Overview
```
Supabase Database
├── public (legacy customer-facing - being phased out)
│ ├── customer_profile # User accounts + subscription
│ ├── items # Inventory management
│ ├── actions # Service requests (pickup/delivery)
│ ├── inventory_events # Item movement audit trail
│ ├── booking_events # Booking lifecycle audit trail
│ ├── claims # Insurance claims
│ └── service_areas # Serviceable ZIP codes
│
├── sv (canonical domain schema - primary)
│ ├── customer_profile # Canonical customer data
│ ├── items # Canonical inventory
│ ├── actions # Service requests
│ ├── inventory_events # Item movement audit
│ ├── ops_tasks # Operations tasks
│ ├── item_embeddings # Vector embeddings for search
│ ├── staff # Staff access control
│ ├── container_catalog # Container types
│ ├── referrals # Referral program
│ ├── account_ledger # Financial transactions
│ ├── pre_customers # Pre-signup leads
│ ├── signup_anomalies # Error tracking
│ └── webhook_events # Webhook idempotency
│
├── billing (Stripe integration)
│ └── webhook_events # Webhook idempotency log
│
├── auth (Supabase-managed)
│ └── users # Authentication (magic links)
│
└── storage (Supabase-managed)
└── buckets # File storage (item-photos)
```
### Current Status (Feb 2026)
| Schema | Status | Notes |
| --------- | ----------------- | ------------------------------------------------- |
| `public` | ACTIVE PRODUCTION | 1 customer profile (total, as of Feb 19, 2026) |
| `sv` | PLANNED CANONICAL | 0 records - not yet migrated to |
| `billing` | ACTIVE | Stripe webhook idempotency log (`webhook_events`) |
The `sv` schema represents the target architecture. Migration from `public` to `sv` is planned post-launch.
---
## Entity Relationship Diagram
```
┌──────────────────────┐
│ auth.users │ (Supabase-managed)
│ ───────────────── │
│ id (PK) │
│ email │
└──────────┬───────────┘
│ 1:1
▼
┌──────────────────────────────────────────────────┐
│ public.customer_profile │
│ ───────────────────────────────────────────── │
│ user_id (PK, FK → auth.users) │
│ email, stripe_customer_id, subscription_id │
│ subscription_status, last_payment_at │
│ full_name, phone, delivery_address (jsonb) │
│ out_of_service_area, needs_manual_refund │
└───────────┬──────────────────────────────────────┘
│ 1:N
▼
┌────────────────────────────────────────────┐
│ public.items │
│ ─────────────────────────────────────── │
│ id (PK), user_id (FK) │
│ label, description, category │
│ status (home|in_transit|stored|scheduled) │
│ photo_paths[], qr_code (UNIQUE) │
│ weight_lbs, dimensions, cubic_feet │
│ physical_locked_at, tags[] │
└────────┬──────────────────┬────────────────┘
│ 1:N │ M:N (via arrays)
▼ ▼
┌────────────────────┐ ┌──────────────────────────┐
│ inventory_events │ │ public.actions │
│ ─────────────── │ │ ───────────────────── │
│ id, item_id (FK) │ │ id, user_id (FK) │
│ event_type │ │ service_type, status │
│ event_data │ │ pickup_item_ids[] │
│ created_at │ │ delivery_item_ids[] │
└────────────────────┘ │ calendly_event_uri │
│ scheduled_start/end │
└─────────┬────────────────┘
│ 1:N
▼
┌──────────────────────┐
│ booking_events │
│ ─────────────────── │
│ id, action_id (FK) │
│ event_type │
│ metadata, created_at│
└──────────────────────┘
```
---
## Core Tables
### customer_profile
**Purpose:** User account and subscription data (1:1 with auth.users)
| Column | Type | Description |
| --------------------- | ------------- | ---------------------------------------------------- |
| `user_id` | UUID (PK, FK) | Links to auth.users |
| `email` | TEXT (UNIQUE) | Customer email |
| `stripe_customer_id` | TEXT (UNIQUE) | Stripe customer ID |
| `subscription_id` | TEXT | Active subscription ID |
| `subscription_status` | ENUM | inactive, active, past_due, canceled, trialing, etc. |
| `last_payment_at` | TIMESTAMPTZ | Latest successful payment |
| `full_name` | TEXT | Customer name |
| `phone` | TEXT | Contact phone |
| `delivery_address` | JSONB | {street, unit, city, state, zip} |
| `out_of_service_area` | BOOLEAN | Soft gate for out-of-area |
> **Protected Columns**
>
> Users CANNOT update `subscription_status`, `stripe_customer_id`, `subscription_id` directly.
>
### items
**Purpose:** Customer inventory with multi-photo support
| Column | Type | Description |
| ------------- | ------------------- | ----------------------------------- |
| `id` | UUID (PK) | Item identifier |
| `user_id` | UUID (FK) | Owner |
| `label` | TEXT (NOT NULL) | Item name |
| `description` | TEXT (NOT NULL) | Item description |
| `category` | TEXT | Optional category |
| `status` | ENUM | home, in_transit, stored, scheduled |
| `photo_paths` | TEXT[] | Multi-photo array (1-5) |
| `qr_code` | TEXT (UNIQUE) | Format: SV-YYYY-NNNNNN |
| `cubic_feet` | NUMERIC (GENERATED) | Auto-calculated |
| `tags` | TEXT[] | Searchable keywords |
### actions
**Purpose:** Service requests (pickup, redelivery, container delivery)
| Column | Type | Description |
| -------------------- | ------------- | -------------------------------------------------------------------------------- |
| `id` | UUID (PK) | Action identifier |
| `user_id` | UUID (FK) | Customer |
| `service_type` | ENUM | pickup, redelivery, container_delivery |
| `status` | ENUM | pending_items, pending_confirmation, confirmed, in_progress, completed, canceled |
| `pickup_item_ids` | UUID[] | Items to pick up |
| `delivery_item_ids` | UUID[] | Items to deliver |
| `calendly_event_uri` | TEXT (UNIQUE) | Calendly event identifier |
| `scheduled_start` | TIMESTAMPTZ | Calendly booking start |
### service_areas
**Purpose:** Serviceable ZIP codes
| Column | Type | Description |
| ------- | --------- | ----------- |
| `zip` | TEXT (PK) | ZIP code |
| `city` | TEXT | City name |
| `state` | TEXT | State code |
---
## Billing Tables
### billing.webhook_events
**Purpose:** Stripe webhook idempotency log
| Column | Type | Description |
| -------------- | -------------- | --------------------------------- |
| `id` | BIGSERIAL (PK) | Auto-increment ID |
| `event_id` | TEXT (UNIQUE) | Stripe event.id (idempotency key) |
| `event_type` | TEXT | e.g., checkout.session.completed |
| `payload` | JSONB | Full Stripe event object |
| `processed_at` | TIMESTAMPTZ | Processing completion |
---
## Database Functions
### update_subscription_status()
**Purpose:** Update billing-protected columns from webhooks
**Type:** SECURITY DEFINER (bypasses RLS)
```
SELECT update_subscription_status(
p_user_id := 'user-uuid',
p_status := 'active',
p_subscription_id := 'sub_123',
p_last_payment_at := now()
);
```
### log_booking_event()
**Purpose:** Insert booking events from edge functions
**Type:** SECURITY DEFINER
```
SELECT log_booking_event(
p_action_id := 'action-uuid',
p_event_type := 'calendly_webhook',
p_metadata := '{"event_uri": "..."}'::jsonb
);
```
### fn_revert_orphaned_scheduled()
**Purpose:** Detect and revert items stuck in scheduled state with no active booking. Called on dashboard load as a safety guardrail.
**Type:** SECURITY DEFINER
- Finds items where `status='scheduled'` with no active action referencing them
- Reverts pickup orphans to `home`, delivery orphans to `stored`
- Logs each reversion to `inventory_events` with type `orphaned_scheduled_reverted`
- Returns JSON summary for frontend toast notification
- Items with no action history are flagged as `unknown` (not mutated)
### get_ops_actions()
**Purpose:** Staff-only RPC for the Ops Dashboard. Returns actions joined with customer profile data.
**Type:** SECURITY DEFINER, gated by `sv.is_staff()`
- Wraps `sv.v_ops_actions` view (cross-schema join of `public.actions` + `sv.customer_profile`)
- Only accessible to authenticated staff members
---
## RLS Policies
### customer_profile
- ✅ Owner can SELECT own profile
- ✅ Owner can UPDATE editable fields (name, phone, address)
- ❌ Owner CANNOT update billing fields
### items
- ✅ Owner can SELECT/INSERT/UPDATE/DELETE own items
### actions
- ✅ Owner can SELECT own actions (or staff can view all)
- ✅ Owner can INSERT actions
- ✅ Owner can UPDATE/DELETE own actions in `pending`, `pending_items`, or `pending_confirmation` status only
- ✅ Staff can UPDATE/DELETE any action regardless of status
- ❌ Owner CANNOT update to confirmed/completed (ops only)
### service_areas
- ✅ RLS enabled — authenticated users can SELECT (lookup table)
- ❌ No INSERT/UPDATE/DELETE for users
### sv.staff
- ✅ RLS enabled — `service_role` only (no direct user access)
### inventory_events
- ✅ Owner can SELECT events for their items
- ❌ Owner CANNOT insert (system-only via service_role)
### booking_events
- ✅ Owner can SELECT events for their own bookings (via action ownership JOIN)
- ✅ Staff can SELECT all booking events
- ❌ Orphan events (`action_id IS NULL`) are only visible to staff
- ❌ Owner CANNOT insert (system-only via service_role)
---
## Common Queries
### Get user items for dashboard
```
SELECT * FROM items
WHERE user_id = auth.uid()
ORDER BY created_at DESC;
```
### Search items
```
SELECT * FROM items
WHERE user_id = auth.uid()
AND status = 'stored'
AND (
label ILIKE '%keyword%'
OR description ILIKE '%keyword%'
OR 'keyword' = ANY(tags)
);
```
### Get pending bookings
```
SELECT * FROM actions
WHERE user_id = auth.uid()
AND status IN ('pending_items', 'pending_confirmation')
ORDER BY scheduled_start ASC;
```
---
## Migration History
**Total: 44 migrations applied** (verified Mar 13, 2026)
Key migrations include:
- `0001_init` — Core schema
- `0004_phase1_inventory_enhancements` — Multi-photo, status, batch ops
- `0005_create_sv_schema` — Create sv domain schema
- `20251112000005_billing_status_tracking` — Payment timestamps
- `20251201062222_create_service_areas_table` — Service areas
- `20260107000001` — Billing v2 trial columns
- `20260129000001` — Enable RLS on service_areas
- `20260202000001` — `fn_revert_orphaned_scheduled` guardrail
- `20260202000002` — Deprecate `billing.customers`
- `20260204000001–05` — Ops dashboard: `sv.v_ops_actions` view, `get_ops_actions` RPC, staff RLS hardening
- `20260211000001` — **Track A:** Harden actions UPDATE/DELETE RLS (restore status constraint), fix booking_events cross-tenant leak
- `20260211000002` — **Track A:** Capture 3 missing RPCs in migration history (`check_stripe_webhook_event`, `insert_stripe_webhook_event`, `get_user_id_by_email`)
- `20260211000003` — **Track A:**`is_valid_zip_code()` STABLE table-backed function
- `20260211000004` — **Track A:** Drop stale `booking_events_read_policy` (P0 leak closure) and duplicate `p_actions_owner_insert`
- `20260215000001` — Drop duplicate `update_subscription_status(subscription_status_enum)` overload (P0 stripe-webhook fix)
- `20260227000001` — Drop deprecated `upsert_billing_customer` function (legacy helper for `billing.customers`, no longer referenced)
- `20260304000001` — Drop deprecated `billing.customers` table (redundant with `customer_profile.stripe_customer_id`)
#### Pending Migrations
No pending migrations.
Full migration list: `~/code/sv-db/supabase/migrations/`
---
# Brand Identity
*Last updated: Feb 19, 2026*
---
## Quick Start (Developers)
This covers 90% of implementation work. Reference detailed sections below for edge cases.
### Use These 90% of the Time
| Purpose | Token / Color | Hex |
| --------------------- | ------------------------------------ | ----------------- |
| Headings (H1/H2) | Deep Teal + DM Serif Display | #213C47 |
| Headings (H3+) | Deep Teal + Inter | #213C47 |
| Body text | Gunmetal | #343A40 |
| Secondary text | Slate Grey (large text only on warm) | #6A7F83 |
| Primary CTA (buttons) | Berry | #A14567 |
| CTA hover | Berry Dark | #8A3755 |
| Links / interactive | Stormy Teal | #0E6F6A |
| Warm backgrounds | Parchment / Alabaster | #EEEBE5 / #E0E1DD |
| Warm accent surfaces | Rosey Tint + Deep Teal text | #E8DAD6 |
| Warm accent | Rose Taupe | #AC928A |
| Borders / disabled | Slate Grey | #6A7F83 |
> **Never**
>
> - **Never use Slate Grey for small text on warm backgrounds** — use Gunmetal instead
> - **Never use Berry for headlines** — reserved for CTAs only
> - **Never remove focus outlines** without a compliant 2px Stormy Teal ring replacement
> - **Never use Rose Taupe for body text** — warm accents only (borders, icons, decorative)
### Quick Token Reference
```
/* Copy-paste starter for any new component */
--color-heading: var(--charcoal-blue); /* Headlines */
--color-body: var(--gunmetal); /* Body text */
--color-cta: var(--berry); /* Buttons */
--color-cta-hover: var(--berry-dark); /* Button hover */
--color-link: var(--stormy-teal); /* Links, focus */
--bg-page: var(--parchment); /* Main background */
--bg-card: var(--rosey-tint); /* Warm card surface */
--color-accent: var(--rose-taupe); /* Warm accent */
```
---
## Brand Overview
**Storage Valet** — Premium pickup, storage, and delivery for the New Jersey Gold Coast
### Brand Personality
- **Warm and approachable** — never cold or corporate
- **Reliable and trustworthy** — your belongings are safe with us
- **Premium without being pretentious**
- **Efficient and technology-forward**
- **Calm and organized** — we handle the chaos so you don't have to
### Visual Language
The palette combines grounded teals (reliability, trust) with warm neutrals (approachability, premium feel) and a distinctive berry call-to-action (warmth, confidence). Rose Taupe and Rosey Tint add a warm, sophisticated accent layer that complements the parchment backgrounds.
The dual-font typography system — DM Serif Display for high-impact headlines paired with Inter for all UI and body text — signals "premium concierge service" rather than "generic storage company."
> **Brand Spelling**
>
> **Storage Valet** (two words, space between) — use consistently everywhere.
>
---
## Logo Specifications
### Logo Variants
| Variant | Use Case | Background |
| --------------- | ------------------------------ | ---------------------------- |
| Full Color | Primary usage, marketing | White, Parchment, Alabaster |
| White (Reverse) | Dark backgrounds, hero panels | Deep Teal, dark photos |
| Single Color | Embroidery, single-color print | Any with sufficient contrast |
### Minimum Size
- **Digital:** 120px wide minimum
- **Print:** 1 inch (25mm) wide minimum
- Below these sizes, use the icon-only mark
### Clear Space
Maintain clear space equal to the height of the "V" in "Valet" on all sides. No other elements should enter this zone.
### Incorrect Usage
- Do not stretch, skew, or distort the logo
- Do not add drop shadows, glows, or effects
- Do not recolor outside of approved variants
- Do not place on low-contrast or busy backgrounds
- Do not rotate or place at angles
---
## Color Palette (v2.0 — Canonical)
The palette consists of 10 core colors with distinct functional roles, plus one hover state derivative.
### Primary Colors
Charcoal Blue (Deep Teal)#213C47 · RGB 33, 60, 71Headlines, hero panels, nav, footerANCHORGunmetal#343A40 · RGB 52, 58, 64Primary body text, paragraphsSlate Grey#6A7F83 · RGB 106, 127, 131Secondary text, borders, disabled states
### Accent & Action
Stormy Teal#0E6F6A · RGB 14, 111, 106Links, icons, focus rings, status indicatorsACCENTBerry#A14567 · RGB 161, 69, 103**PRIMARY CTA buttons ONLY**CTABerry Dark#8A3755 · RGB 138, 55, 85CTA hover/active states
### Warm Surfaces
Rose Taupe#AC928A · RGB 172, 146, 138Warm accent — borders, icons, decorative elementsNEW IN v2.0Rosey Tint#E8DAD6 · RGB 232, 218, 214Card surface, info panels (text-safe warm surface)NEW IN v2.0
### Backgrounds
Parchment#EEEBE5 · RGB 238, 235, 229Warm primary page backgroundAlabaster#E0E1DD · RGB 224, 225, 221Cool alternating sectionsWhite#FFFFFF · RGB 255, 255, 255Cards, modals, inputs
---
## Warm Accent: Usage Guidelines
Rose Taupe (#AC928A) and Rosey Tint (#E8DAD6) are warm accent colors added in v2.0. They provide a sophisticated warm layer that complements the parchment-based backgrounds.
#### Allowed
- Info banners and tooltips (Rose Taupe as border/icon accent)
- Card surfaces and info panels (Rosey Tint background + Deep Teal text)
- "New" badges (Rosey Tint bg + Deep Teal text)
- Decorative borders and dividers
- Empty state illustrations
- Physical brand: packaging accents, tissue paper
#### Prohibited
- Rose Taupe as body text or secondary text (fails contrast)
- White text on Rose Taupe background
- Primary CTAs or action buttons (use Berry)
- Headlines or headings
> **Surface vs. Accent Rule**
>
> Use **Rosey Tint (#E8DAD6)** as the background for any surface containing text. Pair with Deep Teal or Gunmetal text for strong contrast. Reserve **Rose Taupe (#AC928A)** for accents, borders, and icons only — never as a text color.
>
---
## Typography
### Font Families
| Role | Font | Fallback Stack |
| ------------------------------- | ---------------- | --------------------------------------------------------------------------------- |
| **Display Headlines (H1/H2)** | DM Serif Display | Georgia, Times New Roman, serif |
| **Body & UI (everything else)** | Inter | -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica, Arial, sans-serif |
| **Monospace** | JetBrains Mono | Menlo, Monaco, Consolas, monospace |
> **Dual-Font System**
>
> Storage Valet — DM Serif Display is used **exclusively** for H1 and H2 display headlines. All UI elements, navigation, labels, H3+ headings, and body text use Inter. This creates clear visual hierarchy: serif = "high-level narrative," Inter = "functional UI."
>
### Type Scale
| Element | Font | Size | Weight | Line Height | Color |
| ------- | ---------------- | ---- | ------ | ----------- | ------------------ |
| H1 | DM Serif Display | 36px | 400 | 1.2 | Deep Teal |
| H2 | DM Serif Display | 28px | 400 | 1.25 | Deep Teal |
| H3 | Inter | 22px | 600 | 1.3 | Deep Teal |
| Body | Inter | 16px | 400 | 1.5 | Gunmetal |
| Small | Inter | 14px | 400 | 1.4 | Gunmetal (default) |
| Caption | Inter | 12px | 400 | 1.4 | Gunmetal (default) |
| Buttons | Inter | 15px | 500 | 1.0 | White (on Berry) |
### Font Weights
| Font | Weight | Usage |
| ---------------- | ------ | ---------------------------------- |
| DM Serif Display | 400 | H1, H2, hero headlines only |
| Inter | 400 | Body text, paragraphs |
| Inter | 500 | UI labels, buttons, navigation |
| Inter | 600 | H3+, emphasis, card titles |
| Inter | 700 | Reserved for special emphasis only |
---
## Spacing & Layout Grid
All spacing derives from an **8px base unit** for consistent rhythm.
| Token | Value | Multiplier | Usage |
| -------- | ----- | ---------- | ---------------------------- |
| space-1 | 4px | 0.5x | Tight gaps, icon-to-text |
| space-2 | 8px | 1x | Default element gap |
| space-3 | 12px | 1.5x | Input padding, small cards |
| space-4 | 16px | 2x | Card padding, button padding |
| space-6 | 24px | 3x | Section gaps, large cards |
| space-8 | 32px | 4x | Component margins |
| space-12 | 48px | 6x | Page section separation |
| space-16 | 64px | 8x | Hero spacing, major sections |
### Component Spacing
- **Cards:** 16px padding, 8px gap between elements, 24px margin between cards
- **Buttons:** 12px vertical, 16px horizontal padding
- **Banners:** 16px padding, 8px icon-to-text gap
- **Form inputs:** 12px padding, 8px label-to-input gap
---
## Digital Applications
### Customer Portal
- Info banners: Rosey Tint bg + Rose Taupe left border + Deep Teal text
- Tooltips: Rose Taupe icon + Gunmetal text on white
- "New item" badges: Rosey Tint bg + Deep Teal text
- Secondary chart series: Rose Taupe (primary = Stormy Teal)
- Empty states: Rose Taupe illustrations on Parchment
### Marketing Website
- Hero panels: Deep Teal bg + white text + DM Serif Display headlines
- CTAs: Berry buttons with white text (hover: Berry Dark)
- Info callouts: Rosey Tint bg with Rose Taupe accents
- Footer: Deep Teal bg with white/Parchment text
### Email Templates (Resend)
5 transactional templates in Resend. 2 updated to v2.0 colors (Feb 19, 2026); 3 had no brand colors to update.
| Template | v2.0 Status | Notes |
| ---------------------------- | ---------------- | ------------------------------------------------------------------------ |
| Welcome Email | Updated | CTA button: Berry (#A14567) |
| Welcome — Email Confirmation | Updated | Header: Deep Teal (#213C47), CTA button: Berry (#A14567) with white text |
| Pickup Complete | No change needed | No brand colors in template |
| Delivery Complete | No change needed | No brand colors in template |
| Payment Failed | No change needed | No brand colors in template |
#### Email Brand Rules
- Headers: Deep Teal (#213C47)
- Info sections: Rosey Tint bg
- CTAs: Berry (#A14567) buttons with white text
- Invoice accents: Rose Taupe rule lines
### Remaining Manual Items
> **Not Yet Updated to v2.0**
>
> - **Supabase auth email templates** (magic link, recovery) — still on Supabase defaults, not branded
> - **Calendly scheduling page branding** — still default Calendly theme
---
## Data Visualization
| Series | Color | Hex | Notes |
| ------------- | ---------------- | ------- | ----------------------------------- |
| 1st (Primary) | Stormy Teal | #0E6F6A | Most important data |
| 2nd | Berry | #A14567 | Secondary comparison |
| 3rd | Rose Taupe | #AC928A | Tertiary data |
| 4th+ | Opacity variants | — | Use 40% opacity of existing palette |
Don't rely on color alone — supplement with patterns, direct labels, icons in legends, and sufficient contrast between adjacent series for colorblind safety.
---
## UI State Colors
| State | Background | Accent/Border | Text |
| ------- | --------------- | ------------- | -------------------- |
| Success | Stormy Teal 10% | Stormy Teal | Deep Teal / Gunmetal |
| Info | Rosey Tint | Rose Taupe | Deep Teal / Gunmetal |
| Warning | Berry 10% | Berry | Deep Teal / Gunmetal |
| Error | System red 10% | System red | Deep Teal / Gunmetal |
| Loading | Alabaster | Slate Grey | — |
### Focus States
All interactive elements must have visible focus indicators: **2px solid Stormy Teal (#0E6F6A)** with 2px offset. Never remove outline without a compliant replacement.
```
*:focus-visible {
outline: 2px solid var(--stormy-teal);
outline-offset: 2px;
}
```
---
## Physical Applications
### Vehicle Wraps
| Element | Color Application |
| --------------- | ----------------------- |
| Body panels | Deep Teal primary |
| Accent striping | Rose Taupe |
| Logo | White or Parchment |
| Contact info | White text on Deep Teal |
### Storage Containers & Packaging
| Element | Color Application |
| --------------- | ------------------------------------------------ |
| Primary labels | Deep Teal |
| Category coding | Rose Taupe band/accent + Deep Teal/Gunmetal text |
| Tissue paper | Rosey Tint |
| Box tape | Deep Teal with white logo |
### Uniforms
| Item | Color Application |
| -------------- | --------------------- |
| Primary polo | Deep Teal |
| Secondary polo | Rose Taupe (optional) |
| Embroidery | White or Berry |
---
## CSS Design Tokens
### Core Palette Tokens
```
:root {
/* Core Palette (v2.0 — 10 colors + 1 hover derivative) */
--charcoal-blue: #213C47;
--gunmetal: #343A40;
--slate-grey: #6A7F83;
--stormy-teal: #0E6F6A;
--berry: #A14567;
--berry-dark: #8A3755;
--rose-taupe: #AC928A;
--rosey-tint: #E8DAD6;
--parchment: #EEEBE5;
--alabaster: #E0E1DD;
--white: #FFFFFF;
}
```
### Semantic Aliases
```
:root {
/* Text */
--color-heading: var(--charcoal-blue);
--color-body: var(--gunmetal);
--color-secondary: var(--slate-grey);
/* Backgrounds */
--bg-page: var(--parchment);
--bg-alt: var(--alabaster);
--bg-card: var(--rosey-tint);
--bg-dark: var(--charcoal-blue);
/* Interactive */
--color-cta: var(--berry);
--color-cta-hover: var(--berry-dark);
--color-link: var(--stormy-teal);
--color-success: var(--stormy-teal);
/* UI Chrome */
--color-accent: var(--rose-taupe);
}
```
### Component Tokens
```
:root {
/* Info banner */
--sv-banner-info-bg: var(--rosey-tint);
--sv-banner-info-border: var(--rose-taupe);
--sv-banner-info-text: var(--charcoal-blue);
/* Badges */
--sv-badge-new-bg: var(--rosey-tint);
--sv-badge-new-text: var(--charcoal-blue);
/* States */
--sv-state-success-accent: var(--stormy-teal);
--sv-state-warning-accent: var(--berry);
/* Data visualization */
--sv-chart-series-1: var(--stormy-teal);
--sv-chart-series-2: var(--berry);
--sv-chart-series-3: var(--rose-taupe);
}
```
### Spacing Tokens
```
:root {
--sv-space-1: 4px;
--sv-space-2: 8px;
--sv-space-3: 12px;
--sv-space-4: 16px;
--sv-space-6: 24px;
--sv-space-8: 32px;
--sv-space-12: 48px;
--sv-space-16: 64px;
}
```
### Typography Tokens
```
:root {
--sv-font-display: 'DM Serif Display', Georgia, 'Times New Roman', serif;
--sv-font-body: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI',
Roboto, Helvetica, Arial, sans-serif;
--sv-font-mono: 'JetBrains Mono', Menlo, Monaco, Consolas, monospace;
}
```
> **Token Naming Discipline**
>
> - **"berry" / "cta" prefix:** Reserved for CTAs only (buttons, conversion elements)
> - **"link" / "stormy-teal":** For non-CTA interactive elements (links, focus, hover)
> - **"rose-taupe" / "accent":** Decorative/informational accents (not text, not primary actions)
> - **Avoid new hues:** Use opacity variants of existing palette colors
---
## Accessibility Standards
### Text on Light Backgrounds
| Text Color | On White | On Parchment | WCAG Level |
| --------------------- | -------- | ------------ | ------------------------- |
| Deep Teal (#213C47) | 11.65:1 | 9.79:1 | AAA |
| Gunmetal (#343A40) | 11.51:1 | 9.67:1 | AAA |
| Slate Grey (#6A7F83) | 4.22:1 | 3.55:1 | AA-LG only |
| Berry (#A14567) | 5.46:1 | 4.59:1 | AA |
| Berry Dark (#8A3755) | 7.32:1 | 6.15:1 | AAA |
| Stormy Teal (#0E6F6A) | 6.00:1 | 5.04:1 | AA |
| Rose Taupe (#AC928A) | 2.85:1 | 2.39:1 | FAILS — never use as text |
### White Text on Dark Backgrounds
| Background | Contrast Ratio | WCAG Level |
| --------------------- | -------------- | ---------- |
| Deep Teal (#213C47) | 11.65:1 | AAA |
| Berry (#A14567) | 5.46:1 | AA |
| Berry Dark (#8A3755) | 7.32:1 | AAA |
| Stormy Teal (#0E6F6A) | 6.00:1 | AA |
### Key Rules
1. **Never use Rose Taupe for text** on any background — fails WCAG AA
2. **Slate Grey on warm backgrounds:** large text (18pt+) only. Use Gunmetal for small text.
3. **Deep Teal on Rosey Tint:** excellent contrast for info panels
4. **Berry on white:** 5.46:1 — passes AA for all text sizes (buttons)
5. Always verify new combinations with WebAIM Contrast Checker
---
## Common Mistakes
| Don't | Do Instead |
| -------------------------------- | ------------------------------------------------------------- |
| Use Rose Taupe as body text | Gunmetal for body text; Rose Taupe for accents/icons only |
| Berry for headlines | Deep Teal for headlines; Berry for CTAs only |
| Slate Grey small text on warm bg | Slate Grey for large text on warm; Gunmetal for small |
| Text on Rose Taupe bg | Use Rosey Tint for text surfaces; Rose Taupe for accents only |
| Remove focus outlines | Always maintain 2px Stormy Teal focus ring |
| New hues for charts | Opacity variants of existing palette (40%, 60%) |
| DM Serif Display for UI | DM Serif Display H1/H2 only; Inter for all UI |
| Hardcode button hover colors | Use --color-cta-hover token |
---
## Email Best Practices
- **Web-safe fallbacks:** DM Serif Display may not render in all clients — use Georgia fallback for display headlines
- **Dark mode:** Test in Gmail, Apple Mail, Outlook. Use transparent PNGs for logos. Avoid pure white backgrounds — use Parchment.
- **Mobile:** 44px minimum tap targets. 16px minimum body text to prevent auto-zoom. 8px minimum gap between tappable elements.
- **Accessibility:** Alt text for all images. Semantic HTML where supported. Test critical transactional emails with screen readers.
---
## Governance & Changelog
> **Document Authority**
>
> This page reflects **Brand Guide v2.0 FINAL — Canonical — Locked for Production Use** (February 2026). Any deviations require explicit approval from the founder.
>
### Changelog
| Version | Date | Changes |
| ------- | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 1.0 | Jan 21, 2026 | Initial launch edition. Deep Teal anchor, Mist Blue addition. |
| 1.1 | Jan 21, 2026 | Added typography, spacing, logo specs, data viz, UI states, tokens, email guidance. |
| 1.2 | Jan 21, 2026 | Quick Start section. Enhanced Mist Blue guardrails. Token hierarchy. RGB values. |
| 1.3 | Jan 21, 2026 | Contradiction fixes: Slate Grey WCAG label, Mist Blue text rule, typography defaults. |
| 1.4 | Jan 21, 2026 | Action Brown hover token. DM Serif Display for H1/H2. Typography tokens added. |
| 1.5 | Jan 21, 2026 | CANONICAL EDITION: Final Slate Grey/Mist Blue fixes. Physical applications clarified. |
| **2.0** | **Feb 19, 2026** | **v2.0 FINAL:** Berry (#A14567) replaces Action Brown as primary CTA. Berry Dark (#8A3755) replaces hover. Rose Taupe (#AC928A) and Rosey Tint (#E8DAD6) added as warm accents. Cool Steel, Mist Blue, and Mist Blue Light removed. Accent Teal renamed to Stormy Teal (same hex). Deployed to sv-website, sv-portal, Stripe Dashboard branding (#A14567 brand color, #213C47 accent), and Resend email templates (Welcome + Welcome Email Confirmation updated to v2.0 colors). |
### Implementation Checklist
- CSS/design tokens imported into codebase
- DM Serif Display + Inter loaded with correct weights
- H1/H2 using DM Serif Display; H3+ using Inter
- Button hover states using --color-cta-hover token
- Logo assets in all required formats
- Contrast ratios verified for all text/background combinations
- Focus states keyboard-tested across all interactive elements
- Rose Taupe usage audited (never as text)
- Slate Grey audited (large text only on warm backgrounds)
- Mobile breakpoints tested on warm backgrounds
### Deprecated Tokens (v1.x → v2.0)
| Old Token | New Token | New Hex |
| ----------------------- | --------------- | ------- |
| `sv-action-brown` | `berry` | #A14567 |
| `sv-action-brown-hover` | `berry-dark` | #8A3755 |
| `sv-accent-teal` | `stormy-teal` | #0E6F6A |
| `sv-cool-steel` | `slate-grey` | #6A7F83 |
| `sv-mist-blue` | (removed) | — |
| `sv-mist-blue-light` | `rosey-tint` | #E8DAD6 |
| `sv-deep-teal` | `charcoal-blue` | #213C47 |
| `sv-midnight` | `charcoal-blue` | #213C47 |
| `sv-terracotta` | `berry` | #A14567 |
| `sv-ember` | `berry-dark` | #8A3755 |
---
# Storage Valet - Database Schema Documentation
---
# Glossary
*Last updated: Feb 27, 2026*
---
## Business Terms
| Term | Definition |
| ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Storage Valet** | The company name and service. Premium concierge storage for Hudson County, NJ. |
| **Concierge Storage** | Storage service where the provider handles pickup and delivery (vs. self-storage). |
| **As Needed** | Approved language for describing delivery service. Implies 48-hour advance notice. **NEVER say "on-demand."** |
| **Service Area** | Geographic region where pickup/delivery is offered. Launch: 13 ZIP codes across 7 municipalities in Hudson County and the surrounding area (Hoboken, Jersey City, Weehawken, West New York, Union City, North Bergen, Edgewater). |
| **Out of Service Area** | Customer address outside launch ZIP codes. Soft gate, not rejected. |
| **Schedule-First Model** | Customer books appointment (Calendly) BEFORE selecting items. If items are pre-selected on the Dashboard, they auto-attach after booking; otherwise the customer selects items manually. |
| **48-Hour Lead Time** | Standard advance notice required for pickups/deliveries. |
| **White-Glove** | Premium, careful handling of customer belongings. |
---
## Customer Terms
| Term | Definition |
| ------------- | ------------------------------------------------------------------------------ |
| **Item** | A customer-owned belonging tracked in the inventory system. |
| **Inventory** | The collection of items a customer has cataloged. |
| **Portal** | Customer-facing web application at portal.mystoragevalet.com. |
| **Booking** | A scheduled service (pickup or delivery). Represented as `action` in database. |
| **QR Code** | Unique identifier for each item. Format: `SV-YYYY-NNNNNN`. |
---
## Item States
| State | Definition |
| ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| home | Item is at customer's address. |
| scheduled | Item is scheduled for a service (pickup or delivery). The type is determined by the booking's `pickup_item_ids` or `delivery_item_ids` arrays, not the item status itself. UI may display "Scheduled (pickup)" or "Scheduled (delivery)" as labels. |
| in_transit | Item is being moved (pickup or delivery in progress). |
| stored | Item is in Storage Valet facility. |
---
## Booking Statuses
| Status | Definition |
| ---------------------- | ---------------------------------------------- |
| `pending_items` | Appointment booked but items not yet selected. |
| `pending_confirmation` | Items selected, awaiting ops confirmation. |
| `confirmed` | Ops confirmed, scheduled for execution. |
| `in_progress` | Service being executed. |
| `completed` | Service finished. |
| `canceled` | Booking canceled. |
---
## Technical Terms
| Term | Definition |
| ---------------------------- | ----------------------------------------------------------------------------- |
| **Portal** | Customer-facing React web app. Hosted on Vercel at portal.mystoragevalet.com. |
| **Edge Functions** | Serverless functions on Supabase (Deno runtime). Handle webhooks. |
| **RLS (Row Level Security)** | PostgreSQL feature enforcing data isolation. Users only see their own data. |
| **Signed URLs** | Time-limited URLs for private storage bucket access. 1-hour expiry. |
| **Magic Links** | Email-based authentication (no passwords). Click link → authenticated. |
| **Webhook** | HTTP callback from external service (Stripe, Calendly). |
| **Idempotency** | Processing duplicate events only once via unique constraints. |
### Database Terms
| Term | Definition |
| -------------------- | --------------------------------------------------------- |
| **customer_profile** | Table storing user account data. 1:1 with auth.users. |
| **items** | Table storing customer inventory. |
| **actions** | Table storing service requests (pickups, deliveries). |
| **booking_events** | Audit table logging booking lifecycle events. |
| **inventory_events** | Audit table logging item movement events. |
| **SECURITY DEFINER** | PostgreSQL function that runs with creator's permissions. |
### Integration Terms
| Term | Definition |
| -------------------------- | ---------------------------------------------------- |
| **Stripe Checkout** | Hosted payment page for subscription signup. |
| **Stripe Customer Portal** | Hosted billing management page. |
| **Stripe Webhook** | HTTP callback from Stripe for payment events. |
| **Calendly** | Third-party scheduling tool for appointment booking. |
| **Calendly Webhook** | HTTP callback from Calendly for booking events. |
---
## Metrics
| Metric | Definition |
| ----------------------- | ----------------------------------------------------------------------- |
| **CUM** | Containers Under Management — count of active containers. |
| **IUM** | Items Under Management — count of cataloged items. |
| **DVUM** | Declared Value Under Management — sum of customer-declared item values. |
| **Contribution Margin** | Revenue minus variable costs per customer. |
| **CAC** | Customer Acquisition Cost — all-in cost to acquire a customer. |
| **LTV** | Lifetime Value — contribution margin × average customer lifetime. |
| **Golden Ratio** | (Revenue per sqft leased) ÷ (Cost per sqft leased). |
### Subscription Statuses
| Status | Definition |
| ---------- | --------------------------------------------- |
| `active` | Subscription current, full portal access. |
| `past_due` | Payment failed, grace period. Limited access. |
| `canceled` | Subscription ended. Read-only inventory. |
| `trialing` | Trial period. Full access. |
| `inactive` | Not yet subscribed. |
---
## Service Area ZIP Codes
| City | ZIP Codes |
| ----------------- | ----------------------------------------------- |
| **Edgewater** | 07020 |
| **Hoboken** | 07030 |
| **Jersey City** | 07302, 07304, 07305, 07306, 07307, 07310, 07311 |
| **North Bergen** | 07047 |
| **Union City** | 07087 |
| **Weehawken** | 07086 |
| **West New York** | 07093 |
**Total:** 13 active ZIPs (verified Jan 2026)
---
## Pricing
| Term | Value |
| --------------------- | --------------------------------------------------- |
| **Monthly Price** | $299/month |
| **Trial Period** | 14-day complimentary trial on all new subscriptions |
| **Setup Fee** | **NONE** — eliminated for simplicity |
| **Included Coverage** | $2,000 baseline insurance per subscription |
| **Plan Capacity** | 200 cubic feet included per subscription |
> **Trial Purpose**
>
> Gives customers time to explore the portal, create their digital inventory, and schedule their first pickup before billing begins.
>
### Container Catalog
| Container | Cubic Feet | Price |
| ----------------- | ---------- | ----- |
| **Storage Bin** | 1.25 | $15 |
| **Storage Tote** | 2.67 | $20 |
| **Storage Crate** | 5.56 | $25 |
### Referral Program
| Reward | Amount |
| ------------------- | ------------------ |
| **Referrer Reward** | $50 account credit |
| **Referee Reward** | $25 account credit |
---
## Service Types
| Type | Code | Definition |
| ---------------------- | -------------------- | -------------------------------------------------- |
| **Pickup** | `pickup` | Collect items from customer, transport to storage. |
| **Redelivery** | `redelivery` | Return stored items to customer. |
| **Container Delivery** | `container_delivery` | Deliver empty containers (future feature). |
---
## Repository Terms
| Term | Definition |
| -------------- | ------------------------------------------------------------------------------ |
| **sv-portal** | Customer portal React app repository. |
| **sv-edge** | Supabase Edge Functions repository. |
| **sv-db** | Database migrations repository. |
| **sv-docs** | Project documentation repository. |
| **sv-website** | Static marketing site (standalone, no Supabase). |
| **sv-wiki** | Internal team wiki (static HTML, hosted on Vercel at wiki.mystoragevalet.com). |
| **CLAUDE.md** | AI agent context file in each repository. |
---
## Brand Terms
| Term | Definition |
| ---------------- | ---------------------------------------------------------------------------------------------------------------------- |
| **sv-deep-teal** | Primary anchor color (#213C47). Headers, hero panels. |
| **sv-brown** | Primary CTA color — Berry (#A14567). Action buttons ONLY. Token name retained from v1.5; hex updated to Berry in v2.0. |
| **sv-accent** | Accent color (#0E6F6A). Links, focus rings. |
| **Token** | Named color variable (e.g., sv-deep-teal). |
---
# Notes & Reminders
*Last updated: Feb 27, 2026*
Jan 16, 2026Zach
### Welcome to SV-Wiki!
This is our team headquarters. If you're new here (including AI assistants), start with the to understand how this wiki works. Always check the AI Errors table before making assumptions.
onboardingimportantJan 15, 2026Zach
### Promo Codes for Partners
When sharing promo codes with partners, refer them to the for the current list of active codes and their terms.
promosFeb 27, 2026— Claude Opus 4.6
**Auto-attach fix shipped (, closes ).** When customers select items on the Dashboard before opening the booking modal, those items are now automatically attached after the Calendly booking completes via `updateBookingItems()`. Includes a run-once guard (`actionHandledRef`) to prevent race conditions from overlapping polls, visible attaching/success UI states, and toast fallback on failure with redirect to manual flow. Two-file change: `BookingModal.tsx` + `Dashboard.tsx`.
Feb 19, 2026— Claude Opus 4.6
**Brand v2.0 deployed to Stripe Dashboard and Resend email templates.** Stripe branding updated: brand color #6B4E3D → #A14567 (Berry), accent #213C47 (Deep Teal). Resend: 2 of 5 templates updated — Welcome Email (button color), Welcome Email Confirmation (header, button, CSS). 3 templates (Pickup Complete, Delivery Complete, Payment Failed) had no brand colors to update. Remaining manual items: Supabase auth email templates (magic link, recovery) still on defaults; Calendly scheduling page still default theme. Handoff file created at `sv-portal/docs/brand-v2-system-audit-feb2026.md` for portal v2 agent.
Feb 19, 2026— Claude Opus 4.6
**CORS + auth fix deployed for item attachment and booking cancellation.**`update-booking-items` and `booking-cancel` edge functions were broken (CORS missing `apikey` header; auth calling `getUser()` without token). Fixed in sv-edge commit `a32c6d3`. Resend API key consolidated: old "Supabase Integration" key deleted from Resend dashboard; `sv-prod` is now the sole active key. 1Password "Resend API" item updated so both `credential` and `api_key` fields contain the `sv-prod` key. Supabase secret `RESEND_API_KEY` re-pushed from the corrected 1Password field. Full E2E test passed: signup → item create → booking → attach items → cancel → revert. Known gap: checkbox item selection does not auto-attach to Calendly bookings ().
Feb 11, 2026— Claude Opus 4.6 + Codex 5.3
**Track A Stabilization deployed.** Multi-agent review (Opus + Codex) identified and fixed 6 issues. Two P0 security fixes: RLS `actions` UPDATE policy restored status constraint; `booking_events` cross-tenant leak closed. Service area ZIPs unified to DB-backed `is_valid_zip_code()`. Portal booking mutations now route through edge functions. Profile cache keys aligned. 4 DB migrations applied (`20260211000001–04`), 13 edge functions redeployed, portal deployed at commit `2f0350f`. See for details.
Jan 18, 2026— Zach
**Insurance coverage updated:** Included coverage reduced from $3,000 to $2,000 effective Jan 28, 2026 (pre-launch, no customer impact). DB view `v_user_insurance` updated from 300000 to 200000 cents.
Jan 16, 2026— Zach
Wiki renamed from SVOS to SV-Wiki. Update any bookmarks or references. Vercel project will be renamed shortly.
Jan 15, 2026— Zach
Added 22 promo codes to the wiki. All codes verified against live Stripe data. Revenue impact calculations included.
Jan 15, 2026— Zach
Remember: We do NOT charge a $99 setup fee anymore. Trial is 14 days complimentary. AI assistants keep getting this wrong — always double-check.
✓
**Set up wiki custom domain** — wiki.mystoragevalet.com (kept sv-wiki project name)
Zach + Claude Code — Jan 18✓
**Create promo codes page** with all 22 active Stripe codes
Claude Code — Jan 15✓
**Verify wiki data** against live Supabase and Stripe sources
Claude Code — Jan 15
### Essential Reading (15 min)
1. — What we do and why
2. — Our promise to customers
3. — Codes you can share with leads
### Reference As Needed
- — Storage Valet terminology
- — Colors, fonts, tone of voice
- — Why we made certain choices
### Key Facts to Remember
- Pricing: **$299/month**, single tier
- Trial: **14 days free**
- Service area: **Hudson County, NJ** (13 ZIP codes)
- Lead time: **48 hours** for pickup scheduling
### 📝 How to Add Notes
This page is edited manually. To add a note:
1. Edit `~/code/sv-wiki/notes.html` directly, or
2. Ask Claude Code: "Add a note to the SV-Wiki notes page saying..."
3. Commit and push to deploy
**Tip:** Keep notes concise. Move detailed information to the appropriate doc page.
---
# Decision Log
*Last updated: Mar 4, 2026*
### Why Document Decisions?
Storage Valet has gone through 8+ iterations. Without a decision log, we risk revisiting settled questions or forgetting hard-won lessons. This log captures the **what**, **when**, and **why** for key decisions.
**For AI assistants:** Before proposing changes that contradict documented decisions, reference this log and acknowledge the existing rationale.
DEC-001Jan 2026
### 14-Day Complimentary Trial (Replaced Setup Fee)
**Decision:** Remove the $99 setup fee entirely and replace with a 14-day complimentary trial for all new subscriptions.
**Core problem:** Customers were being asked to pay a significant upfront amount ($99 + $299) for a service they couldn't immediately experience. Our 48-hour lead time means even in the best case — inventory already created, first available slot booked — customers wait at least two days before their first pickup. In practice, that slot may not be available, extending the wait further.
**Rationale:** The trial solves this friction. Customers can register, explore the portal, create their digital inventory, and schedule their first pickup — all before billing begins. By the time they're charged, they've already experienced the service. Simpler to explain ("try free for 14 days") than justify an upfront fee for delayed service.
**Reversibility:** We may revisit the setup fee later once we have established trust and reviews. This decision is for the immediate future, not permanent.
**Previous approach:** $99 one-time setup fee charged at signup.
**Impact:** All new customers start with trial. Existing promo codes like SETUPFREE are now redundant but kept for backwards compatibility.
Owner: ZachActiveDEC-002Oct 2025
### Single-Tier Pricing at $299/month
**Decision:** One pricing tier only — $299/month. No "basic" or "premium" tiers.
**Rationale:** Simplicity. Multi-tier pricing creates decision paralysis and support complexity. $299 is premium enough to filter for serious customers while accessible enough for target market (urban professionals).
**Considered alternatives:** $199/$299/$399 tiers with different storage limits. Rejected due to operational complexity.
Owner: ZachActiveDEC-003Sep 2025
### Supabase as Core Backend
**Decision:** Use Supabase (PostgreSQL + Auth + Edge Functions) as the single backend platform.
**Rationale:** After 8 failed attempts with Airtable/Zapier/no-code tools, we needed a real database. Supabase provides PostgreSQL, authentication, and serverless functions in one platform. Direct SQL access enables complex queries without middleware.
**Previous approaches:** Airtable, Notion databases, custom Zapier workflows — all hit scalability or complexity walls.
Owner: ZachActiveDEC-004Jan 2026
### Landing Page on Vercel (Not Webflow/Framer)
**Decision:** Host marketing site (sv-website) on Vercel as static HTML.
**Rationale:** Faster load times, free hosting, same deployment workflow as portal. Webflow/Framer added complexity without proportional benefit.
**Note:** AI systems frequently mis-reference this as "Webflow" or "Framer" — INCORRECT. Always Vercel.
Owner: ZachActiveDEC-005Jan 2026
### SV-Wiki as Static HTML
**Decision:** Build internal wiki as static HTML files deployed to Vercel, not a CMS or Notion.
**Rationale:** AI assistants can read HTML files directly from local filesystem. Git provides versioning. No vendor lock-in. Claude Code can edit files directly.
**Trade-off:** Manual editing required (no WYSIWYG), but acceptable for technical founder + AI workflow.
Owner: ZachActiveDEC-015Feb 2026
### sv-website Code Quality Review & Vercel Analytics Fix
**Decision:** Multi-agent review (Claude Opus 4.6 + OpenAI Codex 5.3) audited all 8 sv-website HTML files. 7 code quality fixes applied, plus a runtime analytics bug discovered and fixed by Codex during verification.
**Changes:**
- Replaced `favicon.png` references with `favicon.svg` (signup pages)
- Fixed "265 days" typo to "365 days" (partnerships page)
- Converted all `.html` hrefs to clean URLs (leveraging Vercel `cleanUrls: true`)
- Removed dead social media links (placeholder `href="#"` in footers)
- Added `requestAnimationFrame` scroll throttling + `passive: true` on 5 pages
- Added `for`/`id` label associations and `autocomplete` attributes to signup form
- Fixed Vercel analytics API: `window.va?.track()` (npm-only) replaced with `window.va?.('event', {name, data})` (script-tag API)
**Rationale:** The analytics bug caused `TypeError` on every scroll-depth event in production. Root cause: `window.va` is a function (not an object) when loaded via script tag. `window.va?.track` evaluates to `undefined` because the function has no `.track` property, causing the call to throw.
Owner: ZachActiveDEC-017Mar 2026
### Platform Operations Toolkit (sv-audit / sv-hygiene / sv-health / sv-check)
**Decision:** Introduce a tiered platform operations toolkit consisting of four commands that provide session integrity verification, repository hygiene, infrastructure observability, and unified platform checks.
**Commands:**
- `sv-audit` — Session integrity and repository hygiene detection. Checks git status across all 6 repos, detects stale PRs (>7 days), orphan remote branches (>30 days), secrets tripwire, root directory policing, and 1Password socket cleanup. Non-destructive.
- `sv-hygiene` — Safe cleanup of remote branches already merged into main. Never deletes unmerged work. Intended for weekly maintenance or after audit flags stale branches.
- `sv-health` — Platform observability: Supabase migration drift, Vercel deployment failures, GitHub Actions failures, and Stripe webhook processing backlog.
- `sv-check` — Unified platform verification. Runs audit, auto-triggers hygiene cleanup if audit reports ATTENTION, then runs health checks. Single command for full platform status.
**Design principles:**
- Audits remain non-destructive (read-only, except 1Password socket cleanup)
- Cleanup actions are explicit and separate (`sv-hygiene` is opt-in, not automatic)
- Scripts reference canonical git-tracked paths (`~/code/sv-docs/scripts/`) rather than symlinks
- The system provides both session-start observability (bootstrap snippet flags stale PRs) and session-end observability (audit script)
**Rationale:** PR #11 in sv-portal went undetected for 35 days because the audit script only checked local git state, never GitHub. This toolkit closes that gap and adds infrastructure health monitoring. The tiered design (detect, clean, verify) prevents accidental destructive actions while enabling comprehensive platform oversight.
**Canonical locations:** All scripts live in `~/code/sv-docs/scripts/` (git-tracked). Shell shortcuts defined in `~/.zshrc`. A convenience symlink at `~/code/sv-final-audit.sh` exists for manual use but is not a dependency for any script.
Owner: ZachActiveDEC-006Nov 2025
### 48-Hour Lead Time for Pickups
**Decision:** Standard lead time for pickup scheduling is 48 hours.
**Rationale:** Balances customer convenience with operational reality. Zach (as solo operator) needs buffer to coordinate vehicle, route efficiently, and handle day-of issues. Rush service (24hr) may be added later at premium.
Owner: ZachActiveDEC-007Dec 2025
### Customer Self-Catalogs Items
**Decision:** Customers photograph and catalog their own items via the portal. We do NOT catalog for them.
**Rationale:** Scalability. If SV staff had to photograph/catalog every item, it would add significant time per pickup. Customer knows their items best. Enables "search your storage" feature.
**Common misconception:** AI assistants often describe SV as "we photograph and catalog your items" — INCORRECT.
Owner: ZachActiveDEC-008Jan 2025
### Brand v2.0 Color Palette
**Decision:** Primary colors are Deep Teal (#213C47) and Berry (#A14567). Accent is Stormy Teal (#0E6F6A). Updated Feb 2026: Berry replaced Action Brown (#6B4E3D) as primary CTA. Rose Taupe (#AC928A) and Rosey Tint (#E8DAD6) added. Cool Steel and Mist Blue removed.
**Rationale:** Premium, trustworthy feel. Berry for CTAs creates warmth and confidence without aggressive red/orange. Teal provides professional anchor.
**See:** for full specification.
Owner: ZachActiveDEC-009Sep 2025
### Hudson County as Launch Market
**Decision:** Launch in Hudson County, NJ and the surrounding area (13 ZIP codes across 7 municipalities) before expanding.
**Rationale:** Dense urban market with high-rise apartments (limited storage). Affluent professionals willing to pay for convenience. Geographically compact for efficient routing.
**ZIP codes:** 07020, 07030, 07047, 07086, 07087, 07093, 07302, 07304, 07305, 07306, 07307, 07310, 07311
Owner: ZachActiveDEC-010Oct 2025
### $2,000 Included Insurance Coverage
**Decision:** Every subscription includes $2,000 of item protection at no additional cost. (Reduced from $3,000 pre-launch, Jan 2026.)
**Rationale:** Trust signal. Customers storing valuables need confidence. $2,000 covers most household items without complex appraisal process. Higher coverage tiers may be offered later as add-on.
Owner: ZachActiveDEC-011Feb 2026
### 1Password CLI as Sole Secrets Source of Truth
**Decision:** All production secrets are stored in the 1Password "Storage Valet" vault and accessed exclusively via `op read` / `op run`. No secrets in `~/.zshrc`, environment files, MCP configs, or inline commands.
**Rationale:** Audit on Feb 6, 2026 discovered that Claude Code's permission caching had persisted plaintext credentials (Stripe secret key, DB password, Calendly PAT, service role key, webhook secret) in `~/.claude/settings.json` and `settings.local.json`. A subsequent audit (Feb 27, 2026) found the Calendly PAT stored in plaintext in `~/.claude.json` MCP server config; remediated with an `op read`-backed launcher script. Root cause: approving commands with inline secrets caused the literal command string to be cached, and MCP `env` blocks required literal values. Migration to `op read` and launcher scripts eliminates these accumulation paths entirely.
**Previous approach:** Secrets exported as plaintext in `~/.zshrc` and passed inline in CLI commands.
Owner: ZachActiveDEC-012Feb 2026
### Defer Supabase Service Role Key Rotation
**Decision:** Do not rotate the Supabase service role key / JWT signing secret as part of the Feb 2026 credential rotation. Schedule as a separate planned operation with a maintenance window.
**Rationale:** Rotating the JWT signing secret invalidates all 15 edge functions simultaneously until they pick up the new key. The exposure was local-only (plaintext in a settings file on Zach's machine, now cleaned). The risk of production downtime outweighs the risk of a local-only exposure that has been remediated.
**Revisit:** If evidence of exfiltration emerges, rotate immediately with coordinated edge function redeployment.
Owner: ZachActiveDEC-014Feb 2026
### Track A Stabilization: RLS Hardening & Service Area Unification
**Decision:** Multi-agent code review (Claude Opus 4.6 + OpenAI Codex 5.3) identified and fixed 6 issues across sv-db, sv-edge, and sv-portal. Two P0 security gaps were closed: an RLS regression on `actions` UPDATE/DELETE (status constraint dropped in migration 0008) and a cross-tenant data leak on `booking_events` (orphan rows with `action_id IS NULL` visible to all authenticated users).
**Rationale:** The RLS regression allowed customers to mutate non-pending actions at the database level. The booking_events leak exposed metadata across tenants. Both were unintentional regressions introduced when adding staff override logic. Additionally, service area ZIP codes were fragmented across 4 sources with 3 different counts; these were unified to a single DB-backed function (`is_valid_zip_code`) and the portal's `SUPPORTED_ZIP_CODES` constant.
**Scope:** 4 DB migrations (`20260211000001–04`), stripe-webhook redeployed with DB-backed ZIP validation, portal booking mutations routed through edge functions, profile cache keys unified.
Owner: ZachActiveDEC-013Feb 2026
### Claude Code Bypass Permissions with Secret-Free Workflow
**Decision:** Keep Claude Code in `bypassPermissions` mode (full autonomy, no approval prompts). This is safe because secrets never appear in commands — they flow through `op read` — so there is nothing for the permission cache to capture.
**Rationale:** Approval prompts did not prevent the original secret exposure; they *caused* it by caching approved command strings verbatim. With secrets removed from commands, bypass mode is purely a productivity toggle. An automated tripwire in `sv-final-audit.sh` detects regressions.
**Previous approach:** Granular permission rules that accumulated to 500+ entries with embedded credentials.
Owner: ZachActiveDEC-016Feb 2026
### Terms of Service & Privacy Policy Accuracy Review
**Decision:** Deep accuracy review of customer-facing legal documents (Terms of Service, Privacy Policy) against current business facts and industry benchmarks. Four substantive changes applied.
**Changes:**
- **Declared value threshold:** $10,000 → $2,000. Aligned with included insurance coverage amount — no reason to require disclosure at 5x the coverage limit.
- **Post-termination retrieval:** 30 → 45 days. Matches Clutter (market leader). NJ lien law (N.J.S.A. 2A:44-187) requires ~75-90 days minimum statutory process, so 45 days is well within legal bounds while encouraging timely retrieval.
- **Photo deletion:** Hard 90-day calendar deadline → purpose-based deletion ("reasonable period" with exceptions for claims, disputes, regulatory obligations). Avoids premature evidence destruction. Follows FTC enforcement patterns (Everalbum).
- **Signup cancel page:** "saved your spot" → "pick up right where you left off." Previous copy was technically false (Stripe saves nothing on cancel). New copy follows Stripe's own sample pattern.
**Rationale:** Industry research across valet competitors (Clutter, STASH), traditional self-storage (CubeSmart, Public Storage, Extra Space), NJ statutes, and FTC enforcement history. Goal: match market-standard terms while avoiding unnecessary legal exposure.
Owner: ZachActive
### 📝 How to Log a Decision
When making a significant business decision:
1. Add a new `.decision-card` to the appropriate category
2. Include: Decision ID, Date, Title, Decision statement, Rationale, Previous approach (if applicable)
3. Mark status as `active`, `superseded`, or `under-review`
**When to log:** Pricing changes, technology choices, operational policies, anything you might need to explain to an investor or new hire.
---
# AI Reference
*Last updated: Mar 4, 2026*
---
## Common AI Errors
> **Critical: Correct These Immediately**
>
> AI systems frequently get these facts wrong. If you reference any of these incorrectly, correct immediately.
>
| Topic | CORRECT ✓ | WRONG ✗ |
| ---------------------------------- | ----------------------------------------------------------------------------------------------------- | -------------------------------------------------------- |
| **Setup fee** | None — eliminated | $99 setup fee |
| **Trial** | 14-day complimentary | No trial |
| **Landing page** | Vercel (sv-website) | Webflow, Framer |
| **Item cataloging** | Customer photographs their own items | "We photograph and catalog your items" |
| **Pricing tiers** | Single tier: $299/month | Multiple tiers ($199/$299/$399) |
| **Insurance coverage** | $2,000 included | No coverage / $1,000 coverage |
| **Item removal from bookings** | Customers can remove items from pending bookings; items revert appropriately | "Customers can't remove individual items once scheduled" |
| **Confirmed booking cancellation** | Customers can cancel confirmed bookings; pickup items revert to home, delivery items revert to stored | "Confirmed bookings can't be canceled by customers" |
---
## Data Hygiene Notes
> **System-Level vs Customer-Level Data**
>
> Some tables (e.g., `booking_events`) may contain system-level webhook logs (such as Calendly events) that are not tied to a user or action. These records do not represent customer data and may exist even when the customer database is otherwise empty. Do not interpret null `user_id` or `action_id` records as orphaned customer data.
>
---
## How to Use SV-Wiki
### For AI Systems
1. **Check the error table first** — These are persistent, common mistakes
2. **Read the relevant file(s)** before proposing solutions
3. **Treat content as authoritative** — Do not invent policy or features
4. **Reference specific sections** in your responses
5. **Ask clarifying questions** when SV-Wiki is silent on a topic
### File Access
AI assistants can access SV-Wiki content via:
- **Local files:**`~/code/sv-wiki/*.html`
- **Live URL:**`wiki.mystoragevalet.com`
- **AI index:**`wiki.mystoragevalet.com/llms.txt` — page listing with descriptions
- **Full content:**`wiki.mystoragevalet.com/llms-full.txt` — all pages as markdown
### When Information Conflicts
If you encounter conflicting information between sources:
1. **SV-Wiki wins** — This is the canonical source
2. Notify Zach of the conflict
3. Do not silently use outdated information
---
## AI Access Files
SV-Wiki provides machine-readable access via two generated files at the site root:
| File | URL | Purpose |
| ----------------- | ---------------- | ---------------------------------------------------------------------------- |
| **llms.txt** | `/llms.txt` | Page index with titles, URLs, and one-line descriptions. Use for discovery. |
| **llms-full.txt** | `/llms-full.txt` | Complete wiki content as a single markdown document. Use for bulk ingestion. |
### Regenerating
Both files are produced by `generate-llms.py` (Python 3, stdlib only). Run from the wiki root:
```
python3 generate-llms.py
```
Re-run this script before committing any HTML content changes. The generated files are committed to git and served by Vercel.
### Copy as Markdown
Each content page includes a **"Copy as Markdown"** button in the page header. This copies the page content to clipboard as clean markdown — useful for pasting into AI chat interfaces.
---
## Wiki Sync Agent
A Claude Code agent that cross-references recent code changes against wiki content to flag pages that may be stale.
| Detail | Value |
| ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| **Location** | `~/.claude/agents/wiki-sync.md` (local, not in repo) |
| **When to run** | End of development sessions, after schema/pricing/feature changes |
| **What it does** | Reads git log across all 6 repos (sv-portal, sv-edge, sv-db, sv-docs, sv-website, sv-wiki); maps changes to wiki pages; reports staleness |
| **Complements** | `sv-final-audit.sh` (git hygiene + GitHub hygiene) — this agent handles content accuracy |
> **Local File**
>
> The agent definition lives at `~/.claude/agents/wiki-sync.md` and is **not** committed to any repository. If setting up a new machine, copy it from an existing environment or recreate it.
>
---
## Document Status Guide
Each document has a status badge indicating its authority level:
| Badge | Meaning | How to Use |
| ------------- | -------------------------------------- | ------------------------------------------ |
| Authoritative | Verified, current, and binding | Treat as ground truth. Do not contradict. |
| Directional | Intent is clear but details may evolve | Use for guidance. Implementation may vary. |
| Archival | Historical reference only | Do not cite as current policy. |
---
## Canonical Authority
> **SV-Wiki Supersedes All Other Sources**
>
> If there is conflict between this documentation and any other source, SV-Wiki governs.
>
### Documentation Authority Map
The file `sv-docs/DOC_AUTHORITY.md` defines the source of truth for every documentation category and establishes precedence when sources conflict:
1. **Code / Schema** (sv-db migrations)
2. **sv-wiki** (business, architecture, operations)
3. **CLAUDE.md** (agent rules, deployment, security)
4. **sv-docs runbooks** (operational guidance)
5. **sv-docs archive** (historical, read-only)
A companion file `sv-docs/DOC_DRIFT_GUARD.md` defines rules preventing documentation duplication across repos. Both files together form the documentation governance layer for multi-agent collaboration.
### Sources That SV-Wiki Supersedes
- Bear notes exports
- Notion pages
- Slack messages
- Email threads
- Previous AI context files
- ChatGPT/Claude conversation history
### Updating the Wiki
If you identify information that needs to be updated:
1. Notify Zach of the discrepancy
2. Propose the specific change
3. Wait for confirmation before treating new information as authoritative
---
## Wiki Page Index
Complete list of all SV-Wiki pages with descriptions. Use this as a sitemap for discovery.
| Page | URL | Contents |
| ---------------- | ------------------- | --------------------------------------------------------------------------------------------------------------------------- |
| **Home** | `index.html` | Quick facts, documentation index, canonical authority statement |
| **Business** | `business.html` | What SV is, pricing ($299/mo), market (7 municipalities, 13 ZIPs in Hudson County), unit economics, competitive positioning |
| **Customer** | `customer.html` | Customer promise, portal functionality, item lifecycle, state machine, user journeys |
| **Operations** | `operations.html` | Service model, scheduling discipline (48hr lead time), ops metrics, go-to-market |
| **Finance** | `finance.html` | Stripe billing, pricing model, tax strategy, financial model, accounting |
| **Legal** | `legal.html` | Entity structure (DE LLC), EIN documentation, customer-facing legal documents (ToS key terms, Privacy Policy provisions) |
| **Insurance** | `insurance.html` | $2,000 included coverage, customer protection policies |
| **Partnerships** | `partnerships.html` | B2B sales strategy, property partnerships, Storage Drives, partner onboarding |
| **Promos** | `promos.html` | All 22 active Stripe promo codes, revenue impact calculations, usage guidelines |
| **Technology** | `technology.html` | Architecture (Supabase + Vercel + Stripe + Calendly), repositories, deployment, integrations |
| **Database** | `database.html` | PostgreSQL schema, table definitions, ERD, migrations, RLS policies |
| **Brand** | `brand.html` | Colors (Deep Teal, Berry, Stormy Teal), typography (DM Serif + Inter), design tokens, usage rules |
| **Glossary** | `glossary.html` | Business, customer, item state, technical, and operational terminology |
| **Notes** | `notes.html` | Team announcements, quick notes, action items, onboarding checklist |
| **Decisions** | `decisions.html` | Key business and technical decisions with rationale and timestamps |
| **AI Reference** | `ai-reference.html` | Common AI errors table, usage guide, document status system, this page index |
---