The Wise Operator
The Outbound Pipeline: From ICP to Gmail Drafts in One Command

A Workflow

outbound and revops · practitioner · 60-90 minutes to install, 10 minutes per run ·

A five-stage AI workflow that turns an ICP definition into personalized Gmail drafts. Exa research, Apollo enrichment, Claude drafting, human review at the end. Install once, run anytime, never auto-send.

By , editor of The Wise Operator


ICP spec  ->  Exa research  ->  Apollo enrichment  ->  Claude drafting  ->  Gmail drafts
 (you)        (discovery)       (verification)        (personalization)     (human review)
The pipeline, end to end.

Cold outbound done well is the grind most teams do not do well. It needs fresh research on what each prospect is actually building, real contact enrichment instead of a guessed email, and personalization that does not read like a template. Doing all of that by hand for ten prospects is a full day. Hiring an SDR or an outbound agency burns three to five thousand dollars a month and still produces templated output. The wrong fix, scripted spray-and-pray, burns deliverability and brand.

This playbook is the right fix. Five stages, packaged as a Claude Code skill, run with one command. Input: a one-page ICP definition. Output: ten to fifty Gmail drafts, each personalized against fresh research from the last seventy-two hours. Nothing sends without you reading it and clicking the send button yourself.

What This Looks Like for You

You do not have to write code to use this pipeline. You write a one-page document describing who you want to reach. You type one sentence into Claude Code: “Run the outbound pipeline against my ICP.” Five minutes later, ten personalized email drafts wait for you in your Gmail Drafts folder. You read them, edit anything that needs editing, and click Send on the ones that pass your bar.

That is the whole experience. Everything below this section explains the moving parts, the tools each stage uses, and what is actually happening when you press enter. You can follow it as a build-it-yourself playbook, or you can read it the way you would read a feature article about how an automated kitchen works without ever planning to install one. Both are useful. The deeper sections labeled Under the hood are optional reads for the curious; the prose around them gives you the full mental model.

What You Will Build

A pipeline that runs like this. Each stage hands its output to the next. Every stage is replaceable. Every stage is auditable. The whole thing runs in about ten minutes per batch of fifty prospects once it is installed.

What you get when it finishes:

  • A folder of fresh research on real prospects, not a list pulled from a stale database
  • Verified contact emails matched to real decision-makers, not “info@” guesses
  • One personalized email per prospect that opens with a specific, recent signal about that company
  • All drafts sitting in your Gmail Drafts folder, ready for you to read, edit, and click send

What you do not get:

  • An auto-sender. The pipeline never sends. Drafts only.
  • A volume play. Fifty thoughtful messages a week beats five hundred templated ones.
  • A magic prospecting list. The quality of the output is the quality of the ICP definition you write at the start.

Prerequisites and the Cost Reality Check

Read this section carefully. The skill will not run if any of these are missing. Skipping past this is the most common reason builds fail at step one.

Accounts and keys you must provision

ServiceWhat it doesFree tierPaid starting at
Anthropic APIClaude does the drafting$5 credit on signupPay-as-you-go (~$5-15/month at typical volume)
ExaFresh research signals~10 searches/day$10/month
Apollo.ioEmail + title enrichment50 credits/month$59/month
Composio (recommended)Handles Gmail + Apollo OAuthFree for personal use$49/month for higher quotas
Google Workspace + Gmail APIDrafts stagingFreen/a

If you only have free credits everywhere, you can run roughly fifty prospects per month before something hits a quota wall. That is enough to validate the workflow and decide whether it earns its paid keep.

Cost expectation at one hundred prospects per month

  • Anthropic API drafting: about $1.20
  • Exa research: $10 paid plan
  • Apollo enrichment: $59 paid plan
  • Composio: free tier
  • Monthly total at one hundred prospects: ~$70-75

For comparison, a junior SDR runs four to seven thousand a month. An outbound agency runs three to five. The pipeline is roughly one to two percent of those numbers and it personalizes on real-time signals neither can match.

The no-cost alternative path

If you want to test the workflow without any paid keys, you can run it in dry-run mode the whole time. Dry-run skips Apollo (synthesizes test data), skips Gmail draft staging (writes drafts to a local file), and uses Anthropic’s $5 signup credit and Exa’s free tier. You will see exactly what the live version produces, just without real contact emails or actual Gmail drafts. Useful for evaluating the output quality before committing to billing setup.

Discernment note: The skill defaults to dry-run mode unless you explicitly confirm a paid run. This is intentional. Apollo credits and Anthropic spend are real money, and a misconfigured ICP can burn fifty credits on a useless audience in three minutes. Always dry-run first when you change the ICP.

