Home Business Customer Operations Technology Database Brand Glossary Promos Notes Decisions

On This Page

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)
β”‚   β”œβ”€β”€ customers             # Denormalized Stripe mapping
β”‚   └── webhook_events        # Webhook idempotency log
β”‚
β”œβ”€β”€ auth (Supabase-managed)
β”‚   └── users                 # Authentication (magic links)
β”‚
└── storage (Supabase-managed)
    └── buckets               # File storage (item-photos)

Current Status (Jan 2026)

SchemaStatusNotes
publicACTIVE PRODUCTION8 customers, 8 items, 5 bookings
svPLANNED CANONICAL0 records - not yet migrated to
billingACTIVEStripe webhook log

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)

ColumnTypeDescription
user_idUUID (PK, FK)Links to auth.users
emailTEXT (UNIQUE)Customer email
stripe_customer_idTEXT (UNIQUE)Stripe customer ID
subscription_idTEXTActive subscription ID
subscription_statusENUMinactive, active, past_due, canceled, trialing, etc.
last_payment_atTIMESTAMPTZLatest successful payment
full_nameTEXTCustomer name
phoneTEXTContact phone
delivery_addressJSONB{street, unit, city, state, zip}
out_of_service_areaBOOLEANSoft 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

ColumnTypeDescription
idUUID (PK)Item identifier
user_idUUID (FK)Owner
labelTEXT (NOT NULL)Item name
descriptionTEXT (NOT NULL)Item description
categoryTEXTOptional category
statusENUMhome, in_transit, stored, scheduled
photo_pathsTEXT[]Multi-photo array (1-5)
qr_codeTEXT (UNIQUE)Format: SV-YYYY-NNNNNN
cubic_feetNUMERIC (GENERATED)Auto-calculated
tagsTEXT[]Searchable keywords

actions

Purpose: Service requests (pickup, redelivery, container delivery)

ColumnTypeDescription
idUUID (PK)Action identifier
user_idUUID (FK)Customer
service_typeENUMpickup, redelivery, container_delivery
statusENUMpending_items, pending_confirmation, confirmed, in_progress, completed, canceled
pickup_item_idsUUID[]Items to pick up
delivery_item_idsUUID[]Items to deliver
calendly_event_uriTEXT (UNIQUE)Calendly event identifier
scheduled_startTIMESTAMPTZCalendly booking start

service_areas

Purpose: Serviceable ZIP codes

ColumnTypeDescription
zipTEXT (PK)ZIP code
cityTEXTCity name
stateTEXTState code

Billing Tables

billing.customers

Purpose: Denormalized Stripe customer mapping

ColumnTypeDescription
user_idUUID (PK, FK)User identifier
stripe_customer_idTEXT (UNIQUE)Stripe customer ID

billing.webhook_events

Purpose: Stripe webhook idempotency log

ColumnTypeDescription
idBIGSERIAL (PK)Auto-increment ID
event_idTEXT (UNIQUE)Stripe event.id (idempotency key)
event_typeTEXTe.g., checkout.session.completed
payloadJSONBFull Stripe event object
processed_atTIMESTAMPTZProcessing 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
);

RLS Policies

customer_profile

items

actions

inventory_events / booking_events

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: 33 migrations applied (verified Jan 2026)

Key migrations include:

Full migration list: ~/code/sv-db/migrations/