If you want Clay-level outcomes without building a fragile pile of zaps, this is the workflow.
We run lead scraping, email verification, and outreach from one operating loop:
- Google Sheets as source of truth
- Multi-provider enrichment fallback
- Verification gate before writing emails
- Gmail draft/send pipeline
- Deterministic status tracking so nothing gets double-touched
This post combines both the operating model and the exact build playbook.
The result we were optimizing for
Not “more data.” Not “more enrichment.”
The goal is decision-ready rows you can safely send.
Each row should end up in one clear state:
- ready_to_draft
- drafted
- sent
- replied
- bounced
- research_alt_email
If a row doesn’t have a clear state, your process leaks money.
Tool stack (from our live TOOLS.md setup)
This is the stack we actually used:
- Google Sheets API for row input/output and system state
- Gmail API for draft + send flow and send-status checks
- RocketReach → Hunter → Dropcontact for email discovery (fallback order)
- Reoon Email Verifier as final verification gate
- 1Password service account (Ace vault) for secure key retrieval
- OpenClaw skills/scripts for repeatable batching and retries
Core rule: discovery is probabilistic, verification is deterministic.
Build it: the exact skill architecture
1) Set up your sheet schema first
Minimum columns:
CompanyWebsiteFounder NameFounder EmailEmail SourceVerification StatusLast CheckedNotes
Operationally useful additions:
Outreach StatusReply StatusBounce Status
If these are missing, your pipeline becomes impossible to audit.
2) Define net-new lead filter
Only process rows when both are true:
Column D = LeadColumn T (Outreach Email #1) is blank
This eliminates duplicate first-touch sends.
3) Run enrichment fallback chain
For each row, run in order:
- RocketReach (person-level first)
- Hunter
- Dropcontact
- Site crawl / pattern inference (last resort)
Collect candidates, then verify before writing.
4) Apply strict verification gate
Write to Founder Email only when status is approved:
- auto-accept:
safe - optional accept:
role_account(if campaign allows) - reject to research queue:
catch_all,unknown,invalid
If multiple valid emails exist, prefer person-specific over generic inboxes.
5) Draft queue + send lifecycle
When a draft is created:
- set
On Deck = drafted - color that cell yellow for instant queue visibility
After send, update immediately:
Outreach StatusLast Outreach DateReply StatusBounce Status
No “update later.” Later means data drift.
6) Track every action for auditability
For each processed row, write back:
Verification StatusEmail SourceLast CheckedNotes(accepted/rejected + why)
You should be able to explain any row decision in under 5 seconds.
7) Keep bounce recovery as a separate loop
Do not mix bounce recovery with net-new sending.
Bounce queue loop:
- isolate bounced rows
- find alternate address
- verify alternate
- only re-queue confidence-approved contacts
This keeps your main pipeline predictable.
Why this can replace Clay for many teams
Clay is powerful. But most teams don’t need infinite enrichment branches. They need:
- one source of truth
- strict acceptance criteria
- clean statuses
- repeatable sends
This workflow does that with less overhead and better operational control.
Copy/paste playbooks (.md downloads)
I added two downloadable Markdown files you can copy directly into your own ops docs:
- Workflow SOP:
/downloads/replace-clay-workflow-sop.md - Templates + prompts:
/downloads/replace-clay-copy-paste-templates.md
If you’re implementing this, start with the SOP, then customize templates.
Final operating principles
- Keep state in the sheet, not in your head
- Verify before you write
- Separate net-new from bounce recovery
- Batch everything (10–25 rows) to avoid timeout failures
- Measure quality with bounce/reply outcomes, not enrichment count
That’s how you get a system that actually ships.