The Tooling, Explained

If you have never used these services, here is what each one is and why it sits where it does in the pipeline.

Exa

Exa is a search engine designed for AI agents. The difference from Google search is that Exa returns clean, structured results with publish dates, full content extraction, and semantic search instead of keyword matching. You can ask it for “Series B SaaS companies that announced product launches in the last 30 days” and it will surface real, dated, recent results. Google would return a mix of stale articles, paid promotions, and listicles.

The pipeline uses Exa at the discovery stage. Given an ICP intent signal like “ESOP transition” or “recent Series B,” Exa returns ten to fifty matching companies with the actual article that contains the signal so the draft can reference it specifically.

Apollo.io

Apollo.io is a contact database that maps a company domain to its decision-makers, with verified email addresses and titles. Given acme.com, Apollo returns the VP of Engineering’s name, title, work email, and LinkedIn profile. The data is sourced from a combination of public records, opt-in submissions, and verified bounce-back testing.

The pipeline uses Apollo at the enrichment stage. Exa tells you which companies are interesting. Apollo tells you who at those companies you should actually email.

Composio

Composio is a tool-bridging layer for AI agents. Instead of writing custom OAuth flows for Gmail, custom API clients for Apollo, custom error handling for each, you give Composio a single API key and it handles every downstream service through one normalized interface. The agent says “create a Gmail draft” and Composio does the OAuth dance, calls the right Gmail endpoint, retries on transient failures, and returns a clean response.

The pipeline uses Composio at the enrichment and Gmail-staging stages. You can skip Composio and wire raw Gmail OAuth + Apollo API yourself, but for non-engineers, Composio collapses two days of integration work into a single key paste.

Anthropic Claude

Anthropic Claude writes the actual emails. The pipeline uses Claude Sonnet (currently 4.6) because it is the right balance of quality, speed, and cost for outbound copy. Opus is overkill and expensive. Haiku is too thin to produce nuanced personalization.

Gmail API

The Gmail API creates drafts in your personal Drafts folder. Composio wraps it for you. Drafts only, never sends. This is a hard rule of the pipeline and it is enforced at the script level.

Stage 1: Define Your ICP

The pipeline reads a single markdown file called icp-spec.md. The quality of every downstream stage depends on the quality of this file. Vague ICP, vague drafts. Specific ICP, specific drafts.

The template ships with the playbook. Copy it into your working directory and fill in seven fields:

  1. Target role: the title you want to reach (VP of Engineering, Owner-operator of a regional manufacturer)
  2. Company size: headcount or revenue range (50-500 employees, $5M-$50M revenue)
  3. Geography: states, regions, countries, or “anywhere”
  4. Verticals: one to four industries you serve
  5. Intent signal: the specific recent event Exa will search for (Announced a Series B in the last 90 days, Three or more engineering manager job posts open)
  6. Value proposition: one sentence on what you offer this audience
  7. Call to action: the exact small ask at the end of every email (15-minute call next week)

The intent signal is the single highest-leverage field. “Recent funding” is too broad. “Announced a Series B in healthcare AI in the last 60 days” produces a list of fifteen real companies you can actually pitch.

Discernment note: Write the ICP for the next thirty prospects, not for a five-year vision. The point of having a portable ICP file is that you can swap it out next week when you decide to target a different vertical. Optimize for specific over comprehensive every time.

Stage 2: Discovery via Exa

What happens. The pipeline reads your ICP, builds a search query out of the intent signal plus the vertical and geography fields, and asks Exa to return ten to fifty matching companies. For each match, it captures the source article, the publish date, and the specific sentence from the article that justifies why this company is on the list. That sentence becomes the research signal each draft will open with.

What you see. A list of ten to fifty real companies, each with a one-line “why we picked it” snippet pulled from a real article published in the last few days. If the list looks off, you sharpen the ICP intent signal and run again. No code editing required.

Under the hood: how the Exa query is constructed

The script combines four ICP fields into a single natural-language Exa query, then calls Exa’s search endpoint with useAutoprompt: true so Exa rephrases for better semantic matching:

def build_query(icp: dict) -> str:
    parts = []
    if "intent signal" in icp:
        parts.append(icp["intent signal"])
    if "verticals" in icp:
        parts.append(f"in {icp['verticals']}")
    if "geography" in icp:
        parts.append(f"based in {icp['geography']}")
    if "company size" in icp:
        parts.append(f"({icp['company size']})")
    return ". ".join(parts).strip()

For an ICP targeting “Series B SaaS companies in the US that posted three or more engineering manager roles in the last 30 days,” that produces:

Posted three or more engineering manager roles in the last 30 days. in B2B SaaS. based in United States. (Series B, 50-500 employees)

