File size: 14,333 Bytes
8bab08d |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 |
-- CX AI Agent - Enterprise Database Schema
-- SQLite Schema for Campaign Management, Contact Tracking, and Analytics
-- =============================================================================
-- COMPANIES
-- =============================================================================
CREATE TABLE IF NOT EXISTS companies (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
domain TEXT UNIQUE,
industry TEXT,
size TEXT,
revenue TEXT,
location TEXT,
description TEXT,
pain_points TEXT, -- JSON array
website TEXT,
linkedin_url TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_companies_domain ON companies(domain);
CREATE INDEX idx_companies_industry ON companies(industry);
-- =============================================================================
-- CONTACTS
-- =============================================================================
CREATE TABLE IF NOT EXISTS contacts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
company_id INTEGER,
first_name TEXT,
last_name TEXT,
email TEXT UNIQUE NOT NULL,
phone TEXT,
job_title TEXT,
department TEXT,
seniority_level TEXT, -- C-Level, VP, Director, Manager, Individual Contributor
linkedin_url TEXT,
twitter_url TEXT,
location TEXT,
timezone TEXT,
-- Scoring
fit_score REAL DEFAULT 0.0,
engagement_score REAL DEFAULT 0.0,
intent_score REAL DEFAULT 0.0,
overall_score REAL DEFAULT 0.0,
-- Status & Lifecycle
status TEXT DEFAULT 'new', -- new, contacted, responded, meeting_scheduled, qualified, lost, customer
lifecycle_stage TEXT DEFAULT 'lead', -- lead, mql, sql, opportunity, customer, churned
-- Tracking
source TEXT, -- discovery_agent, manual_import, api, referral
first_contacted_at TIMESTAMP,
last_contacted_at TIMESTAMP,
last_activity_at TIMESTAMP,
-- Metadata
tags TEXT, -- JSON array
notes TEXT,
custom_fields TEXT, -- JSON object for extensibility
is_suppressed BOOLEAN DEFAULT 0,
suppression_reason TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (company_id) REFERENCES companies(id) ON DELETE SET NULL
);
CREATE INDEX idx_contacts_email ON contacts(email);
CREATE INDEX idx_contacts_company ON contacts(company_id);
CREATE INDEX idx_contacts_status ON contacts(status);
CREATE INDEX idx_contacts_lifecycle_stage ON contacts(lifecycle_stage);
CREATE INDEX idx_contacts_overall_score ON contacts(overall_score);
-- =============================================================================
-- CAMPAIGNS
-- =============================================================================
CREATE TABLE IF NOT EXISTS campaigns (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
description TEXT,
status TEXT DEFAULT 'draft', -- draft, active, paused, completed, archived
-- Targeting
target_industries TEXT, -- JSON array
target_company_sizes TEXT, -- JSON array
target_locations TEXT, -- JSON array
target_job_titles TEXT, -- JSON array
-- Configuration
sequence_id INTEGER,
goal_contacts INTEGER,
goal_response_rate REAL,
goal_meetings INTEGER,
-- Tracking
contacts_discovered INTEGER DEFAULT 0,
contacts_enriched INTEGER DEFAULT 0,
contacts_scored INTEGER DEFAULT 0,
contacts_contacted INTEGER DEFAULT 0,
contacts_responded INTEGER DEFAULT 0,
meetings_booked INTEGER DEFAULT 0,
-- Dates
started_at TIMESTAMP,
completed_at TIMESTAMP,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
created_by TEXT,
FOREIGN KEY (sequence_id) REFERENCES sequences(id) ON DELETE SET NULL
);
CREATE INDEX idx_campaigns_status ON campaigns(status);
-- =============================================================================
-- CAMPAIGN CONTACTS (Many-to-Many with Stage Tracking)
-- =============================================================================
CREATE TABLE IF NOT EXISTS campaign_contacts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
campaign_id INTEGER NOT NULL,
contact_id INTEGER NOT NULL,
stage TEXT DEFAULT 'discovery', -- discovery, enrichment, scoring, outreach, responded, meeting, closed_won, closed_lost
stage_updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
added_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
notes TEXT,
FOREIGN KEY (campaign_id) REFERENCES campaigns(id) ON DELETE CASCADE,
FOREIGN KEY (contact_id) REFERENCES contacts(id) ON DELETE CASCADE,
UNIQUE(campaign_id, contact_id)
);
CREATE INDEX idx_campaign_contacts_campaign ON campaign_contacts(campaign_id);
CREATE INDEX idx_campaign_contacts_contact ON campaign_contacts(contact_id);
CREATE INDEX idx_campaign_contacts_stage ON campaign_contacts(stage);
-- =============================================================================
-- EMAIL SEQUENCES
-- =============================================================================
CREATE TABLE IF NOT EXISTS sequences (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
description TEXT,
category TEXT DEFAULT 'outbound', -- outbound, nurture, re-engagement
is_active BOOLEAN DEFAULT 1,
is_template BOOLEAN DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
created_by TEXT
);
-- =============================================================================
-- SEQUENCE EMAILS (Steps in a sequence)
-- =============================================================================
CREATE TABLE IF NOT EXISTS sequence_emails (
id INTEGER PRIMARY KEY AUTOINCREMENT,
sequence_id INTEGER NOT NULL,
step_number INTEGER NOT NULL,
wait_days INTEGER DEFAULT 0, -- Days to wait after previous email
subject TEXT NOT NULL,
body TEXT NOT NULL,
send_time_preference TEXT, -- morning, afternoon, evening, or specific time
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (sequence_id) REFERENCES sequences(id) ON DELETE CASCADE,
UNIQUE(sequence_id, step_number)
);
CREATE INDEX idx_sequence_emails_sequence ON sequence_emails(sequence_id);
-- =============================================================================
-- EMAIL ACTIVITIES (Tracking email interactions)
-- =============================================================================
CREATE TABLE IF NOT EXISTS email_activities (
id INTEGER PRIMARY KEY AUTOINCREMENT,
contact_id INTEGER NOT NULL,
campaign_id INTEGER,
sequence_email_id INTEGER,
type TEXT NOT NULL, -- sent, delivered, opened, clicked, replied, bounced, unsubscribed, complained
subject TEXT,
preview TEXT,
link_url TEXT, -- For click tracking
meta_data TEXT, -- JSON for additional data
occurred_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (contact_id) REFERENCES contacts(id) ON DELETE CASCADE,
FOREIGN KEY (campaign_id) REFERENCES campaigns(id) ON DELETE SET NULL,
FOREIGN KEY (sequence_email_id) REFERENCES sequence_emails(id) ON DELETE SET NULL
);
CREATE INDEX idx_email_activities_contact ON email_activities(contact_id);
CREATE INDEX idx_email_activities_campaign ON email_activities(campaign_id);
CREATE INDEX idx_email_activities_type ON email_activities(type);
CREATE INDEX idx_email_activities_occurred ON email_activities(occurred_at);
-- =============================================================================
-- MEETINGS
-- =============================================================================
CREATE TABLE IF NOT EXISTS meetings (
id INTEGER PRIMARY KEY AUTOINCREMENT,
contact_id INTEGER NOT NULL,
campaign_id INTEGER,
title TEXT NOT NULL,
description TEXT,
scheduled_at TIMESTAMP NOT NULL,
duration_minutes INTEGER DEFAULT 30,
meeting_url TEXT,
location TEXT,
status TEXT DEFAULT 'scheduled', -- scheduled, completed, cancelled, no_show, rescheduled
outcome TEXT, -- interested, not_interested, needs_follow_up, closed_won
notes TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (contact_id) REFERENCES contacts(id) ON DELETE CASCADE,
FOREIGN KEY (campaign_id) REFERENCES campaigns(id) ON DELETE SET NULL
);
CREATE INDEX idx_meetings_contact ON meetings(contact_id);
CREATE INDEX idx_meetings_campaign ON meetings(campaign_id);
CREATE INDEX idx_meetings_scheduled ON meetings(scheduled_at);
CREATE INDEX idx_meetings_status ON meetings(status);
-- =============================================================================
-- ACTIVITIES (General activity log)
-- =============================================================================
CREATE TABLE IF NOT EXISTS activities (
id INTEGER PRIMARY KEY AUTOINCREMENT,
contact_id INTEGER,
campaign_id INTEGER,
meeting_id INTEGER,
type TEXT NOT NULL, -- discovery, enrichment, email_sent, email_opened, reply_received, meeting_scheduled, meeting_completed, note_added, status_changed
description TEXT,
meta_data TEXT, -- JSON for additional context
performed_by TEXT, -- agent_name or 'user'
occurred_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (contact_id) REFERENCES contacts(id) ON DELETE CASCADE,
FOREIGN KEY (campaign_id) REFERENCES campaigns(id) ON DELETE SET NULL,
FOREIGN KEY (meeting_id) REFERENCES meetings(id) ON DELETE SET NULL
);
CREATE INDEX idx_activities_contact ON activities(contact_id);
CREATE INDEX idx_activities_campaign ON activities(campaign_id);
CREATE INDEX idx_activities_type ON activities(type);
CREATE INDEX idx_activities_occurred ON activities(occurred_at);
-- =============================================================================
-- AB TESTS (for email sequences)
-- =============================================================================
CREATE TABLE IF NOT EXISTS ab_tests (
id INTEGER PRIMARY KEY AUTOINCREMENT,
campaign_id INTEGER NOT NULL,
sequence_id INTEGER NOT NULL,
name TEXT NOT NULL,
description TEXT,
test_type TEXT NOT NULL, -- subject_line, body, send_time, from_name
variant_a TEXT NOT NULL, -- JSON configuration
variant_b TEXT NOT NULL, -- JSON configuration
winner TEXT, -- 'a', 'b', or null if test ongoing
status TEXT DEFAULT 'running', -- running, completed, cancelled
started_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
completed_at TIMESTAMP,
FOREIGN KEY (campaign_id) REFERENCES campaigns(id) ON DELETE CASCADE,
FOREIGN KEY (sequence_id) REFERENCES sequences(id) ON DELETE CASCADE
);
-- =============================================================================
-- AB TEST RESULTS
-- =============================================================================
CREATE TABLE IF NOT EXISTS ab_test_results (
id INTEGER PRIMARY KEY AUTOINCREMENT,
ab_test_id INTEGER NOT NULL,
variant TEXT NOT NULL, -- 'a' or 'b'
emails_sent INTEGER DEFAULT 0,
emails_delivered INTEGER DEFAULT 0,
emails_opened INTEGER DEFAULT 0,
emails_clicked INTEGER DEFAULT 0,
emails_replied INTEGER DEFAULT 0,
meetings_booked INTEGER DEFAULT 0,
FOREIGN KEY (ab_test_id) REFERENCES ab_tests(id) ON DELETE CASCADE,
UNIQUE(ab_test_id, variant)
);
-- =============================================================================
-- TEMPLATES (Email templates)
-- =============================================================================
CREATE TABLE IF NOT EXISTS templates (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
category TEXT, -- cold_outreach, follow_up, meeting_request, thank_you
subject TEXT NOT NULL,
body TEXT NOT NULL,
variables TEXT, -- JSON array of variable names
is_active BOOLEAN DEFAULT 1,
usage_count INTEGER DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- =============================================================================
-- ANALYTICS SNAPSHOTS (Daily/hourly aggregated metrics)
-- =============================================================================
CREATE TABLE IF NOT EXISTS analytics_snapshots (
id INTEGER PRIMARY KEY AUTOINCREMENT,
campaign_id INTEGER,
date DATE NOT NULL,
hour INTEGER, -- null for daily snapshots
-- Metrics
contacts_discovered INTEGER DEFAULT 0,
contacts_enriched INTEGER DEFAULT 0,
emails_sent INTEGER DEFAULT 0,
emails_opened INTEGER DEFAULT 0,
emails_clicked INTEGER DEFAULT 0,
emails_replied INTEGER DEFAULT 0,
meetings_booked INTEGER DEFAULT 0,
-- Rates
open_rate REAL DEFAULT 0.0,
click_rate REAL DEFAULT 0.0,
response_rate REAL DEFAULT 0.0,
meeting_rate REAL DEFAULT 0.0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (campaign_id) REFERENCES campaigns(id) ON DELETE CASCADE,
UNIQUE(campaign_id, date, hour)
);
CREATE INDEX idx_analytics_campaign ON analytics_snapshots(campaign_id);
CREATE INDEX idx_analytics_date ON analytics_snapshots(date);
-- =============================================================================
-- SETTINGS (Application configuration)
-- =============================================================================
CREATE TABLE IF NOT EXISTS settings (
key TEXT PRIMARY KEY,
value TEXT NOT NULL,
description TEXT,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Insert default settings
INSERT OR IGNORE INTO settings (key, value, description) VALUES
('company_name', 'Your Company', 'Company name for email footers'),
('company_address', '123 Main St, City, State 12345', 'Physical address for CAN-SPAM compliance'),
('sender_name', 'Sales Team', 'Default sender name for emails'),
('sender_email', 'hello@example.com', 'Default sender email'),
('daily_email_limit', '1000', 'Maximum emails to send per day'),
('enable_tracking', '1', 'Enable email open and click tracking'),
('auto_pause_on_low_score', '1', 'Automatically pause contacts with low engagement'),
('min_engagement_score', '0.3', 'Minimum engagement score before auto-pause');
|