Overview
Haggle uses Supabase (managed PostgreSQL) for persistent storage. The schema is minimal by design - only service providers are persisted, while job sessions remain in-memory.The database uses PostgreSQL-specific features like BIGSERIAL, NUMERIC, and Row Level Security (RLS).
Schema Diagram
Tables
providers
Stores service provider information discovered via AI search and negotiation outcomes.Column Reference
Auto-incrementing primary key. Uses BIGSERIAL for high-volume inserts.
Business name of the service provider (e.g., “Reliable Plumbing Services”).
Provider’s phone number in various formats:
(408) 555-0101408-555-0101408.555.0101
r'\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}'Formatted paragraph of user’s answers to clarifying questions.Example:Used in AI agent prompt for context-aware negotiation.
Full address provided by user (e.g., “123 Main St, San Jose, CA 95126”).Not used for search (zip_code is used instead), but stored for record-keeping.
5-digit ZIP code used for provider search.
Indexed for fast filtering by location.
Maximum budget user is willing to pay. Used by AI agent during negotiation.Example values:
250.00(user set $250 limit)NULL(“no_limit” option selected)
UUID linking provider to the originating job session.Format: UUID v4 (e.g.,
550e8400-e29b-41d4-a716-446655440000)Jobs are NOT stored in the database - only in memory. This field is for grouping providers.
Initial price estimate from provider (if obtained during search).
AI-formatted problem statement in second person.Examples:
"your toilet needs to be fixed""your lawn needs to be mowed""your faucet is leaking"
format_problem_statement() in services/grok_llm.pyFinal agreed-upon price after AI negotiation.Extraction logic:
NULL if no agreement reached.Current status of the negotiation call.Valid values:
pending- Not yet calledin_progress- Currently on callcompleted- Call finished with agreementfailed- Call finished without agreement
Full conversation transcript from the AI agent call.Format:Captured real-time from Grok Realtime API events:
conversation.item.input_audio_transcription.completed(user)response.audio_transcript.done(assistant)
Timestamp when provider record was created (defaults to NOW()).Uses
TIMESTAMP WITH TIME ZONE for timezone-aware storage.Python Model
TheProvider class in db/models.py maps to the database table:
Database Operations
Common Queries
Get all providers for a job
Get all providers for a job
Get providers by location
Get providers by location
idx_providers_zip_codeGet completed negotiations under budget
Get completed negotiations under budget
Update call status after negotiation
Update call status after negotiation
Migration Files
The database schema is managed through SQL migration files:- Initial Schema
- Call Fields
- Updates
File:
supabase_migration.sqlCreates the base providers table with core fields:- id, service_provider, phone_number
- context_answers, house_address, zip_code
- max_price, job_id, minimum_quote, problem
- created_at
Data Lifecycle
Provider Discovery
OpenAI Search finds providers → Parsed → Inserted into
providers table with call_status = 'pending'Call Completion
- Extract negotiated price from transcript using LLM
- Update
call_status = 'completed'or'failed' - Save
negotiated_priceand fullcall_transcript
Performance Optimization
Indexing Strategy
All frequently queried columns are indexed:
job_id(most common query)zip_code(location filtering)service_provider(text search)house_address(address lookup)
Data Types
BIGSERIALfor high-volume insertsNUMERIC(10, 2)for precise money valuesTEXTfor unlimited string storageTIMESTAMPTZfor timezone handling
Connection Pooling
Supabase automatically handles connection pooling:
- Max connections: 100+ (varies by plan)
- Idle timeout: 10 minutes
- Python client reuses connections
Minimal Writes
Only 2 write operations per provider:
- INSERT on discovery
- UPDATE after call completion
Security
Row Level Security (RLS)
Current Policy (Development)
Data Privacy
PII Storage
PII Storage
Stored PII:
- House addresses
- Phone numbers (providers, not users)
- Call transcripts (may contain personal info)
- Encrypt
call_transcriptat application level - Hash or tokenize addresses
- Implement data retention policy (delete after 30 days)
- Add GDPR compliance endpoints (data export, deletion)
API Key Security
API Key Security
Supabase keys are stored in environment variables:Best practices:
- Use
service_rolekey only on backend - Rotate keys periodically
- Never commit keys to Git
- Use secret management (AWS Secrets Manager, etc.)
Backup & Recovery
Supabase provides automatic daily backups:Automatic Backups
- Free tier: 7 days of backups
- Pro tier: 30 days of backups
- Point-in-time recovery (PITR) on Pro+
Future Schema Extensions
Users Table
Users Table
Track user accounts and job history:
Jobs Table
Jobs Table
Persist job sessions to database:
Call Analytics
Call Analytics
Track negotiation metrics: