Spaces:
Running
Running
| -- ============================================================ | |
| -- AI Client Acquisition System β Supabase Schema | |
| -- Run this in Supabase SQL Editor | |
| -- ============================================================ | |
| -- Enable pgcrypto for UUID generation | |
| CREATE EXTENSION IF NOT EXISTS "pgcrypto"; | |
| CREATE EXTENSION IF NOT EXISTS "pg_trgm"; -- for fuzzy name matching | |
| -- βββ ENUMS ββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| CREATE TYPE company_status AS ENUM ( | |
| 'discovered', 'researching', 'profiled', | |
| 'qualified', 'nurture', 'archived', 'suppressed' | |
| ); | |
| CREATE TYPE contact_status AS ENUM ( | |
| 'found', 'email_verified', 'email_invalid', | |
| 'linkedin_only', 'suppressed' | |
| ); | |
| CREATE TYPE lead_tier AS ENUM ('hot', 'warm', 'nurture', 'archive'); | |
| CREATE TYPE outreach_channel AS ENUM ('email', 'linkedin'); | |
| CREATE TYPE outreach_status AS ENUM ( | |
| 'queued', 'sent', 'opened', 'replied', | |
| 'bounced', 'failed', 'review_needed' | |
| ); | |
| CREATE TYPE intent_type AS ENUM ( | |
| 'interested', 'question', 'not_now', | |
| 'not_interested', 'out_of_office', 'wrong_person', 'unknown' | |
| ); | |
| CREATE TYPE review_status AS ENUM ('pending', 'approved', 'rejected', 'edited'); | |
| -- βββ CORE TABLES ββββββββββββββββββββββββββββββββββββββββββββ | |
| -- ICP Configuration (editable from dashboard) | |
| CREATE TABLE icp_config ( | |
| id UUID PRIMARY KEY DEFAULT gen_random_uuid(), | |
| name TEXT NOT NULL DEFAULT 'default', | |
| min_employees INTEGER NOT NULL DEFAULT 50, | |
| industries TEXT[] NOT NULL DEFAULT ARRAY['technology','manufacturing','logistics','healthcare','finance'], | |
| exclude_industries TEXT[] NOT NULL DEFAULT ARRAY['government','non-profit','education'], | |
| geographies TEXT[] NOT NULL DEFAULT ARRAY['US','UK','AU','UAE','SA'], | |
| keywords TEXT[] NOT NULL DEFAULT ARRAY['automation','digital transformation','AI','operations'], | |
| tech_signals TEXT[] DEFAULT ARRAY['salesforce','hubspot','legacy_erp','sap'], | |
| score_threshold INTEGER NOT NULL DEFAULT 70, | |
| is_active BOOLEAN NOT NULL DEFAULT true, | |
| created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), | |
| updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() | |
| ); | |
| -- Weekly rotation state | |
| CREATE TABLE rotation_state ( | |
| id UUID PRIMARY KEY DEFAULT gen_random_uuid(), | |
| week_number INTEGER NOT NULL, -- 1=USA, 2=UK, 3=AU, 4=Gulf | |
| region TEXT NOT NULL, | |
| started_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), | |
| completed_at TIMESTAMPTZ, | |
| companies_found INTEGER DEFAULT 0, | |
| leads_qualified INTEGER DEFAULT 0 | |
| ); | |
| -- Companies discovered | |
| CREATE TABLE companies ( | |
| id UUID PRIMARY KEY DEFAULT gen_random_uuid(), | |
| domain TEXT NOT NULL UNIQUE, | |
| name TEXT NOT NULL, | |
| industry TEXT, | |
| employee_count INTEGER, | |
| employee_range TEXT, -- "50-200", "200-500" etc | |
| description TEXT, | |
| website_url TEXT, | |
| linkedin_url TEXT, | |
| country TEXT, | |
| region TEXT, | |
| tech_stack TEXT[], | |
| growth_signals JSONB DEFAULT '[]', -- job posts, news, funding | |
| raw_data JSONB DEFAULT '{}', | |
| source TEXT NOT NULL, -- 'serper', 'linkedin', 'manual' | |
| status company_status NOT NULL DEFAULT 'discovered', | |
| discovered_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), | |
| updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() | |
| ); | |
| CREATE INDEX idx_companies_domain ON companies(domain); | |
| CREATE INDEX idx_companies_status ON companies(status); | |
| CREATE INDEX idx_companies_country ON companies(country); | |
| CREATE INDEX idx_companies_name_trgm ON companies USING GIN (name gin_trgm_ops); | |
| -- Contacts (decision-makers) | |
| CREATE TABLE contacts ( | |
| id UUID PRIMARY KEY DEFAULT gen_random_uuid(), | |
| company_id UUID NOT NULL REFERENCES companies(id) ON DELETE CASCADE, | |
| full_name TEXT NOT NULL, | |
| first_name TEXT, | |
| last_name TEXT, | |
| title TEXT NOT NULL, | |
| seniority TEXT, -- 'c_suite','vp','director','manager' | |
| email TEXT, | |
| email_verified BOOLEAN DEFAULT FALSE, | |
| email_source TEXT, -- 'hunter','snov','pattern' | |
| linkedin_url TEXT, | |
| linkedin_verified BOOLEAN DEFAULT FALSE, | |
| status contact_status NOT NULL DEFAULT 'found', | |
| suppressed BOOLEAN NOT NULL DEFAULT FALSE, | |
| suppressed_at TIMESTAMPTZ, | |
| suppressed_reason TEXT, | |
| created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), | |
| updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() | |
| ); | |
| CREATE INDEX idx_contacts_company ON contacts(company_id); | |
| CREATE INDEX idx_contacts_email ON contacts(email); | |
| CREATE INDEX idx_contacts_suppressed ON contacts(suppressed); | |
| -- Evidence gathered during research | |
| CREATE TABLE evidence ( | |
| id UUID PRIMARY KEY DEFAULT gen_random_uuid(), | |
| company_id UUID NOT NULL REFERENCES companies(id) ON DELETE CASCADE, | |
| type TEXT NOT NULL, -- 'job_posting','news','social_post','website_text' | |
| content TEXT NOT NULL, | |
| source_url TEXT, | |
| ai_signal BOOLEAN DEFAULT FALSE, -- does this mention AI/automation? | |
| collected_at TIMESTAMPTZ NOT NULL DEFAULT NOW() | |
| ); | |
| CREATE INDEX idx_evidence_company ON evidence(company_id); | |
| -- Lead profiles (LLM output) | |
| CREATE TABLE lead_profiles ( | |
| id UUID PRIMARY KEY DEFAULT gen_random_uuid(), | |
| company_id UUID NOT NULL REFERENCES companies(id) ON DELETE CASCADE, | |
| profile_summary TEXT NOT NULL, | |
| pain_points TEXT[] DEFAULT '{}', | |
| ai_use_case TEXT, | |
| ai_readiness TEXT NOT NULL DEFAULT 'medium', -- low/medium/high | |
| outreach_angle TEXT, | |
| llm_model TEXT NOT NULL, | |
| llm_confidence NUMERIC(3,2), | |
| is_fallback BOOLEAN DEFAULT FALSE, | |
| created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() | |
| ); | |
| -- Lead scores | |
| CREATE TABLE lead_scores ( | |
| id UUID PRIMARY KEY DEFAULT gen_random_uuid(), | |
| company_id UUID NOT NULL REFERENCES companies(id) ON DELETE CASCADE, | |
| contact_id UUID REFERENCES contacts(id), | |
| total_score INTEGER NOT NULL CHECK (total_score BETWEEN 0 AND 100), | |
| tier lead_tier NOT NULL, | |
| company_fit INTEGER, -- 0-25 | |
| ai_readiness INTEGER, -- 0-25 | |
| decision_maker INTEGER, -- 0-20 | |
| growth_signal INTEGER, -- 0-15 | |
| engagement_potential INTEGER, -- 0-15 | |
| score_reasoning TEXT, | |
| scored_at TIMESTAMPTZ NOT NULL DEFAULT NOW() | |
| ); | |
| CREATE INDEX idx_scores_company ON lead_scores(company_id); | |
| CREATE INDEX idx_scores_tier ON lead_scores(tier); | |
| CREATE INDEX idx_scores_total ON lead_scores(total_score DESC); | |
| -- βββ OUTREACH TABLES ββββββββββββββββββββββββββββββββββββββββ | |
| CREATE TABLE outreach_sequences ( | |
| id UUID PRIMARY KEY DEFAULT gen_random_uuid(), | |
| company_id UUID NOT NULL REFERENCES companies(id) ON DELETE CASCADE, | |
| contact_id UUID NOT NULL REFERENCES contacts(id), | |
| current_step INTEGER NOT NULL DEFAULT 0, | |
| total_steps INTEGER NOT NULL DEFAULT 5, | |
| next_action_at TIMESTAMPTZ, | |
| status TEXT NOT NULL DEFAULT 'active', -- active/paused/completed/stopped | |
| stopped_reason TEXT, | |
| created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), | |
| updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() | |
| ); | |
| CREATE TABLE outreach_log ( | |
| id UUID PRIMARY KEY DEFAULT gen_random_uuid(), | |
| sequence_id UUID REFERENCES outreach_sequences(id), | |
| company_id UUID NOT NULL REFERENCES companies(id), | |
| contact_id UUID NOT NULL REFERENCES contacts(id), | |
| channel outreach_channel NOT NULL, | |
| step_number INTEGER NOT NULL, | |
| template_id TEXT, | |
| message_hash TEXT NOT NULL UNIQUE, -- prevent duplicate sends | |
| subject TEXT, | |
| status outreach_status NOT NULL DEFAULT 'queued', | |
| provider_id TEXT, -- external message ID from Resend/LinkedIn | |
| sent_at TIMESTAMPTZ, | |
| opened_at TIMESTAMPTZ, | |
| replied_at TIMESTAMPTZ, | |
| bounced_at TIMESTAMPTZ, | |
| created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() | |
| ); | |
| CREATE INDEX idx_outreach_company ON outreach_log(company_id); | |
| CREATE INDEX idx_outreach_hash ON outreach_log(message_hash); | |
| CREATE INDEX idx_outreach_status ON outreach_log(status); | |
| -- βββ ENGAGEMENT TABLES ββββββββββββββββββββββββββββββββββββββ | |
| CREATE TABLE engagement_log ( | |
| id UUID PRIMARY KEY DEFAULT gen_random_uuid(), | |
| outreach_id UUID REFERENCES outreach_log(id), | |
| company_id UUID NOT NULL REFERENCES companies(id), | |
| contact_id UUID NOT NULL REFERENCES contacts(id), | |
| signal_type TEXT NOT NULL, -- 'open','reply','bounce','linkedin_accept' | |
| intent intent_type, | |
| raw_content TEXT, -- actual reply text (for NLP) | |
| detected_at TIMESTAMPTZ NOT NULL DEFAULT NOW() | |
| ); | |
| -- βββ SYSTEM TABLES ββββββββββββββββββββββββββββββββββββββββββ | |
| CREATE TABLE suppression_list ( | |
| id UUID PRIMARY KEY DEFAULT gen_random_uuid(), | |
| email TEXT, | |
| domain TEXT, | |
| reason TEXT NOT NULL, -- 'unsubscribed','bounced','manual' | |
| added_at TIMESTAMPTZ NOT NULL DEFAULT NOW() | |
| ); | |
| CREATE INDEX idx_suppression_email ON suppression_list(email); | |
| CREATE INDEX idx_suppression_domain ON suppression_list(domain); | |
| CREATE TABLE human_review_queue ( | |
| id UUID PRIMARY KEY DEFAULT gen_random_uuid(), | |
| type TEXT NOT NULL, -- 'outreach_approval','score_anomaly','escalation' | |
| company_id UUID REFERENCES companies(id), | |
| contact_id UUID REFERENCES contacts(id), | |
| payload JSONB NOT NULL, -- full context for reviewer | |
| status review_status NOT NULL DEFAULT 'pending', | |
| reviewer_notes TEXT, | |
| resolved_at TIMESTAMPTZ, | |
| created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() | |
| ); | |
| CREATE INDEX idx_review_status ON human_review_queue(status); | |
| CREATE TABLE api_usage_log ( | |
| id UUID PRIMARY KEY DEFAULT gen_random_uuid(), | |
| provider TEXT NOT NULL, -- 'serper','hunter','snov','reoon' | |
| endpoint TEXT, | |
| credits_used INTEGER DEFAULT 1, | |
| success BOOLEAN NOT NULL, | |
| error_msg TEXT, | |
| called_at TIMESTAMPTZ NOT NULL DEFAULT NOW() | |
| ); | |
| CREATE TABLE audit_log ( | |
| id UUID PRIMARY KEY DEFAULT gen_random_uuid(), | |
| action TEXT NOT NULL, | |
| entity_type TEXT NOT NULL, | |
| entity_id UUID, | |
| actor TEXT NOT NULL DEFAULT 'system', | |
| details JSONB DEFAULT '{}', | |
| created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() | |
| ); | |
| -- βββ SEED DATA ββββββββββββββββββββββββββββββββββββββββββββββ | |
| INSERT INTO icp_config (name, min_employees, industries, geographies, keywords, score_threshold) | |
| VALUES ( | |
| 'default', | |
| 50, | |
| ARRAY['technology','software','manufacturing','logistics','supply_chain','healthcare','finance','real_estate_tech','retail_tech'], | |
| ARRAY['US','UK','AU','UAE','SA','SG'], | |
| ARRAY['automation','digital transformation','AI','machine learning','operations','workflow','efficiency'], | |
| 70 | |
| ); | |
| INSERT INTO rotation_state (week_number, region) | |
| VALUES (1, 'US'); | |