Exa returns the top matches with content extracts and highlighted sentences. Those highlights become the research signal each draft opens with. The output lands at out/candidates.jsonl with one row per candidate (company name, source URL, signal text, publish date).

Discernment note: Exa search results are only as good as the intent signal. If you get back a list of irrelevant companies, do not blame Exa. Sharpen the signal. “Hiring engineering managers” is a fine signal. “Hiring three or more engineering managers in the last 30 days at a B2B SaaS company” is a great one.

Stage 3: Enrichment via Apollo

What happens. For every company Exa surfaced, the pipeline asks Apollo “who at this company matches the target role we are looking for?” Apollo returns a real person’s name, title, verified email address, and LinkedIn URL. By default the pipeline filters for senior titles (VP, Director, Head, Owner, Founder, President, Chief). Companies where Apollo cannot find a match get dropped from the run rather than silently emailed to nobody.

What you see. Each candidate from stage two now has a real human attached to it. Where stage two said “Acme Corp,” stage three says “Jamie Chen, VP of Engineering at Acme Corp, jamie@acme.com, here is her LinkedIn.” That is the file the drafting stage reads next.

Under the hood: the Apollo people-search payload

Apollo’s mixed_people/search endpoint takes a company domain and a title filter. The script sends one request per candidate company:

payload = {
    "q_organization_domains_list": [domain],
    "page": 1,
    "per_page": 5,
    "person_titles": ["VP", "Director", "Head", "Owner", "Founder", "President", "Chief"],
}

The output writes to out/enriched.jsonl with each row carrying contact_name, contact_email, contact_title, and linkedin_url. Apollo enforces a per-month credit cap; the free tier gives 50 credits, paid plans give thousands. If you hit the cap mid-run, the script logs a warning and skips that candidate rather than failing the whole batch.

If your ICP target role does not match the default senior-title filter (for example, you are pitching engineering individual contributors), edit the person_titles list in enrich.py and re-run.

Discernment note: Apollo’s data is mostly good but not always current. People change jobs. Treat the contact email as a strong default but always glance at the LinkedIn URL before clicking send to confirm the person still works there. The pipeline does not check this for you, on purpose, because the human review at the end is supposed to catch it.

Stage 4: Drafting with Claude

What happens. For each enriched prospect, Claude writes one personalized email. The opening sentence references the specific Exa research signal for that company. The body explains your value proposition in plain language. The closing is the exact call-to-action you specified in the ICP. Length is capped at 90 to 130 words. No em dashes, no sales-speak, no templated openers, no fabricated connections.

What you see. Ten ready-to-review drafts that read like a real person wrote them, because a real person, with a model’s help, did. If a draft sounds templated or off-tone, the issue is almost always in the ICP rather than the prompt. Sharpen the value proposition and the next run improves.

Under the hood: the system prompt that produces the drafts

The drafting stage calls the Anthropic API with a system prompt that hard-codes the rules of the format and a user prompt that injects the ICP context, the sender identity, and the specific prospect’s research signal and contact info. The system prompt is the single most load-bearing component of the entire pipeline:

You write cold outbound emails for a non-technical operator who values
honesty over volume. Your job is to produce one personalized email per
prospect using the research signal and contact information provided.

Hard rules:
- The opening sentence must reference the specific research signal.
  Not a generic compliment.
- The body explains the value proposition in plain language, no jargon.
- The CTA at the end is exactly the one the user specified.
- Sign-off is the sender name only. No marketing footer.
- Length: 90 to 130 words including signature. No exceptions.
- Tone: peer to peer. Not a vendor pitch. Not a desperate ask.
- No em dashes. Use commas, colons, or periods.
- No "I hope this finds you well" or similar templated openers.
- No "circling back," "touching base," "synergy," or other sales-speak.
- If the research signal is weak or missing, say so honestly inside
  the email rather than fabricating a connection.

Output format: a JSON object with keys subject, body. Nothing else.

The “do not fabricate a connection” rule is the one that separates this pipeline from a thousand spammy lookalikes. Claude will sometimes get a thin Exa signal, especially in slower news weeks. The rule says: write a shorter, more honest email rather than inventing a fake research hook. The result is a draft that sounds like a person wrote it because a person, with a model’s help, did.

The output is out/drafts.jsonl with each prospect now carrying a draft_subject and draft_body. The system prompt lives in scripts/draft.py; edits there propagate to the next run immediately.

Discernment note: Tune this prompt to your audience. The defaults are calibrated for a consultative, peer-to-peer tone. If you sell to procurement officers in regulated industries, you will want a more formal register. If you sell to founders in early-stage startups, a sharper, shorter style. The system prompt is the lever; the ICP is the lever; the model itself is rarely the problem.

