Not "I use AI to write emails." An AI agent runs the whole campaign pipeline: it pulls the right companies from our sales database, enforces do-not-email rules it cannot skip, matches each company to a pitch that has already won us customers, and writes a unique email for every person with about twenty AI writers working in parallel. I describe the campaign in plain English and approve at the two moments that matter.
A campaign starts as a sentence: "Target VPs of Marketing at mid-market SaaS companies." Claude Code reads the system's instruction file, copies a campaign template, fills in the campaign settings from the brain dump, shows it to me for confirmation, then runs six stages, each writing a checkpoint file so any failure resumes instead of restarting, and every stage asking before proceeding.
flowchart TD DUMP["Brain dump:
'target VPs of Marketing at mid-market SaaS'"] --> YAML[Claude fills in the campaign settings,
shows them for confirmation] YAML --> S1[Stage 1 · Pull target companies
from a local pre-scored database
fit, tier, and size filters] S1 --> S2[Stage 2 · HubSpot enrich
read-only: contacts, deals,
lifecycle, company props] S2 --> S3[Stage 3 · Find the right people
CRM first, then our database,
outside lookups only for gaps] S3 --> S4[Stage 4 · VOC play scoring
7 plays, signal + persona fit] S4 --> PAUSE{{HUMAN GATE
review play assignments}} PAUSE --> S5[Stage 5 · Play-aware scaffolds
hook, pain, mechanism, CTA
+ customer quotes per play] S5 --> S6[Stage 6 · Copy generation
~20 parallel AI writers
~4 leads each, unique copy] S6 --> VAL[Validators:
spam words, word count,
play alignment, resolved vars] VAL --> SAMPLE{{HUMAN GATE
review sample emails}} SAMPLE --> UP[Upload to EmailBison
create leads → attach to campaign] S1 -.checkpoint.-> CK[(accounts.json)] S3 -.checkpoint.-> CK2[(contacts.json)] S6 -.checkpoint.-> CK3[(leads_final.csv)]
The data compounds. Across my work we were dealing with tens of thousands of accounts and hundreds of thousands of contacts, and every campaign made those databases better: fit scores, enriched profiles, and buying signals accumulated just by doing the job regularly. Outside lookup tools (Apollo, LeadMagic, Exa) only fill gaps, and every result is cached so a credit is never spent twice on the same person.
The instruction file marks these mandatory at every stage: account pull, contact discovery, and final output all re-check them. This is the difference between "AI helps with email" and "AI can be trusted with the send button":
| Gate | Rule |
|---|---|
| DB-only targeting | Only companies in the sales DB get emailed. External tools detect signals and enrich, they never inject net-new companies into a campaign. |
| Customer exclusion | Checked in two places (account flag AND customer table) at three stages. Either flag excludes. |
| Pipeline protection | HubSpot lifecycle = customer/opportunity, or any open deal, blocks the account, catching CRM truth the local DB hasn't synced yet. |
| Recently contacted | The email platform itself is the source of truth for who was messaged recently, and accounts a rep is actively working get a longer buffer than unowned ones. Relationships get protected automatically. |
| Domain match | Enriched emails must match the target company's domain, with known aliases mapped, no Frankenstein contacts. |
| Final preflight | Before any upload: a preflight script re-checks customers/deals, plus a title and location review, plus a pre-delivery sample. Never skipped. |
Play selection is scored, not vibes. Every account is scored against 7 plays derived from actual customer call analysis, each play defined in a settings file with its signals, weights, persona fit, employee ranges, and real customer quotes. A weak match falls back to a default play, and the assignment file pauses for my review before any copy gets written.
Every lead gets unique copy. The default is not "one template with merge tags." Stage 6 groups leads into batches of ~4, generates play-aware prompts, and launches ~20 AI writers in parallel. Each email is structurally unique while holding the play's messaging, an anti-fingerprinting defense that also just reads better. Templates exist as a speed fallback, not the default.
Then machines check the machines. Validators scan every generated email: banned spam-trigger words, unresolved variables, word count in the 80–200 band, play alignment, and formatting rules (like never using em dashes, and simple HTML instead of pre-wrapped plaintext that breaks on mobile).
flowchart LR MSG[messages.csv
play context per lead] --> PREP[--prepare
batch into prompts] PREP --> SA1[Subagent 1
4 leads] PREP --> SA2[Subagent 2
4 leads] PREP --> SA3[...] PREP --> SA20[Subagent ~20
4 leads] SA1 --> CONS[--consolidate
merge JSON outputs] SA2 --> CONS SA3 --> CONS SA20 --> CONS CONS --> VAL[--validate
spam words · word count
play alignment · vars resolved] VAL --> FINAL[(leads_final.csv)] FINAL --> BISON[EmailBison API
create leads → attach to campaign]