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');