Stage 5: Gmail Draft Staging

What happens. Each draft from stage four lands in your personal Gmail Drafts folder as a real Gmail draft. Nothing sends. The pipeline refuses to call any “send” endpoint at the script level; it only creates drafts. You open Gmail, read each one, edit anything that needs editing, and click Send on the ones that pass your bar.

What you see. Ten new drafts in Gmail. Same recipient field, same subject, same body that Claude produced. Sender is your real email address. If you have Composio configured, the pipeline used Composio’s Gmail action to create them. If you wired raw Gmail OAuth instead, the pipeline used the Gmail API directly. The user-visible result is identical either way.

Under the hood: the Composio Gmail action call

With Composio, the call is one line per draft:

payload = {
    "action": "GMAIL_CREATE_EMAIL_DRAFT",
    "params": {
        "recipient_email": to_email,
        "subject": d.get("draft_subject", ""),
        "body": d.get("draft_body", ""),
        "is_html": False,
    },
}

Without Composio, the script falls back to raw Gmail OAuth using a refresh token you generate once during install. Either path produces the same outcome: drafts in your Gmail Drafts folder, never sent. The output is out/staged.jsonl with each prospect carrying a gmail_draft_id you can reference if you ever want to bulk-edit or delete drafts programmatically later.

Discernment note: Drafts, never sends. This is the load-bearing rule of the entire pipeline. The skill will refuse to run a stage that auto-sends. If you decide later that you want one-click sending, you should run the drafts through a separate human-review surface rather than removing the safeguard.

The First Real Run

After you finish the install walkthrough and your .env is filled in and your icp-spec.md is filled in, the first paid run is one sentence in Claude Code: “Run the outbound pipeline against my ICP, limit five prospects.” Claude executes the skill end to end. Five prospects, full enrichment, real drafts in Gmail. Estimated cost: $0.50 in Anthropic API spend, five Apollo credits used, no Exa charges since five searches stays inside the free tier. Total elapsed time: about two minutes.

When it finishes, open Gmail. You will see five new drafts. Read each one. The opening sentence should reference a specific recent signal about that company. The body should explain your value proposition in language a busy executive can scan in fifteen seconds. The CTA should be the exact one you wrote in the ICP.

If any draft does not meet that bar, do not send it. Either edit it manually before clicking send, or delete it and refine the ICP for the next run. The pipeline gets better as you read more drafts.

Under the hood: the equivalent CLI command if you want to bypass Claude Code
python ~/.claude/skills/outbound-pipeline/scripts/run_pipeline.py \
  --icp icp-spec.md \
  --limit 5

The orchestrator script chains the five stages, writes intermediate artifacts to out/*.jsonl for inspection, and exits when Gmail draft staging completes. Identical outcome, slightly more direct.

Operating This Day to Day

After the first successful run, the workflow is:

  • Monday morning: open Claude Code, type /outbound-pipeline in any session, the skill activates and runs against your existing ICP file. Drafts land in Gmail.
  • Tuesday-Thursday: review one batch per day in the morning. Send the ones that pass your bar. Delete the rest.
  • Friday: edit the ICP if last week’s response rate suggested a tighter audience. Save the new version and let next week’s runs use the refined spec.
  • Monthly: check your Apollo credit balance. Top up before the cap if you are pacing past it.

The pipeline is designed to disappear into a routine, not to be a daily tinkering project. If you find yourself rewriting the system prompt twice a week, the ICP probably needs sharpening rather than the prompt.

A Word on Honest Outreach

The pipeline is built for a particular kind of operator: someone who sees outbound as a way to start real conversations with people who have a real problem, not as a numbers game. There is a Christian wisdom angle here that is worth saying out loud, because it is the reason the pipeline produces drafts and never auto-sends.

“Let your speech always be gracious, seasoned with salt, so that you may know how you ought to answer each person.” (Colossians 4:6, ESV)

The verse has been used for generations as a guide to conversation. It applies equally to a cold email, which is a conversation a stranger did not ask for. “Gracious” rules out sales-speak and manipulation. “Seasoned with salt” rules out templates and filler. “How you ought to answer each person” is the case for personalization on real signals, not on a first-name mail-merge dressed up as personalization.

The five-stage pipeline is engineered to produce drafts that pass that test. Specific opener about a real, recent thing. Plain explanation. Small ask. The operator reviews each one because every email is a small claim on someone’s attention, and that claim should be made carefully or not at all.

This is what separates wise operation from frantic operation. The model handles the research and the drafting. The operator handles the discernment about which messages are worth sending. The two together produce something neither would alone.