Date: 24 April 2026
Model: sonnet
Author: Lovinia Mak
You
I am going to start my first cold outreach campaign. What has stopped me before was the idea of organizing leads as i had this phobia of spreadsheets. I had gotten over that so now i have a basic spreadsheet that has cleaned up data and columns. I am someone who thinks in levels before steps and want systems that scale and can transmit my quality standards without me always having to be in the room. I believe a brand is not logos or design only but in how a user experiences the brand from discovery all the way until being the customer and also continuous relationship with the brand. This is why my marketing and sales voice needs to be coherent, efficient and delivers my signal in high fidelity transmission. Being disorganized or not knowing enough about a customer, our history with them, etc can spoil the brand experience. I could also be overwhelmed if i cannot find the data i need in one place or to know the history of interaction or outreach with a customer. All this is market research and data - how i design a campaign, what emails i write, what channels they are coming from, what increases sign up for my trainings, referrals etc. If I were a training house that can serve millions at scale, how would i design and set it up so it can be automated and then have orchestrators and agents in the loop with human managers to pull edge cases or attend to VIP support calls, etc. Tell me what success is for the level of goal i have.
Sloane
You're not building a spreadsheet system. You're building a signal transmission infrastructure — where your brand quality, voice, and standards replicate themselves faithfully across every touchpoint, at volume, without degrading. The spreadsheet is just the seed crystal.
Success at your level means:
A lead can enter your world as a cold name and exit as a loyal customer, advocate, or alumni — and every interaction in between reflects the same standard you would deliver personally, whether you touched it or not.
Think in levels the way you already do.
This is where everything else gets its integrity. If this layer is corrupted or incomplete, everything above it transmits noise instead of signal.
What lives here:
Master Contact Record — not just a lead, but a person file
Interaction History Log — every single touchpoint
Lead Score — a living number that updates automatically based on behavior
Campaign Attribution — where did they come from and what converted them
The discipline here: Every field must have a reason it exists operationally — not for vanity, but because something downstream uses it to make a decision or trigger an action.
This is where your standards get encoded into behavior. This is how you transmit quality without being in the room.
Core concept: Every lead has a status and every status has a playbook.
| Status | What it means | What happens next |
|---|---|---|
| Cold | Never heard of you | Awareness sequence begins |
| Engaged | Opened / clicked something | Nurture sequence activates |
| Warm | Attended free training or downloaded something | Conversion sequence begins |
| Hot | Visited pricing, replied to email, booked a call | Sales sequence / human flag |
| Customer | Bought something | Onboarding sequence |
| Alumni | Completed training | Retention + upsell sequence |
| Dormant | 90+ days no activity | Re-engagement sequence |
| VIP | High LTV or high referral activity | White glove lane activated |
Each sequence is a pre-written, pre-approved transmission of your voice and standards. You write it once. It runs forever. The quality is locked in at authorship.
What the engine does automatically:
This is where scale becomes possible without chaos. You're not replacing human judgment — you're deploying it strategically.
Three types of actors at this level:
These are trained team members or contractors who operate inside your system using your documented standards.
Their job is not to make creative decisions — it is to handle exceptions the automation flagged and execute the playbook for that exception.
Examples of what they handle:
Orchestrators have a daily queue — not an inbox. The queue shows them exactly what needs attention and what the standard response or action is. They are not improvising. They are executing your encoded standards.
Their job is pattern recognition and system integrity, not firefighting.
They ask:
Most systems at scale fail here. They automate but never improve. You want a system that gets smarter.
The feedback loop has four beats:
Measure — what happened this week/month across all campaigns
Interpret — what does the data mean for your signal
Decide — update the playbook, the sequence, or the scoring model
Propagate — push the updated standard back into the system so every future interaction benefits from the learning
This is how your brand experience improves even while you're not touching it. The system evolves under governed conditions.
You don't need to build all of this at once. But you need to know what each tool category is doing so you don't buy redundant tools or create gaps.
| Layer | Tool Category | Examples |
|---|---|---|
| Data Foundation | CRM | Airtable (now), HubSpot, Notion (light) |
| Communication | Email Automation | Mailchimp, ConvertKit, ActiveCampaign |
| Workflow Engine | Automation Platform | Make (Integromat), Zapier, n8n |
| Intelligence Layer | AI Agents / Ops | Claude, GPT via API, custom prompts |
| Queue Management | Task + Ops | Notion, Linear, ClickUp |
| Analytics | Reporting | Native CRM reports, Looker Studio |
| Voice Consistency | Brand Docs | Notion wiki, internal prompt library |
The golden rule: your CRM is the source of truth. Everything else writes back to it.
Not vanity metrics. Operational proof.
| Dimension | What success looks like |
|---|---|
| Signal Fidelity | Someone who met you through cold outreach and someone who was referred both experience the same quality of communication — they cannot tell which one was automated |
| Data Integrity | You can pull up any contact and know their full history, status, score, and next action in under 30 seconds |
| Operator Independence | An orchestrator joins your team, reads the playbook, and is handling their queue to your standard within one week — without you training them personally |
| Feedback Velocity | You identify a sequence that is underperforming and have a new version running within 72 hours |
| Attribution Clarity | You know exactly which channel, which message, and which training produces your highest LTV customers |
| Scale Without Drift | You go from 100 contacts to 10,000 and the brand experience does not degrade |
| VIP Separation | Your highest value relationships always get a human, always get speed, always feel seen — automatically flagged before they have to ask |
| Revenue Predictability | Because the pipeline is instrumented, you can forecast which cohort of leads will convert and when |
Since you think in levels, here is the honest level-by-level build:
Level 0 — Right now: Clean CRM. Basic columns. Status field. Source field. That spreadsheet you already have.
Level 1 — Next 30 days: Your first sequence written in your voice. Your first status-based workflow. Every new contact gets a source tag.
Level 2 — 60–90 days: Lead scoring logic. Automated status transitions. Your first playbook document for orchestrators.
Level 3 — 90–180 days: Orchestrator in the loop. Queue-based operations. Feedback loop meeting monthly.
Level 4 — 6–12 months: Full attribution model. AI-assisted drafting inside your voice guidelines. Predictive scoring. VIP lane fully operational.
Your voice documentation.
Every automation, every orchestrator, every AI agent — they all need to know what your standard sounds like. This is not a style guide. It is an operating transmission standard.
It answers:
Write this before you scale anything. It is the master signal everything else replicates from.
You are not building a campaign. You are building a replication machine for your standards. The campaign is just the first proof of concept.
You
what is the prompt to give airtable to build? how do i plan my lists and campaigns and manage airtable? I don't want my data to live on hubspot or notion. I plan to build my own one day on Cloudflare. I already have Cloudflare. I was hoping to use R2, KV, D1, pages and workers. My communication tool is now Resend. I need another way to integrate intelligence layer, queue management - why not Cloudflare? With workers that have python or js scripts? Analytics and reporting - what about Google Sheet or Business Intelligence or a PWA that can pull data for analytics? How do people do this? What about google data studio ?
Sloane
You're not just avoiding HubSpot. You're making a sovereignty decision — your data, your infrastructure, your rules. Cloudflare is a legitimate production choice for this. Let me map exactly how each piece fits.
| Your Layer | Cloudflare Primitive | What It Does |
|---|---|---|
| Data Foundation | D1 (SQLite at edge) | Master contact records, interaction history, lead scores |
| File / Blob Storage | R2 | Email templates, attachments, exports, training assets |
| Fast Key-Value State | KV | Session state, lead scores cache, feature flags, queue locks |
| Logic / Automation | Workers (JS or Python) | Triggers, scoring, sequence logic, webhook receivers |
| Scheduled Jobs | Workers Cron Triggers | Daily queue builds, score decay, digest reports |
| Queue Management | Queues | Async job processing, email send queue, task queue |
| Frontend / Dashboard | Pages | Your PWA dashboard, analytics UI, operator queue UI |
| Email Sending | Resend via Worker | All outbound email, webhook back for open/click tracking |
| Analytics Reporting | Analytics Engine or Google Looker Studio | Campaign metrics, funnel data |
This is a real production stack. Companies run serious infrastructure on this. You are not compromising.
Use Airtable only as your transition tool while you build on Cloudflare. Here is what to build in Airtable now that will directly translate to your D1 schema later.
I am building a CRM for a training house that serves leads
through cold outreach to paid customer to alumni.
I need the following tables:
TABLE: Contacts
- Contact ID (autonumber)
- Full Name
- Email
- Phone
- Company / Organization
- Role / Title
- Industry
- Country / Timezone
- Channel of Origin (LinkedIn / Referral / Cold Email /
Organic / Paid / Event)
- Lead Status (Cold / Engaged / Warm / Hot / Customer /
Alumni / Dormant / VIP / Unsubscribed)
- Lead Score (number, manually updated for now)
- Assigned Orchestrator
- Tags (multiple select)
- Notes
- Date Added
- Last Contact Date
- Next Action
- Next Action Date
- Linked Interactions (link to Interactions table)
- Linked Campaigns (link to Campaign Contacts table)
- Linked Purchases (link to Purchases table)
TABLE: Interactions
- Interaction ID
- Contact (linked)
- Date
- Channel (Email / Call / DM / Event / Support / Referral)
- Direction (Inbound / Outbound)
- Summary
- Outcome (No Response / Replied / Booked / Converted /
Complaint / Referral Given)
- Initiated By (Automated / Orchestrator / Manager / Self)
- Linked Campaign
TABLE: Campaigns
- Campaign ID
- Campaign Name
- Goal (Awareness / Nurture / Conversion / Retention /
Re-engagement)
- Channel (Email / LinkedIn / SMS / Multi)
- Status (Draft / Active / Paused / Complete)
- Start Date
- End Date
- Target Segment (based on Lead Status or Tags)
- Sequence Steps (long text or linked table)
- Owner
TABLE: Campaign Contacts
(Junction table — who is in which campaign)
- Contact (linked)
- Campaign (linked)
- Date Added to Campaign
- Current Step Number
- Status in Campaign (Active / Completed / Unsubscribed /
Converted)
- Converted (checkbox)
- Conversion Date
TABLE: Purchases
- Purchase ID
- Contact (linked)
- Training / Product Name
- Amount
- Date
- Payment Method
- Cohort / Batch
- Completion Status (Enrolled / In Progress / Completed /
Dropped)
- Certificate Issued (checkbox)
- Referral Source Contact (linked)
TABLE: Sequences
- Sequence ID
- Sequence Name
- Trigger (Status Change / Tag Applied / Date / Manual)
- Step Number
- Delay (days after previous step)
- Channel
- Subject Line
- Body / Script
- From Name / From Address
This Airtable structure directly mirrors what you will build in D1. You are not doing double work — you are prototyping your schema.
-- Contacts
CREATE TABLE contacts (
id TEXT PRIMARY KEY,
full_name TEXT NOT NULL,
email TEXT UNIQUE NOT NULL,
phone TEXT,
company TEXT,
role TEXT,
industry TEXT,
country TEXT,
timezone TEXT,
channel_origin TEXT,
lead_status TEXT DEFAULT 'cold',
lead_score INTEGER DEFAULT 0,
assigned_orchestrator TEXT,
tags TEXT, -- JSON array
notes TEXT,
date_added TEXT DEFAULT (datetime('now')),
last_contact_date TEXT,
next_action TEXT,
next_action_date TEXT,
unsubscribed INTEGER DEFAULT 0,
vip INTEGER DEFAULT 0
);
-- Interactions
CREATE TABLE interactions (
id TEXT PRIMARY KEY,
contact_id TEXT NOT NULL,
interaction_date TEXT DEFAULT (datetime('now')),
channel TEXT,
direction TEXT,
summary TEXT,
outcome TEXT,
initiated_by TEXT,
campaign_id TEXT,
FOREIGN KEY (contact_id) REFERENCES contacts(id)
);
-- Campaigns
CREATE TABLE campaigns (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
goal TEXT,
channel TEXT,
status TEXT DEFAULT 'draft',
start_date TEXT,
end_date TEXT,
target_segment TEXT, -- JSON rules
owner TEXT,
created_at TEXT DEFAULT (datetime('now'))
);
-- Campaign Contacts (enrollment)
CREATE TABLE campaign_contacts (
id TEXT PRIMARY KEY,
contact_id TEXT NOT NULL,
campaign_id TEXT NOT NULL,
date_added TEXT DEFAULT (datetime('now')),
current_step INTEGER DEFAULT 1,
status TEXT DEFAULT 'active',
converted INTEGER DEFAULT 0,
conversion_date TEXT,
FOREIGN KEY (contact_id) REFERENCES contacts(id),
FOREIGN KEY (campaign_id) REFERENCES campaigns(id)
);
-- Sequences
CREATE TABLE sequences (
id TEXT PRIMARY KEY,
campaign_id TEXT NOT NULL,
step_number INTEGER NOT NULL,
delay_days INTEGER DEFAULT 0,
channel TEXT DEFAULT 'email',
subject TEXT,
body TEXT,
from_name TEXT,
from_address TEXT,
FOREIGN KEY (campaign_id) REFERENCES campaigns(id)
);
-- Email Events (from Resend webhooks)
CREATE TABLE email_events (
id TEXT PRIMARY KEY,
contact_id TEXT,
campaign_id TEXT,
sequence_step INTEGER,
resend_email_id TEXT,
event_type TEXT, -- sent / delivered / opened / clicked / bounced / complained
event_time TEXT DEFAULT (datetime('now')),
metadata TEXT -- JSON
);
-- Purchases
CREATE TABLE purchases (
id TEXT PRIMARY KEY,
contact_id TEXT NOT NULL,
product_name TEXT,
amount REAL,
currency TEXT DEFAULT 'USD',
purchase_date TEXT,
completion_status TEXT DEFAULT 'enrolled',
cohort TEXT,
certificate_issued INTEGER DEFAULT 0,
referral_contact_id TEXT,
FOREIGN KEY (contact_id) REFERENCES contacts(id)
);
-- Orchestrator Queue
CREATE TABLE queue_items (
id TEXT PRIMARY KEY,
contact_id TEXT NOT NULL,
queue_type TEXT, -- reply / hot-lead / complaint / vip / dormant / onboarding
priority INTEGER DEFAULT 3, -- 1=urgent, 2=high, 3=normal
title TEXT,
description TEXT,
assigned_to TEXT,
status TEXT DEFAULT 'open', -- open / in-progress / resolved / escalated
created_at TEXT DEFAULT (datetime('now')),
due_by TEXT,
resolved_at TEXT,
FOREIGN KEY (contact_id) REFERENCES contacts(id)
);
// workers/email-events.js
// Receives open/click/bounce/reply events from Resend
// Writes to email_events table
// Updates contact lead_score
// Triggers status changes
// Creates queue items if reply detected
export default {
async fetch(request, env) {
const payload = await request.json();
// Verify Resend webhook signature
// Write event to D1
// If event_type === 'opened' → score +5
// If event_type === 'clicked' → score +10
// If event_type === 'replied' → score +20, create queue item
// If event_type === 'complained' → unsubscribe contact
// Advance campaign_contacts to next step if applicable
return new Response('ok');
}
}
// workers/sequence-runner.js
// Runs every hour via Cron Trigger
// Checks campaign_contacts for contacts due for next step
// Pulls sequence step body and subject
// Personalizes with contact data
// Sends via Resend API
// Logs to email_events and interactions
// Cron schedule in wrangler.toml:
// [triggers]
// crons = ["0 * * * *"] ← every hour
// workers/lead-scorer.js
// Runs daily
// Recalculates scores based on:
// - Email engagement in last 30 days
// - Purchase history
// - Training completion
// - Days since last contact (decay)
// Updates contacts.lead_score
// Promotes or demotes lead_status
// Creates queue items for Hot leads
// workers/queue-builder.js
// Runs every morning at 7am
// Finds contacts that are:
// - Hot with no activity in 3 days → flag
// - Customers who never logged in → flag
// - VIP with no touch in 14 days → flag
// - Dormant (90 days) → re-engagement sequence
// Creates queue_items for orchestrators
// Assigns based on orchestrator workload
// workers/api.js
// REST API that your Pages PWA calls
// Endpoints:
// GET /api/contacts
// GET /api/contacts/:id
// POST /api/contacts
// PUT /api/contacts/:id
// GET /api/queue
// PUT /api/queue/:id/resolve
// GET /api/campaigns
// GET /api/analytics/funnel
// GET /api/analytics/campaign/:id
How it works:
The pattern:
D1 → Worker (daily cron) → Google Sheets API → Looker Studio
Best for: Sharing reports with stakeholders, basic funnel views, campaign comparisons
Limitation: Not real-time. One step removed from your source of truth.
Cloudflare has its own Analytics Engine — it's a time-series data store built into Workers.
// Inside any worker, write analytics events:
env.ANALYTICS.writeDataPoint({
blobs: ['email_opened', campaign_id, contact_id],
doubles: [lead_score],
indexes: [contact_id]
});
Then query it via the Analytics Engine GraphQL API and render in your Pages PWA.
Best for: Real-time event data, high volume, keeping everything on Cloudflare
This is your cleanest long-term play.
Cloudflare Pages (React/Vue PWA)
↓ calls
Workers API (/api/analytics/*)
↓ queries
D1 database
↓ renders
Charts (Chart.js or Recharts)
Your orchestrators log into your Pages PWA. They see their queue. They resolve items. They see contact history. All data stays on Cloudflare. No third party sees your list.
If you want serious BI without building it yourself:
| Approach | Who Uses It | Tradeoff |
|---|---|---|
| HubSpot / Salesforce all-in | Funded startups, enterprises | Powerful but expensive, your data is their product |
| Airtable + Zapier + Mailchimp | Early stage, non-technical founders | Fast to start, hits walls at scale, expensive per row |
| Notion + Make + ConvertKit | Solo operators, coaches | Good for small lists, not real CRM |
| Custom on Supabase + Resend | Technical indie builders | Postgres, great ecosystem, but you pay Supabase |
| Custom on Cloudflare D1 + Workers + Resend | Serious builders who want sovereignty | What you're building. Correct choice at your goals |
| Google Sheets as database | Scrappy early stage | Works until it doesn't. 10k rows and it breaks |
| Looker Studio for analytics | Everyone who wants free dashboards | Industry standard for free BI on top of Sheets/BigQuery |
1. Create D1 database with schema above
2. Build Worker API (read/write contacts)
3. Build Resend webhook receiver Worker
4. Migrate contacts from Airtable CSV → D1
5. Send first campaign via Worker + Resend
1. Lead scoring Worker (cron daily)
2. Sequence engine Worker (cron hourly)
3. Queue builder Worker (cron daily)
4. Basic Pages PWA with queue UI
1. Analytics Engine for real-time events
2. Daily summary Worker → Google Sheets
3. Looker Studio dashboard on top of Sheets
4. Eventually replace Looker with PWA charts
# wrangler.toml
name = "training-house-crm"
main = "src/index.js"
compatibility_date = "2024-01-01"
[[d1_databases]]
binding = "DB"
database_name = "crm"
database_id = "your-d1-id"
[[kv_namespaces]]
binding = "CACHE"
id = "your-kv-id"
[[r2_buckets]]
binding = "STORAGE"
bucket_name = "crm-assets"
[[queues.producers]]
binding = "EMAIL_QUEUE"
queue = "email-send-queue"
[[queues.consumers]]
queue = "email-send-queue"
max_batch_size = 10
[triggers]
crons = [
"0 * * * *", # sequence runner - every hour
"0 7 * * *", # queue builder - every morning 7am
"0 2 * * *
---
*Session cost: $0.10644*