PostgreSQL 17.6.1 (Supabase-managed) β Tables, relationships, and migrations
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)
β βββ customers # Denormalized Stripe mapping
β βββ webhook_events # Webhook idempotency log
β
βββ auth (Supabase-managed)
β βββ users # Authentication (magic links)
β
βββ storage (Supabase-managed)
βββ buckets # File storage (item-photos)
| Schema | Status | Notes |
|---|---|---|
public | ACTIVE PRODUCTION | 8 customers, 8 items, 5 bookings |
sv | PLANNED CANONICAL | 0 records - not yet migrated to |
billing | ACTIVE | Stripe webhook log |
The sv schema represents the target architecture. Migration from public to sv is planned post-launch.
ββββββββββββββββββββββββ
β 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β
ββββββββββββββββββββββββ
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 |
Users CANNOT update subscription_status, stripe_customer_id, subscription_id directly.
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 |
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 |
Purpose: Serviceable ZIP codes
| Column | Type | Description |
|---|---|---|
zip | TEXT (PK) | ZIP code |
city | TEXT | City name |
state | TEXT | State code |
Purpose: Denormalized Stripe customer mapping
| Column | Type | Description |
|---|---|---|
user_id | UUID (PK, FK) | User identifier |
stripe_customer_id | TEXT (UNIQUE) | Stripe customer ID |
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 |
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() );
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
);
SELECT * FROM items WHERE user_id = auth.uid() ORDER BY created_at DESC;
SELECT * FROM items
WHERE user_id = auth.uid()
AND status = 'stored'
AND (
label ILIKE '%keyword%'
OR description ILIKE '%keyword%'
OR 'keyword' = ANY(tags)
);
SELECT * FROM actions
WHERE user_id = auth.uid()
AND status IN ('pending_items', 'pending_confirmation')
ORDER BY scheduled_start ASC;
Total: 33 migrations applied (verified Jan 2026)
Key migrations include:
0001_init β Core schema0004_phase1_inventory_enhancements β Multi-photo, status, batch ops0005_create_sv_schema β Create sv domain schema20251112000005_billing_status_tracking β Payment timestamps20251201062222_create_service_areas_table β Service areas20260107220309 β Billing v2 trial columnsFull migration list: ~/code/sv-db/migrations/