This is a LinkedIn engagement → enriched lead → personalized email → multi-channel push pipeline. HeyDigital sends LinkedIn post engagement data via webhook. The workbook extracts the engager identity, hunts for their email via three providers, validates the best result, verifies they still work at the company, qualifies their job title authority, enriches their company profile, filters by geography and headcount, runs competitor analysis, cleans the data, generates a cold email opening line, and pushes the lead to four destinations simultaneously: LeadGrow CRM, Bison (email campaigns), HeyReach (LinkedIn outreach), and a social post content bank.
| Metric | Value |
|---|---|
| Tables in workspace | 33 (5 active for this pipeline) |
| Main table columns | 61 |
| AI (Claygent) calls per row | 10-12 (conditional path-dependent) |
| External API calls per row | 4-7 (Icypeas, Kitt, LeadMagic, EmailGuard, LeadGrow, HeyReach) |
| Email providers in waterfall | 3 (Icypeas → Kitt → LeadMagic) |
| Output destinations | 4 (CUL, Bison, HeyReach, Social Post Bank) |
| AI models used | gpt-4.1-mini (6 calls), gpt-4o-mini (4 calls) |
| Total actions (column types) | 21 enrichment actions + 75 formulas + 1 source + 2 date |
Table t_0tfqyhkneq3VAFhoM6F — "HeyDigital ABM engagers" —
is the heart of the system. Each row represents one LinkedIn post engager flowing through
a linear enrichment chain with conditional branching (waterfall logic on email find/validate).
Here is every phase, in execution order:
conditionalRunFieldIds to chain: CompQual blocks Kitt, Kitt blocks Icypeas, Icypeas blocks LeadMagic.
| Table | ID | Cols | Role | Connected To |
|---|---|---|---|---|
| HeyDigital ABM engagers | t_0tfqyhkneq3VAFhoM6F |
61 | Main pipeline — processes each LinkedIn engager | Routes to Social Post Bank; pushes to CUL, Bison, HeyReach |
| Social Post Bank | t_0tfqyiguHZvcGoypZJV |
12 | Post content archive — receives routed rows | Receives from Main via Route Row action; has Hey Digital Relevance AI enrichment |
| Pull in data from a Webhook Table | t_0tfr7d8dF5mnzfnpzzZ |
19 | Separate company enrichment entry point | Independent webhook → LinkedIn Company Info → HTTP API output |
| Enrich 22K unknowns - headcount | t_0tfnv6iGPatnmaWKaTC |
40 | Bulk headcount enrichment for companies missing data | Likely feeds results back to main table or CRM |
| Enrich missing company information | t_0tcxgijYsmrT5seFVrr |
36 | Gap-fill enrichment for incomplete company profiles | Likely feedback loop to main table |
Primary stitch: The Main table Send table data action (Route Row, Clay Labs package) sends Post Text + Post URL to the Social Post Bank table (t_0tfqyiguHZvcGoypZJV). This is a Clay-native table-to-table routing — no external API needed. The Social Post Bank then enriches with "Hey Digital Relevance" AI scoring.
Secondary stitch (inferred): The enrichment tables (headcount, missing company info) likely feed into the Main table via Clay Lookup or Import from Table actions, pulling enriched company data back into the main pipeline. The "Enrich missing company information" workbook contains multiple tables that appear to be a sub-pipeline for company data.
External stitches: The 4 destination pushes all leave Clay via HTTP API calls — to LeadGrow API, Bison campaigns, and HeyReach. These are the exit points from Clay into the rest of the LeadGrow stack.
Webhook entry: Table 3 ("Pull in data from a Webhook Table") is a separate entry point with its own webhook source. It enriches company data and outputs via HTTP API — possibly used to backfill company info that the main pipeline references.
Clay charges credits per enrichment action. Pricing varies by action type and provider. Approximate credit costs based on Clay published pricing and provider tiers:
| Action Type | Count per Row | Credits Each | Subtotal |
|---|---|---|---|
| Icypeas Find Email | 1 (conditional) | ~8 | 0-8 |
| Kitt HTTP API (find email) | 1 (conditional) | ~2 | 0-2 |
| LeadMagic Find Email | 1 (conditional) | ~6 | 0-6 |
| LeadMagic Validate Email ×3 | 0-3 (conditional) | ~3 | 0-9 |
| Claygent AI (gpt-4o-mini) ×4 | 4 | ~3-5 | 12-20 |
| Use AI (gpt-4.1-mini) ×6 | 6 | ~1-2 | 6-12 |
| EmailGuard HTTP API | 1 | ~2 | 2 |
| Route Row (Send table data) | 1 | ~1 | 1 |
| HeyReach Add to Campaign | 1 | ~3 | 3 |
| LeadGrow CUL (HTTP API) | 1 | ~2 | 2 |
| LeadGrow Bison (HTTP API) | 1 | ~2 | 2 |
| TOTAL (full path) | ~26-65 credits | ||
| TOTAL (avg — waterfall skips 2 finders) | ~40-50 credits |
Claygent actions are expensive. Each Claygent call (the AI agent that navigates LinkedIn for employment verification, company URL finding, domain extraction, company info, and location) costs 3-5 credits minimum. This workbook has 5 Claygent calls per row — that is 15-25 credits just for AI web browsing. The gpt-4.1-mini calls are cheaper but there are 6 of them.
Waterfall email finding: The conditional logic saves costs (only 1-2 of 3 providers run), but Clay still charges for the attempt even if it yields no result.
Credit rounding: Clay rounds up fractional credits. A 0.1 credit action costs 1 credit. This is most punitive for the many small AI calls in this workbook.
Duplicate charges: The Cold Email Personalization v1 and v2 run BOTH — producing two AI-generated opening lines per row. v1 result appears unused; only v2 feeds into First Line and Company Type. That is a wasted AI call per row.
Trigger.dev is built on event-driven task execution with retries, idempotency, and observable state. Each Clay "column" becomes a Trigger.dev task step or independent subtask. The 12-phase pipeline becomes a parent task that orchestrates child tasks, with each phase as a discrete retryable unit.
The key insight: Clay columns are just sequential code with field references.
In Trigger.dev, this is TypeScript with variables — no graph resolution engine needed. The
formula expressions become inline code. Conditional branching becomes if statements.
The waterfall becomes explicit try/catch chains.
| Clay Pattern | Trigger.dev Equivalent |
|---|---|
| Column formula with field references | TypeScript variable assignments. No graph resolution — just sequential code. |
| Conditional action execution (conditionalRunFieldIds) | if statements + early returns. More readable, easier to debug. |
| Waterfall enrichment (try A, if fails try B) | Explicit try/catch + sequential API calls. Trigger.dev retry config handles transient failures. |
| Claygent AI web browsing | Replace with Serper.dev for search + Spider Cloud or ScrapingBee for scraping + OpenAI for extraction. No Claygent platform markup. |
| Credit-based cost model | Pay for actual API usage only (OpenAI tokens, Serper queries, scraping credits). No platform margin on top. |
| Route Row (table-to-table) | INSERT into Supabase/Postgres. Same data model, zero vendor lock-in. |
| Formula language (Clay formulas) | TypeScript. More expressive, testable with Jest, versionable in git. |
| Error handling | Per-step retries with exponential backoff, dead letter queues, alerting. Clay: column turns red, manual fix. |
| Clay Feature | Replacement | Cost Impact |
|---|---|---|
| Icypeas Find Email | Icypeas API directly (bypass Clay ~4x markup) | ~75% cheaper per lookup |
| LeadMagic Find + Validate Email | LeadMagic API directly | ~60% cheaper |
| Kitt (via Clay HTTP API) | Kitt API directly | No change (already direct API call in Clay) |
| Claygent (AI web browsing) | Serper.dev + OpenAI gpt-4o-mini + fetch/scrape | ~80% cheaper (no Claygent markup) |
| Route Row to Social Post Bank | Supabase INSERT | Free (Supabase is already in stack) |
| Country acronym generation (AI) | Deterministic lookup table (200 lines of JSON) | Free — no AI call needed! |
| Author name mapping (formula) | TypeScript Map/switch | Free — no formula engine needed |
| Pluralization rule (formula) | TypeScript pluralize npm package | Free — pure code, tested once |
| Plan | Price | Runs/Month | Concurrency | Environments | Overage |
|---|---|---|---|---|---|
| Hobby | Free | 1,000 | 1 | 1 (dev only) | N/A |
| Pro | $20/mo | 10,000 | 100 | 1 | $0.002/run |
| Team | $100/mo | 100,000 | 1,000 | 5 | $0.001/run |
| Enterprise | Custom | Unlimited | Unlimited | Unlimited | Negotiated |
A "run" = one task execution. Our parent task + ~5 child tasks per row = ~6 runs per lead processed. Child tasks count against the run quota. At 10,000 leads/month, that is ~60,000 total runs. On the Team plan, 100K included runs covers this with room. On Pro, 40K overage at $0.002 = $80 extra.
| Cost Component | Per Row | Notes |
|---|---|---|
| Trigger.dev runs (~6 tasks/row, Team plan) | $0.006 | 6 tasks x $0.001/run (marginal overage cost) |
| OpenAI gpt-4.1-mini (6 calls x ~1K tokens) | $0.004 | $0.60/1M input + $2.40/1M output tokens |
| OpenAI gpt-4o-mini (1-2 calls x ~2K tokens) | $0.001 | Cheaper model, used for company extraction |
| Serper.dev search (5-6 queries for Claygent replacement) | $0.005 | ~$0.001/query at scale |
| Spider Cloud / ScrapingBee (2-3 pages scraped) | $0.006 | ~$0.002/page for LinkedIn + company website scraping |
| Icypeas email lookup | $0.02 | Direct API pricing (vs Clay ~$0.08) |
| LeadMagic find + validate | $0.03 | Direct API pricing (vs Clay ~$0.09) |
| Kitt email lookup | $0.01 | Direct API — same cost since already direct in Clay |
| EmailGuard host lookup | $0.003 | Direct API |
| LeadGrow CUL + Bison API | $0.00 | Own infrastructure — already running |
| HeyReach API | $0.01 | Direct API |
| Supabase INSERT | $0.00 | Already in stack, marginal cost zero |
| TOTAL per row | ~$0.095 |
| Volume (leads/month) | Clay Cost | Trigger.dev Cost | Monthly Savings | Annual Savings |
|---|---|---|---|---|
| 1,000 | $400 – $500 | $95 + $20 (Pro plan) = $115 | $285 – $385 | $3,420 – $4,620 |
| 5,000 | $2,000 – $2,500 | $475 + $20 = $495 | $1,505 – $2,005 | $18,060 – $24,060 |
| 10,000 | $4,000 – $5,000 | $950 + $20 = $970 | $3,030 – $4,030 | $36,360 – $48,360 |
| 50,000 | $20,000 – $25,000 | $4,750 + $100 (Team) + ~$150 overage = $5,000 | $15,000 – $20,000 | $180,000 – $240,000 |
| 100,000 | $40,000 – $50,000 | $9,500 + $100 + ~$400 overage = $10,000 | $30,000 – $40,000 | $360,000 – $480,000 |
lg-trigger CLI for project scaffolding)social_post_bank to replace the Social Post Bank Clay table||, optional chaining). TypeScript requires explicit null checks. Mitigation: Use TypeScript optional chaining (?.) and nullish coalescing (??) — the syntax maps directly to Clay formula patterns.
C:\Users\mitch\Everything_CC\temp\clay-workbook-analysis\table_main_schema.json (165 KB — 61 columns)
C:\Users\mitch\Everything_CC\temp\clay-workbook-analysis\table_routed_target_schema.json (20 KB — Social Post Bank)
C:\Users\mitch\Everything_CC\temp\clay-workbook-analysis\table_webhook_source_schema.json (32 KB — Webhook Table)
C:\Users\mitch\Everything_CC\temp\clay-workbook-analysis\table_enrich_company_schema.json (68 KB — Enrich Company)
C:\Users\mitch\Everything_CC\temp\clay-workbook-analysis\table_enrich_headcount_schema.json (71 KB — Enrich Headcount)
C:\Users\mitch\Everything_CC\temp\clay-workbook-analysis\index.html (this page)
All schemas extracted via clay pull --table [id] from leadgrow-clay-cli.
Analysis performed 2026-06-10. Clay workspace 206846, workbook wb_0tfqyhki4hnphtPTmom.
No records were available (table was empty or API-filtered at extraction time).
Next step: Deploy this page to Cloudflare Pages for team review.
Every prompt follows this structure to maximize caching:
<!-- CACHED (80% of tokens, 50% discount) -->
System prompt + Instructions + Banned words + Scoring rubric + Examples
<!-- UNCACHED (20% of tokens) -->
Variable 1: {{job_title}}
Variable 2: {{company_name}}
Variable 3: {{post_text}}
Variable 4: {{company_description}}
Flex API pricing (gpt-4.1-mini):
company.domain, company.employee_count, company.description, company.industry, company.location, company.products_services, company.linkedin_url — all the data Clay spends 5 Claygent + Serper + Firecrawl to discover is handed to us for free.waterfall-enrich.ts in the GTM orchestrator runs Supabase → MillionVerifier → Kitt → Icypeas → LeadMagic with 20-contact concurrency on Trigger.dev. We wire it in, not build it..00028 per row. The real costs are the enrichment APIs.
| Phase | Provider | Cost | Gate |
|---|---|---|---|
| People enrichment | AI Ark (export-one by LinkedIn URL) | ~.001 | 30% of rows (pre-email) |
| Email + people find | Blitz | TBD | 30% of rows |
| Internal lookup | Supabase contacts table | FREE | 30% of rows |
| Email verification | MillionVerifier | ~.002 | 30% of rows |
| Email find | Kitt (trykitt.ai) | ~.01 | 30% of rows |
| Email find | Icypeas | ~.02 | fallback only |
| Email find (last resort) | LeadMagic | ~.03 | fallback only |
| Employment verification | Apify harvestapi | .004 | 65% (optional toggle) |
| AI calls (x4) | OpenAI Flex API + caching | .00028 | varies by gate |
| Orchestration | Trigger.dev (batched 20x) | .002 | 100% |
| # | Task | Uses | Status |
|---|---|---|---|
| 1 | HeyDigital webhook → Trigger.dev batch endpoint | Trigger.dev HTTP trigger + batch | To build |
| 2 | Job title qualification prompt | OpenAI Flex API + caching pattern | To build |
| 3 | Country lookup table (JSON) | Static file, deterministic | To build (60 lines) |
| 4 | Competitor analysis prompt | OpenAI Flex API + caching | To build |
| 5 | AI Ark export-one integration | ai-ark CLI (own tool) | Exists |
| 6 | Blitz integration | Blitz API | Need API details |
| 7 | Email waterfall | waterfall-enrich.ts (GTM orchestrator) | Exists — wire in |
| 8 | harvestapi integration | Apify harvestapi (paid account) | To build |
| 9 | Name cleaning (regex) | split() + replace() | Trivial (5 lines) |
| 10 | Company name cleaning prompt | OpenAI Flex API + caching | To build |
| 11 | Cold email personalization prompt | OpenAI Flex API + caching | To build |
| 12 | CUL push | Existing LeadGrow API | Exists |
| 13 | Bison push | Existing LeadGrow API | Exists |
| 14 | HeyReach push | HeyReach API | Exists |
| 15 | Supabase social_post_bank | Supabase INSERT | Need schema |
| Cost Line | Amortized/Row | Monthly (10K leads) | % |
|---|---|---|---|
| Email waterfall (amortized across 30% of rows) | .0099 | $99 | 71% |
| harvestapi employment verification (65% of rows) | .0026 | $26 | 19% |
| Trigger.dev platform (batched 20x) | .0010 | $10 | 7% |
| AI Ark person enrichment (30% of rows) | .0003 | $3 | 2% |
| All AI calls combined (Flex + cached, gated) | .0003 | $3 | 2% |
| TOTAL | .014 | $140 |
.30/1M input,
company.domain, company.employee_count, company.description, company.industry, company.location, company.products_services, company.linkedin_url — all the data Clay spends 5 Claygent + Serper + Firecrawl to discover is handed to us for free.waterfall-enrich.ts in the GTM orchestrator runs Supabase → MillionVerifier → Kitt → Icypeas → LeadMagic with 20-contact concurrency on Trigger.dev. We wire it in, not build it.| Phase | Provider | Cost | Gate |
|---|---|---|---|
| People enrichment | AI Ark (export-one by LinkedIn URL) | ~.001 | 30% of rows (pre-email) |
| Email + people find | Blitz | TBD | 30% of rows |
| Internal lookup | Supabase contacts table | FREE | 30% of rows |
| Email verification | MillionVerifier | ~.002 | 30% of rows |
| Email find | Kitt (trykitt.ai) | ~.01 | 30% of rows |
| Email find | Icypeas | ~.02 | fallback only |
| Email find (last resort) | LeadMagic | ~.03 | fallback only |
| Employment verification | Apify harvestapi | .004 | 65% (optional toggle) |
| AI calls (x4) | OpenAI Flex API + caching | .00028 | varies by gate |
| Orchestration | Trigger.dev (batched 20x) | .002 | 100% |
| # | Task | Uses | Status |
|---|---|---|---|
| 1 | HeyDigital webhook → Trigger.dev batch endpoint | Trigger.dev HTTP trigger + batch | To build |
| 2 | Job title qualification prompt | OpenAI Flex API + caching pattern | To build |
| 3 | Country lookup table (JSON) | Static file, deterministic | To build (60 lines) |
| 4 | Competitor analysis prompt | OpenAI Flex API + caching | To build |
| 5 | AI Ark export-one integration | ai-ark CLI (own tool) | Exists |
| 6 | Blitz integration | Blitz API | Need API details |
| 7 | Email waterfall | waterfall-enrich.ts (GTM orchestrator) | Exists — wire in |
| 8 | harvestapi integration | Apify harvestapi (paid account) | To build |
| 9 | Name cleaning (regex) | split() + replace() | Trivial (5 lines) |
| 10 | Company name cleaning prompt | OpenAI Flex API + caching | To build |
| 11 | Cold email personalization prompt | OpenAI Flex API + caching | To build |
| 12 | CUL push | Existing LeadGrow API | Exists |
| 13 | Bison push | Existing LeadGrow API | Exists |
| 14 | HeyReach push | HeyReach API | Exists |
| 15 | Supabase social_post_bank | Supabase INSERT | Need schema |
| Cost Line | Amortized/Row | Monthly (10K leads) | % |
|---|---|---|---|
| Email waterfall (amortized across 30% of rows) | .0099 | $99 | 71% |
| harvestapi employment verification (65% of rows) | .0026 | $26 | 19% |
| Trigger.dev platform (batched 20x) | .0010 | $10 | 7% |
| AI Ark person enrichment (30% of rows) | .0003 | $3 | 2% |
| All AI calls combined (Flex + cached, gated) | .0003 | $3 | 2% |
| TOTAL | .014 | $140 |
.15/1M cached input, $1.20/1M output.
| AI Call | Input Tokens | Output Tokens | Cost (cached) | Runs on | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Job Title Qualification | ~600 (80% cached) | ~40 |
11. Final Architecture — Build-Ready
Three critical realizations from the webhook payload:
11.1 Final Provider Stack
11.2 Final Mermaid — All Corrections Applied
flowchart TD
subgraph TRIGGER["TRIGGER"]
WH["HeyDigital Webhook\n→ Trigger.dev batch endpoint\n(20 rows/batch, 5s window)"] --> PARSE["Parse Payload\nCompany: domain, employees, industry,\nlocation, description, products\nALL FROM WEBHOOK — no Serper needed\nProspect: firstName, lastName,\njobTitle, linkedinUrl\nEngagement: postText, postUrl, author"]
end
subgraph GATE1["GATE 1 — Job Title (100%)"]
PARSE --> JT_AI["AI: Job Title Qualification\nGPT-4.1-mini, Flex API\nCACHED: instructions at top\nUNCACHED: {{jobTitle}} at bottom\nCost: .00014/row"]
end
JT_AI --> JT_CHECK{"score >= 5?"}
JT_CHECK -->|"No (~35%)\nscore 1-4"| PUSH_EARLY["DISQUALIFIED: Bad title\n→ Supabase only (no email push)"]
JT_CHECK -->|"Yes (~65%)\nQualified/Maybe"| PARALLEL["PARALLEL EXECUTION"]
subgraph PARALLEL_RUN["Parallel (~65% of rows)"]
direction LR
EMP["Employment Verification\nApify harvestapi\nlinkedin-profile-scraper\n.004/row\n[OPTIONAL — can toggle off]"]
GEO["Geo + Headcount Filter\nCountry: LOOKUP TABLE (free)\nSource: webhook.location\nEmployees: webhook.employee_count\nCheck: target country + 10-300\nALL DETERMINISTIC — NO AI\nCost: FREE"]
end
PARALLEL --> EMP
PARALLEL --> GEO
EMP --> MERGE["Merge"]
GEO --> MERGE
MERGE --> GEO_CHECK{"In target country\nAND 10-300 employees?"}
GEO_CHECK -->|"No (~25%)"| PUSH_GEO["DISQUALIFIED: Geo/Size\n→ Supabase only"]
GEO_CHECK -->|"Yes (~40%)"| COMP_AI["GATE 3: Competitor Analysis\nGPT-4.1-mini, Flex API, CACHED\nInput: webhook.description + products\nScore: is this a digital agency?\nCost: .00010/row"]
COMP_AI --> COMP_CHECK{"Is prospect?\n(not a digital agency)"}
COMP_CHECK -->|"No (~10%)\nagency/competitor"| PUSH_COMP["DISQUALIFIED: Competitor\n→ Supabase only"]
COMP_CHECK -->|"Yes (~30%)\nqualified prospect"| PEOPLE["PEOPLE + EMAIL PHASE"]
subgraph PEOPLE["People + Email Enrichment (~30%)"]
direction LR
AI_ARK["AI Ark\nexport-one\nby LinkedIn URL\n~.001/row"]
BLITZ["Blitz\nEmail finder +\nPeople data finder"]
SUPABASE["Supabase\nInternal lookup\ncontacts table\nFREE"]
MV["MillionVerifier\nEmail verification\n~.002/row"]
KITT["Kitt API\ntrykitt.ai\n~.01/row"]
ICY["Icypeas API\n~.02/row\n(fallback)"]
LM["LeadMagic API\n~.03/row\n(last resort)"]
AI_ARK --> BLITZ
BLITZ --> SUPABASE
SUPABASE --> MV
MV -->|"no valid"| KITT
KITT -->|"no valid"| ICY
ICY -->|"no valid"| LM
LM --> EMAIL_DONE["Best email +\nprovider tag"]
end
MV -->|"valid email"| EMAIL_DONE
KITT -->|"valid email"| EMAIL_DONE
ICY -->|"valid email"| EMAIL_DONE
EMAIL_DONE --> EMAIL_CHECK{"Email found?"}
EMAIL_CHECK -->|"No"| PUSH_NOEMAIL["NO EMAIL\n→ Push all destinations"]
EMAIL_CHECK -->|"Yes"| CLEAN["DATA CLEANING"]
subgraph CLEAN["Data Cleaning (~30%)"]
direction LR
FN["First Name\nREGEX: split(' ') [0]\nOnly if >1 word\nStrip non-alpha\nCost: FREE"]
CN["Company Name\nGPT-4.1-mini, CACHED\nStrip Inc/LLC\nCreate acronyms\nCost: .00014"]
end
FN --> COLD["COLD EMAIL"]
CN --> COLD
COLD["Cold Email Personalization\nGPT-4.1-mini, CACHED\nONE merged call (not two)\nOutput: firstLine (20 words)\n+ companyType (2-3 words)\nCost: .00016"] --> DEST
subgraph DEST["DESTINATIONS (always, free)"]
direction LR
CUL["CUL: create-or-update\nsend.leadgrow.ai"]
BISON["Bison: attach to campaign\nsend.leadgrow.ai"]
HEY["HeyReach: add to campaign"]
SUPA["Supabase: social_post_bank"]
end
PUSH_EARLY --> DEST
PUSH_GEO --> DEST
PUSH_COMP --> DEST
PUSH_NOEMAIL --> DEST
COLD --> CUL
COLD --> BISON
COLD --> HEY
COLD --> SUPA
CUL --> DONE["DONE"]
BISON --> DONE
HEY --> DONE
SUPA --> DONE
style TRIGGER fill:#0d1117,stroke:#3fb950,color:#3fb950
style GATE1 fill:#0d1117,stroke:#a371f7,color:#a371f7
style PARALLEL_RUN fill:#0d1117,stroke:#39d2c0,color:#39d2c0
style PEOPLE fill:#0d1117,stroke:#d2991d,color:#d2991d
style CLEAN fill:#0d1117,stroke:#58a6ff,color:#58a6ff
style DEST fill:#0d1117,stroke:#3fb950,color:#3fb950
11.3 Build Checklist
11.4 Final Cost — All Corrections~.014
Amortized per row (gated)
~$140
Per 10,000 leads/month
~96%
Savings vs Clay ($4,000-5,000/mo)
Employment verification is OPTIONAL. Toggle it off: cost drops to .011/row ($114/month). The HeyDigital webhook data is real-time engagement — the person was at that company at engagement time. Employment verification catches a tiny edge case and costs 19% of the budget. Start without it, add only if bounce rates warrant it.
.00014 | 100% of rows | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Competitor Analysis | ~400 (80% cached) | ~40 |
11. Final Architecture — Build-Ready
Three critical realizations from the webhook payload:
11.1 Final Provider Stack
11.2 Final Mermaid — All Corrections Applied
flowchart TD
subgraph TRIGGER["TRIGGER"]
WH["HeyDigital Webhook\n→ Trigger.dev batch endpoint\n(20 rows/batch, 5s window)"] --> PARSE["Parse Payload\nCompany: domain, employees, industry,\nlocation, description, products\nALL FROM WEBHOOK — no Serper needed\nProspect: firstName, lastName,\njobTitle, linkedinUrl\nEngagement: postText, postUrl, author"]
end
subgraph GATE1["GATE 1 — Job Title (100%)"]
PARSE --> JT_AI["AI: Job Title Qualification\nGPT-4.1-mini, Flex API\nCACHED: instructions at top\nUNCACHED: {{jobTitle}} at bottom\nCost: .00014/row"]
end
JT_AI --> JT_CHECK{"score >= 5?"}
JT_CHECK -->|"No (~35%)\nscore 1-4"| PUSH_EARLY["DISQUALIFIED: Bad title\n→ Supabase only (no email push)"]
JT_CHECK -->|"Yes (~65%)\nQualified/Maybe"| PARALLEL["PARALLEL EXECUTION"]
subgraph PARALLEL_RUN["Parallel (~65% of rows)"]
direction LR
EMP["Employment Verification\nApify harvestapi\nlinkedin-profile-scraper\n.004/row\n[OPTIONAL — can toggle off]"]
GEO["Geo + Headcount Filter\nCountry: LOOKUP TABLE (free)\nSource: webhook.location\nEmployees: webhook.employee_count\nCheck: target country + 10-300\nALL DETERMINISTIC — NO AI\nCost: FREE"]
end
PARALLEL --> EMP
PARALLEL --> GEO
EMP --> MERGE["Merge"]
GEO --> MERGE
MERGE --> GEO_CHECK{"In target country\nAND 10-300 employees?"}
GEO_CHECK -->|"No (~25%)"| PUSH_GEO["DISQUALIFIED: Geo/Size\n→ Supabase only"]
GEO_CHECK -->|"Yes (~40%)"| COMP_AI["GATE 3: Competitor Analysis\nGPT-4.1-mini, Flex API, CACHED\nInput: webhook.description + products\nScore: is this a digital agency?\nCost: .00010/row"]
COMP_AI --> COMP_CHECK{"Is prospect?\n(not a digital agency)"}
COMP_CHECK -->|"No (~10%)\nagency/competitor"| PUSH_COMP["DISQUALIFIED: Competitor\n→ Supabase only"]
COMP_CHECK -->|"Yes (~30%)\nqualified prospect"| PEOPLE["PEOPLE + EMAIL PHASE"]
subgraph PEOPLE["People + Email Enrichment (~30%)"]
direction LR
AI_ARK["AI Ark\nexport-one\nby LinkedIn URL\n~.001/row"]
BLITZ["Blitz\nEmail finder +\nPeople data finder"]
SUPABASE["Supabase\nInternal lookup\ncontacts table\nFREE"]
MV["MillionVerifier\nEmail verification\n~.002/row"]
KITT["Kitt API\ntrykitt.ai\n~.01/row"]
ICY["Icypeas API\n~.02/row\n(fallback)"]
LM["LeadMagic API\n~.03/row\n(last resort)"]
AI_ARK --> BLITZ
BLITZ --> SUPABASE
SUPABASE --> MV
MV -->|"no valid"| KITT
KITT -->|"no valid"| ICY
ICY -->|"no valid"| LM
LM --> EMAIL_DONE["Best email +\nprovider tag"]
end
MV -->|"valid email"| EMAIL_DONE
KITT -->|"valid email"| EMAIL_DONE
ICY -->|"valid email"| EMAIL_DONE
EMAIL_DONE --> EMAIL_CHECK{"Email found?"}
EMAIL_CHECK -->|"No"| PUSH_NOEMAIL["NO EMAIL\n→ Push all destinations"]
EMAIL_CHECK -->|"Yes"| CLEAN["DATA CLEANING"]
subgraph CLEAN["Data Cleaning (~30%)"]
direction LR
FN["First Name\nREGEX: split(' ') [0]\nOnly if >1 word\nStrip non-alpha\nCost: FREE"]
CN["Company Name\nGPT-4.1-mini, CACHED\nStrip Inc/LLC\nCreate acronyms\nCost: .00014"]
end
FN --> COLD["COLD EMAIL"]
CN --> COLD
COLD["Cold Email Personalization\nGPT-4.1-mini, CACHED\nONE merged call (not two)\nOutput: firstLine (20 words)\n+ companyType (2-3 words)\nCost: .00016"] --> DEST
subgraph DEST["DESTINATIONS (always, free)"]
direction LR
CUL["CUL: create-or-update\nsend.leadgrow.ai"]
BISON["Bison: attach to campaign\nsend.leadgrow.ai"]
HEY["HeyReach: add to campaign"]
SUPA["Supabase: social_post_bank"]
end
PUSH_EARLY --> DEST
PUSH_GEO --> DEST
PUSH_COMP --> DEST
PUSH_NOEMAIL --> DEST
COLD --> CUL
COLD --> BISON
COLD --> HEY
COLD --> SUPA
CUL --> DONE["DONE"]
BISON --> DONE
HEY --> DONE
SUPA --> DONE
style TRIGGER fill:#0d1117,stroke:#3fb950,color:#3fb950
style GATE1 fill:#0d1117,stroke:#a371f7,color:#a371f7
style PARALLEL_RUN fill:#0d1117,stroke:#39d2c0,color:#39d2c0
style PEOPLE fill:#0d1117,stroke:#d2991d,color:#d2991d
style CLEAN fill:#0d1117,stroke:#58a6ff,color:#58a6ff
style DEST fill:#0d1117,stroke:#3fb950,color:#3fb950
11.3 Build Checklist
11.4 Final Cost — All Corrections~.014
Amortized per row (gated)
~$140
Per 10,000 leads/month
~96%
Savings vs Clay ($4,000-5,000/mo)
Employment verification is OPTIONAL. Toggle it off: cost drops to .011/row ($114/month). The HeyDigital webhook data is real-time engagement — the person was at that company at engagement time. Employment verification catches a tiny edge case and costs 19% of the budget. Start without it, add only if bounce rates warrant it.
.00010 | 40% of rows | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Clean Company Name | ~800 (80% cached) | ~50 |
11. Final Architecture — Build-Ready
Three critical realizations from the webhook payload:
11.1 Final Provider Stack
11.2 Final Mermaid — All Corrections Applied
flowchart TD
subgraph TRIGGER["TRIGGER"]
WH["HeyDigital Webhook\n→ Trigger.dev batch endpoint\n(20 rows/batch, 5s window)"] --> PARSE["Parse Payload\nCompany: domain, employees, industry,\nlocation, description, products\nALL FROM WEBHOOK — no Serper needed\nProspect: firstName, lastName,\njobTitle, linkedinUrl\nEngagement: postText, postUrl, author"]
end
subgraph GATE1["GATE 1 — Job Title (100%)"]
PARSE --> JT_AI["AI: Job Title Qualification\nGPT-4.1-mini, Flex API\nCACHED: instructions at top\nUNCACHED: {{jobTitle}} at bottom\nCost: .00014/row"]
end
JT_AI --> JT_CHECK{"score >= 5?"}
JT_CHECK -->|"No (~35%)\nscore 1-4"| PUSH_EARLY["DISQUALIFIED: Bad title\n→ Supabase only (no email push)"]
JT_CHECK -->|"Yes (~65%)\nQualified/Maybe"| PARALLEL["PARALLEL EXECUTION"]
subgraph PARALLEL_RUN["Parallel (~65% of rows)"]
direction LR
EMP["Employment Verification\nApify harvestapi\nlinkedin-profile-scraper\n.004/row\n[OPTIONAL — can toggle off]"]
GEO["Geo + Headcount Filter\nCountry: LOOKUP TABLE (free)\nSource: webhook.location\nEmployees: webhook.employee_count\nCheck: target country + 10-300\nALL DETERMINISTIC — NO AI\nCost: FREE"]
end
PARALLEL --> EMP
PARALLEL --> GEO
EMP --> MERGE["Merge"]
GEO --> MERGE
MERGE --> GEO_CHECK{"In target country\nAND 10-300 employees?"}
GEO_CHECK -->|"No (~25%)"| PUSH_GEO["DISQUALIFIED: Geo/Size\n→ Supabase only"]
GEO_CHECK -->|"Yes (~40%)"| COMP_AI["GATE 3: Competitor Analysis\nGPT-4.1-mini, Flex API, CACHED\nInput: webhook.description + products\nScore: is this a digital agency?\nCost: .00010/row"]
COMP_AI --> COMP_CHECK{"Is prospect?\n(not a digital agency)"}
COMP_CHECK -->|"No (~10%)\nagency/competitor"| PUSH_COMP["DISQUALIFIED: Competitor\n→ Supabase only"]
COMP_CHECK -->|"Yes (~30%)\nqualified prospect"| PEOPLE["PEOPLE + EMAIL PHASE"]
subgraph PEOPLE["People + Email Enrichment (~30%)"]
direction LR
AI_ARK["AI Ark\nexport-one\nby LinkedIn URL\n~.001/row"]
BLITZ["Blitz\nEmail finder +\nPeople data finder"]
SUPABASE["Supabase\nInternal lookup\ncontacts table\nFREE"]
MV["MillionVerifier\nEmail verification\n~.002/row"]
KITT["Kitt API\ntrykitt.ai\n~.01/row"]
ICY["Icypeas API\n~.02/row\n(fallback)"]
LM["LeadMagic API\n~.03/row\n(last resort)"]
AI_ARK --> BLITZ
BLITZ --> SUPABASE
SUPABASE --> MV
MV -->|"no valid"| KITT
KITT -->|"no valid"| ICY
ICY -->|"no valid"| LM
LM --> EMAIL_DONE["Best email +\nprovider tag"]
end
MV -->|"valid email"| EMAIL_DONE
KITT -->|"valid email"| EMAIL_DONE
ICY -->|"valid email"| EMAIL_DONE
EMAIL_DONE --> EMAIL_CHECK{"Email found?"}
EMAIL_CHECK -->|"No"| PUSH_NOEMAIL["NO EMAIL\n→ Push all destinations"]
EMAIL_CHECK -->|"Yes"| CLEAN["DATA CLEANING"]
subgraph CLEAN["Data Cleaning (~30%)"]
direction LR
FN["First Name\nREGEX: split(' ') [0]\nOnly if >1 word\nStrip non-alpha\nCost: FREE"]
CN["Company Name\nGPT-4.1-mini, CACHED\nStrip Inc/LLC\nCreate acronyms\nCost: .00014"]
end
FN --> COLD["COLD EMAIL"]
CN --> COLD
COLD["Cold Email Personalization\nGPT-4.1-mini, CACHED\nONE merged call (not two)\nOutput: firstLine (20 words)\n+ companyType (2-3 words)\nCost: .00016"] --> DEST
subgraph DEST["DESTINATIONS (always, free)"]
direction LR
CUL["CUL: create-or-update\nsend.leadgrow.ai"]
BISON["Bison: attach to campaign\nsend.leadgrow.ai"]
HEY["HeyReach: add to campaign"]
SUPA["Supabase: social_post_bank"]
end
PUSH_EARLY --> DEST
PUSH_GEO --> DEST
PUSH_COMP --> DEST
PUSH_NOEMAIL --> DEST
COLD --> CUL
COLD --> BISON
COLD --> HEY
COLD --> SUPA
CUL --> DONE["DONE"]
BISON --> DONE
HEY --> DONE
SUPA --> DONE
style TRIGGER fill:#0d1117,stroke:#3fb950,color:#3fb950
style GATE1 fill:#0d1117,stroke:#a371f7,color:#a371f7
style PARALLEL_RUN fill:#0d1117,stroke:#39d2c0,color:#39d2c0
style PEOPLE fill:#0d1117,stroke:#d2991d,color:#d2991d
style CLEAN fill:#0d1117,stroke:#58a6ff,color:#58a6ff
style DEST fill:#0d1117,stroke:#3fb950,color:#3fb950
11.3 Build Checklist
11.4 Final Cost — All Corrections~.014
Amortized per row (gated)
~$140
Per 10,000 leads/month
~96%
Savings vs Clay ($4,000-5,000/mo)
Employment verification is OPTIONAL. Toggle it off: cost drops to .011/row ($114/month). The HeyDigital webhook data is real-time engagement — the person was at that company at engagement time. Employment verification catches a tiny edge case and costs 19% of the budget. Start without it, add only if bounce rates warrant it.
.00014 | 30% of rows | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Cold Email Personalization | ~1000 (80% cached) | ~30 |
11. Final Architecture — Build-Ready
Three critical realizations from the webhook payload:
11.1 Final Provider Stack
11.2 Final Mermaid — All Corrections Applied
flowchart TD
subgraph TRIGGER["TRIGGER"]
WH["HeyDigital Webhook\n→ Trigger.dev batch endpoint\n(20 rows/batch, 5s window)"] --> PARSE["Parse Payload\nCompany: domain, employees, industry,\nlocation, description, products\nALL FROM WEBHOOK — no Serper needed\nProspect: firstName, lastName,\njobTitle, linkedinUrl\nEngagement: postText, postUrl, author"]
end
subgraph GATE1["GATE 1 — Job Title (100%)"]
PARSE --> JT_AI["AI: Job Title Qualification\nGPT-4.1-mini, Flex API\nCACHED: instructions at top\nUNCACHED: {{jobTitle}} at bottom\nCost: .00014/row"]
end
JT_AI --> JT_CHECK{"score >= 5?"}
JT_CHECK -->|"No (~35%)\nscore 1-4"| PUSH_EARLY["DISQUALIFIED: Bad title\n→ Supabase only (no email push)"]
JT_CHECK -->|"Yes (~65%)\nQualified/Maybe"| PARALLEL["PARALLEL EXECUTION"]
subgraph PARALLEL_RUN["Parallel (~65% of rows)"]
direction LR
EMP["Employment Verification\nApify harvestapi\nlinkedin-profile-scraper\n.004/row\n[OPTIONAL — can toggle off]"]
GEO["Geo + Headcount Filter\nCountry: LOOKUP TABLE (free)\nSource: webhook.location\nEmployees: webhook.employee_count\nCheck: target country + 10-300\nALL DETERMINISTIC — NO AI\nCost: FREE"]
end
PARALLEL --> EMP
PARALLEL --> GEO
EMP --> MERGE["Merge"]
GEO --> MERGE
MERGE --> GEO_CHECK{"In target country\nAND 10-300 employees?"}
GEO_CHECK -->|"No (~25%)"| PUSH_GEO["DISQUALIFIED: Geo/Size\n→ Supabase only"]
GEO_CHECK -->|"Yes (~40%)"| COMP_AI["GATE 3: Competitor Analysis\nGPT-4.1-mini, Flex API, CACHED\nInput: webhook.description + products\nScore: is this a digital agency?\nCost: .00010/row"]
COMP_AI --> COMP_CHECK{"Is prospect?\n(not a digital agency)"}
COMP_CHECK -->|"No (~10%)\nagency/competitor"| PUSH_COMP["DISQUALIFIED: Competitor\n→ Supabase only"]
COMP_CHECK -->|"Yes (~30%)\nqualified prospect"| PEOPLE["PEOPLE + EMAIL PHASE"]
subgraph PEOPLE["People + Email Enrichment (~30%)"]
direction LR
AI_ARK["AI Ark\nexport-one\nby LinkedIn URL\n~.001/row"]
BLITZ["Blitz\nEmail finder +\nPeople data finder"]
SUPABASE["Supabase\nInternal lookup\ncontacts table\nFREE"]
MV["MillionVerifier\nEmail verification\n~.002/row"]
KITT["Kitt API\ntrykitt.ai\n~.01/row"]
ICY["Icypeas API\n~.02/row\n(fallback)"]
LM["LeadMagic API\n~.03/row\n(last resort)"]
AI_ARK --> BLITZ
BLITZ --> SUPABASE
SUPABASE --> MV
MV -->|"no valid"| KITT
KITT -->|"no valid"| ICY
ICY -->|"no valid"| LM
LM --> EMAIL_DONE["Best email +\nprovider tag"]
end
MV -->|"valid email"| EMAIL_DONE
KITT -->|"valid email"| EMAIL_DONE
ICY -->|"valid email"| EMAIL_DONE
EMAIL_DONE --> EMAIL_CHECK{"Email found?"}
EMAIL_CHECK -->|"No"| PUSH_NOEMAIL["NO EMAIL\n→ Push all destinations"]
EMAIL_CHECK -->|"Yes"| CLEAN["DATA CLEANING"]
subgraph CLEAN["Data Cleaning (~30%)"]
direction LR
FN["First Name\nREGEX: split(' ') [0]\nOnly if >1 word\nStrip non-alpha\nCost: FREE"]
CN["Company Name\nGPT-4.1-mini, CACHED\nStrip Inc/LLC\nCreate acronyms\nCost: .00014"]
end
FN --> COLD["COLD EMAIL"]
CN --> COLD
COLD["Cold Email Personalization\nGPT-4.1-mini, CACHED\nONE merged call (not two)\nOutput: firstLine (20 words)\n+ companyType (2-3 words)\nCost: .00016"] --> DEST
subgraph DEST["DESTINATIONS (always, free)"]
direction LR
CUL["CUL: create-or-update\nsend.leadgrow.ai"]
BISON["Bison: attach to campaign\nsend.leadgrow.ai"]
HEY["HeyReach: add to campaign"]
SUPA["Supabase: social_post_bank"]
end
PUSH_EARLY --> DEST
PUSH_GEO --> DEST
PUSH_COMP --> DEST
PUSH_NOEMAIL --> DEST
COLD --> CUL
COLD --> BISON
COLD --> HEY
COLD --> SUPA
CUL --> DONE["DONE"]
BISON --> DONE
HEY --> DONE
SUPA --> DONE
style TRIGGER fill:#0d1117,stroke:#3fb950,color:#3fb950
style GATE1 fill:#0d1117,stroke:#a371f7,color:#a371f7
style PARALLEL_RUN fill:#0d1117,stroke:#39d2c0,color:#39d2c0
style PEOPLE fill:#0d1117,stroke:#d2991d,color:#d2991d
style CLEAN fill:#0d1117,stroke:#58a6ff,color:#58a6ff
style DEST fill:#0d1117,stroke:#3fb950,color:#3fb950
11.3 Build Checklist
11.4 Final Cost — All Corrections~.014
Amortized per row (gated)
~$140
Per 10,000 leads/month
~96%
Savings vs Clay ($4,000-5,000/mo)
Employment verification is OPTIONAL. Toggle it off: cost drops to .011/row ($114/month). The HeyDigital webhook data is real-time engagement — the person was at that company at engagement time. Employment verification catches a tiny edge case and costs 19% of the budget. Start without it, add only if bounce rates warrant it.
.00016 | 30% of rows | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| TOTAL AI (amortized per row) |
11. Final Architecture — Build-Ready
Three critical realizations from the webhook payload:
11.1 Final Provider Stack
11.2 Final Mermaid — All Corrections Applied
flowchart TD
subgraph TRIGGER["TRIGGER"]
WH["HeyDigital Webhook\n→ Trigger.dev batch endpoint\n(20 rows/batch, 5s window)"] --> PARSE["Parse Payload\nCompany: domain, employees, industry,\nlocation, description, products\nALL FROM WEBHOOK — no Serper needed\nProspect: firstName, lastName,\njobTitle, linkedinUrl\nEngagement: postText, postUrl, author"]
end
subgraph GATE1["GATE 1 — Job Title (100%)"]
PARSE --> JT_AI["AI: Job Title Qualification\nGPT-4.1-mini, Flex API\nCACHED: instructions at top\nUNCACHED: {{jobTitle}} at bottom\nCost: .00014/row"]
end
JT_AI --> JT_CHECK{"score >= 5?"}
JT_CHECK -->|"No (~35%)\nscore 1-4"| PUSH_EARLY["DISQUALIFIED: Bad title\n→ Supabase only (no email push)"]
JT_CHECK -->|"Yes (~65%)\nQualified/Maybe"| PARALLEL["PARALLEL EXECUTION"]
subgraph PARALLEL_RUN["Parallel (~65% of rows)"]
direction LR
EMP["Employment Verification\nApify harvestapi\nlinkedin-profile-scraper\n.004/row\n[OPTIONAL — can toggle off]"]
GEO["Geo + Headcount Filter\nCountry: LOOKUP TABLE (free)\nSource: webhook.location\nEmployees: webhook.employee_count\nCheck: target country + 10-300\nALL DETERMINISTIC — NO AI\nCost: FREE"]
end
PARALLEL --> EMP
PARALLEL --> GEO
EMP --> MERGE["Merge"]
GEO --> MERGE
MERGE --> GEO_CHECK{"In target country\nAND 10-300 employees?"}
GEO_CHECK -->|"No (~25%)"| PUSH_GEO["DISQUALIFIED: Geo/Size\n→ Supabase only"]
GEO_CHECK -->|"Yes (~40%)"| COMP_AI["GATE 3: Competitor Analysis\nGPT-4.1-mini, Flex API, CACHED\nInput: webhook.description + products\nScore: is this a digital agency?\nCost: .00010/row"]
COMP_AI --> COMP_CHECK{"Is prospect?\n(not a digital agency)"}
COMP_CHECK -->|"No (~10%)\nagency/competitor"| PUSH_COMP["DISQUALIFIED: Competitor\n→ Supabase only"]
COMP_CHECK -->|"Yes (~30%)\nqualified prospect"| PEOPLE["PEOPLE + EMAIL PHASE"]
subgraph PEOPLE["People + Email Enrichment (~30%)"]
direction LR
AI_ARK["AI Ark\nexport-one\nby LinkedIn URL\n~.001/row"]
BLITZ["Blitz\nEmail finder +\nPeople data finder"]
SUPABASE["Supabase\nInternal lookup\ncontacts table\nFREE"]
MV["MillionVerifier\nEmail verification\n~.002/row"]
KITT["Kitt API\ntrykitt.ai\n~.01/row"]
ICY["Icypeas API\n~.02/row\n(fallback)"]
LM["LeadMagic API\n~.03/row\n(last resort)"]
AI_ARK --> BLITZ
BLITZ --> SUPABASE
SUPABASE --> MV
MV -->|"no valid"| KITT
KITT -->|"no valid"| ICY
ICY -->|"no valid"| LM
LM --> EMAIL_DONE["Best email +\nprovider tag"]
end
MV -->|"valid email"| EMAIL_DONE
KITT -->|"valid email"| EMAIL_DONE
ICY -->|"valid email"| EMAIL_DONE
EMAIL_DONE --> EMAIL_CHECK{"Email found?"}
EMAIL_CHECK -->|"No"| PUSH_NOEMAIL["NO EMAIL\n→ Push all destinations"]
EMAIL_CHECK -->|"Yes"| CLEAN["DATA CLEANING"]
subgraph CLEAN["Data Cleaning (~30%)"]
direction LR
FN["First Name\nREGEX: split(' ') [0]\nOnly if >1 word\nStrip non-alpha\nCost: FREE"]
CN["Company Name\nGPT-4.1-mini, CACHED\nStrip Inc/LLC\nCreate acronyms\nCost: .00014"]
end
FN --> COLD["COLD EMAIL"]
CN --> COLD
COLD["Cold Email Personalization\nGPT-4.1-mini, CACHED\nONE merged call (not two)\nOutput: firstLine (20 words)\n+ companyType (2-3 words)\nCost: .00016"] --> DEST
subgraph DEST["DESTINATIONS (always, free)"]
direction LR
CUL["CUL: create-or-update\nsend.leadgrow.ai"]
BISON["Bison: attach to campaign\nsend.leadgrow.ai"]
HEY["HeyReach: add to campaign"]
SUPA["Supabase: social_post_bank"]
end
PUSH_EARLY --> DEST
PUSH_GEO --> DEST
PUSH_COMP --> DEST
PUSH_NOEMAIL --> DEST
COLD --> CUL
COLD --> BISON
COLD --> HEY
COLD --> SUPA
CUL --> DONE["DONE"]
BISON --> DONE
HEY --> DONE
SUPA --> DONE
style TRIGGER fill:#0d1117,stroke:#3fb950,color:#3fb950
style GATE1 fill:#0d1117,stroke:#a371f7,color:#a371f7
style PARALLEL_RUN fill:#0d1117,stroke:#39d2c0,color:#39d2c0
style PEOPLE fill:#0d1117,stroke:#d2991d,color:#d2991d
style CLEAN fill:#0d1117,stroke:#58a6ff,color:#58a6ff
style DEST fill:#0d1117,stroke:#3fb950,color:#3fb950
11.3 Build Checklist
11.4 Final Cost — All Corrections~.014
Amortized per row (gated)
~$140
Per 10,000 leads/month
~96%
Savings vs Clay ($4,000-5,000/mo)
Employment verification is OPTIONAL. Toggle it off: cost drops to .011/row ($114/month). The HeyDigital webhook data is real-time engagement — the person was at that company at engagement time. Employment verification catches a tiny edge case and costs 19% of the budget. Start without it, add only if bounce rates warrant it.
.00028 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Cost Line | Per Row (ungated) | % of Rows That Hit | Amortized | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| harvestapi employment verification |
11. Final Architecture — Build-Ready
Three critical realizations from the webhook payload:
11.1 Final Provider Stack
11.2 Final Mermaid — All Corrections Applied
flowchart TD
subgraph TRIGGER["TRIGGER"]
WH["HeyDigital Webhook\n→ Trigger.dev batch endpoint\n(20 rows/batch, 5s window)"] --> PARSE["Parse Payload\nCompany: domain, employees, industry,\nlocation, description, products\nALL FROM WEBHOOK — no Serper needed\nProspect: firstName, lastName,\njobTitle, linkedinUrl\nEngagement: postText, postUrl, author"]
end
subgraph GATE1["GATE 1 — Job Title (100%)"]
PARSE --> JT_AI["AI: Job Title Qualification\nGPT-4.1-mini, Flex API\nCACHED: instructions at top\nUNCACHED: {{jobTitle}} at bottom\nCost: .00014/row"]
end
JT_AI --> JT_CHECK{"score >= 5?"}
JT_CHECK -->|"No (~35%)\nscore 1-4"| PUSH_EARLY["DISQUALIFIED: Bad title\n→ Supabase only (no email push)"]
JT_CHECK -->|"Yes (~65%)\nQualified/Maybe"| PARALLEL["PARALLEL EXECUTION"]
subgraph PARALLEL_RUN["Parallel (~65% of rows)"]
direction LR
EMP["Employment Verification\nApify harvestapi\nlinkedin-profile-scraper\n.004/row\n[OPTIONAL — can toggle off]"]
GEO["Geo + Headcount Filter\nCountry: LOOKUP TABLE (free)\nSource: webhook.location\nEmployees: webhook.employee_count\nCheck: target country + 10-300\nALL DETERMINISTIC — NO AI\nCost: FREE"]
end
PARALLEL --> EMP
PARALLEL --> GEO
EMP --> MERGE["Merge"]
GEO --> MERGE
MERGE --> GEO_CHECK{"In target country\nAND 10-300 employees?"}
GEO_CHECK -->|"No (~25%)"| PUSH_GEO["DISQUALIFIED: Geo/Size\n→ Supabase only"]
GEO_CHECK -->|"Yes (~40%)"| COMP_AI["GATE 3: Competitor Analysis\nGPT-4.1-mini, Flex API, CACHED\nInput: webhook.description + products\nScore: is this a digital agency?\nCost: .00010/row"]
COMP_AI --> COMP_CHECK{"Is prospect?\n(not a digital agency)"}
COMP_CHECK -->|"No (~10%)\nagency/competitor"| PUSH_COMP["DISQUALIFIED: Competitor\n→ Supabase only"]
COMP_CHECK -->|"Yes (~30%)\nqualified prospect"| PEOPLE["PEOPLE + EMAIL PHASE"]
subgraph PEOPLE["People + Email Enrichment (~30%)"]
direction LR
AI_ARK["AI Ark\nexport-one\nby LinkedIn URL\n~.001/row"]
BLITZ["Blitz\nEmail finder +\nPeople data finder"]
SUPABASE["Supabase\nInternal lookup\ncontacts table\nFREE"]
MV["MillionVerifier\nEmail verification\n~.002/row"]
KITT["Kitt API\ntrykitt.ai\n~.01/row"]
ICY["Icypeas API\n~.02/row\n(fallback)"]
LM["LeadMagic API\n~.03/row\n(last resort)"]
AI_ARK --> BLITZ
BLITZ --> SUPABASE
SUPABASE --> MV
MV -->|"no valid"| KITT
KITT -->|"no valid"| ICY
ICY -->|"no valid"| LM
LM --> EMAIL_DONE["Best email +\nprovider tag"]
end
MV -->|"valid email"| EMAIL_DONE
KITT -->|"valid email"| EMAIL_DONE
ICY -->|"valid email"| EMAIL_DONE
EMAIL_DONE --> EMAIL_CHECK{"Email found?"}
EMAIL_CHECK -->|"No"| PUSH_NOEMAIL["NO EMAIL\n→ Push all destinations"]
EMAIL_CHECK -->|"Yes"| CLEAN["DATA CLEANING"]
subgraph CLEAN["Data Cleaning (~30%)"]
direction LR
FN["First Name\nREGEX: split(' ') [0]\nOnly if >1 word\nStrip non-alpha\nCost: FREE"]
CN["Company Name\nGPT-4.1-mini, CACHED\nStrip Inc/LLC\nCreate acronyms\nCost: .00014"]
end
FN --> COLD["COLD EMAIL"]
CN --> COLD
COLD["Cold Email Personalization\nGPT-4.1-mini, CACHED\nONE merged call (not two)\nOutput: firstLine (20 words)\n+ companyType (2-3 words)\nCost: .00016"] --> DEST
subgraph DEST["DESTINATIONS (always, free)"]
direction LR
CUL["CUL: create-or-update\nsend.leadgrow.ai"]
BISON["Bison: attach to campaign\nsend.leadgrow.ai"]
HEY["HeyReach: add to campaign"]
SUPA["Supabase: social_post_bank"]
end
PUSH_EARLY --> DEST
PUSH_GEO --> DEST
PUSH_COMP --> DEST
PUSH_NOEMAIL --> DEST
COLD --> CUL
COLD --> BISON
COLD --> HEY
COLD --> SUPA
CUL --> DONE["DONE"]
BISON --> DONE
HEY --> DONE
SUPA --> DONE
style TRIGGER fill:#0d1117,stroke:#3fb950,color:#3fb950
style GATE1 fill:#0d1117,stroke:#a371f7,color:#a371f7
style PARALLEL_RUN fill:#0d1117,stroke:#39d2c0,color:#39d2c0
style PEOPLE fill:#0d1117,stroke:#d2991d,color:#d2991d
style CLEAN fill:#0d1117,stroke:#58a6ff,color:#58a6ff
style DEST fill:#0d1117,stroke:#3fb950,color:#3fb950
11.3 Build Checklist
11.4 Final Cost — All Corrections~.014
Amortized per row (gated)
~$140
Per 10,000 leads/month
~96%
Savings vs Clay ($4,000-5,000/mo)
Employment verification is OPTIONAL. Toggle it off: cost drops to .011/row ($114/month). The HeyDigital webhook data is real-time engagement — the person was at that company at engagement time. Employment verification catches a tiny edge case and costs 19% of the budget. Start without it, add only if bounce rates warrant it.
.004 | 65% |
11. Final Architecture — Build-Ready
Three critical realizations from the webhook payload:
11.1 Final Provider Stack
11.2 Final Mermaid — All Corrections Applied
flowchart TD
subgraph TRIGGER["TRIGGER"]
WH["HeyDigital Webhook\n→ Trigger.dev batch endpoint\n(20 rows/batch, 5s window)"] --> PARSE["Parse Payload\nCompany: domain, employees, industry,\nlocation, description, products\nALL FROM WEBHOOK — no Serper needed\nProspect: firstName, lastName,\njobTitle, linkedinUrl\nEngagement: postText, postUrl, author"]
end
subgraph GATE1["GATE 1 — Job Title (100%)"]
PARSE --> JT_AI["AI: Job Title Qualification\nGPT-4.1-mini, Flex API\nCACHED: instructions at top\nUNCACHED: {{jobTitle}} at bottom\nCost: .00014/row"]
end
JT_AI --> JT_CHECK{"score >= 5?"}
JT_CHECK -->|"No (~35%)\nscore 1-4"| PUSH_EARLY["DISQUALIFIED: Bad title\n→ Supabase only (no email push)"]
JT_CHECK -->|"Yes (~65%)\nQualified/Maybe"| PARALLEL["PARALLEL EXECUTION"]
subgraph PARALLEL_RUN["Parallel (~65% of rows)"]
direction LR
EMP["Employment Verification\nApify harvestapi\nlinkedin-profile-scraper\n.004/row\n[OPTIONAL — can toggle off]"]
GEO["Geo + Headcount Filter\nCountry: LOOKUP TABLE (free)\nSource: webhook.location\nEmployees: webhook.employee_count\nCheck: target country + 10-300\nALL DETERMINISTIC — NO AI\nCost: FREE"]
end
PARALLEL --> EMP
PARALLEL --> GEO
EMP --> MERGE["Merge"]
GEO --> MERGE
MERGE --> GEO_CHECK{"In target country\nAND 10-300 employees?"}
GEO_CHECK -->|"No (~25%)"| PUSH_GEO["DISQUALIFIED: Geo/Size\n→ Supabase only"]
GEO_CHECK -->|"Yes (~40%)"| COMP_AI["GATE 3: Competitor Analysis\nGPT-4.1-mini, Flex API, CACHED\nInput: webhook.description + products\nScore: is this a digital agency?\nCost: .00010/row"]
COMP_AI --> COMP_CHECK{"Is prospect?\n(not a digital agency)"}
COMP_CHECK -->|"No (~10%)\nagency/competitor"| PUSH_COMP["DISQUALIFIED: Competitor\n→ Supabase only"]
COMP_CHECK -->|"Yes (~30%)\nqualified prospect"| PEOPLE["PEOPLE + EMAIL PHASE"]
subgraph PEOPLE["People + Email Enrichment (~30%)"]
direction LR
AI_ARK["AI Ark\nexport-one\nby LinkedIn URL\n~.001/row"]
BLITZ["Blitz\nEmail finder +\nPeople data finder"]
SUPABASE["Supabase\nInternal lookup\ncontacts table\nFREE"]
MV["MillionVerifier\nEmail verification\n~.002/row"]
KITT["Kitt API\ntrykitt.ai\n~.01/row"]
ICY["Icypeas API\n~.02/row\n(fallback)"]
LM["LeadMagic API\n~.03/row\n(last resort)"]
AI_ARK --> BLITZ
BLITZ --> SUPABASE
SUPABASE --> MV
MV -->|"no valid"| KITT
KITT -->|"no valid"| ICY
ICY -->|"no valid"| LM
LM --> EMAIL_DONE["Best email +\nprovider tag"]
end
MV -->|"valid email"| EMAIL_DONE
KITT -->|"valid email"| EMAIL_DONE
ICY -->|"valid email"| EMAIL_DONE
EMAIL_DONE --> EMAIL_CHECK{"Email found?"}
EMAIL_CHECK -->|"No"| PUSH_NOEMAIL["NO EMAIL\n→ Push all destinations"]
EMAIL_CHECK -->|"Yes"| CLEAN["DATA CLEANING"]
subgraph CLEAN["Data Cleaning (~30%)"]
direction LR
FN["First Name\nREGEX: split(' ') [0]\nOnly if >1 word\nStrip non-alpha\nCost: FREE"]
CN["Company Name\nGPT-4.1-mini, CACHED\nStrip Inc/LLC\nCreate acronyms\nCost: .00014"]
end
FN --> COLD["COLD EMAIL"]
CN --> COLD
COLD["Cold Email Personalization\nGPT-4.1-mini, CACHED\nONE merged call (not two)\nOutput: firstLine (20 words)\n+ companyType (2-3 words)\nCost: .00016"] --> DEST
subgraph DEST["DESTINATIONS (always, free)"]
direction LR
CUL["CUL: create-or-update\nsend.leadgrow.ai"]
BISON["Bison: attach to campaign\nsend.leadgrow.ai"]
HEY["HeyReach: add to campaign"]
SUPA["Supabase: social_post_bank"]
end
PUSH_EARLY --> DEST
PUSH_GEO --> DEST
PUSH_COMP --> DEST
PUSH_NOEMAIL --> DEST
COLD --> CUL
COLD --> BISON
COLD --> HEY
COLD --> SUPA
CUL --> DONE["DONE"]
BISON --> DONE
HEY --> DONE
SUPA --> DONE
style TRIGGER fill:#0d1117,stroke:#3fb950,color:#3fb950
style GATE1 fill:#0d1117,stroke:#a371f7,color:#a371f7
style PARALLEL_RUN fill:#0d1117,stroke:#39d2c0,color:#39d2c0
style PEOPLE fill:#0d1117,stroke:#d2991d,color:#d2991d
style CLEAN fill:#0d1117,stroke:#58a6ff,color:#58a6ff
style DEST fill:#0d1117,stroke:#3fb950,color:#3fb950
11.3 Build Checklist
11.4 Final Cost — All Corrections~.014
Amortized per row (gated)
~$140
Per 10,000 leads/month
~96%
Savings vs Clay ($4,000-5,000/mo)
Employment verification is OPTIONAL. Toggle it off: cost drops to .011/row ($114/month). The HeyDigital webhook data is real-time engagement — the person was at that company at engagement time. Employment verification catches a tiny edge case and costs 19% of the budget. Start without it, add only if bounce rates warrant it.
.0026 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Serper (4-5 Google searches) |
11. Final Architecture — Build-Ready
Three critical realizations from the webhook payload:
11.1 Final Provider Stack
11.2 Final Mermaid — All Corrections Applied
flowchart TD
subgraph TRIGGER["TRIGGER"]
WH["HeyDigital Webhook\n→ Trigger.dev batch endpoint\n(20 rows/batch, 5s window)"] --> PARSE["Parse Payload\nCompany: domain, employees, industry,\nlocation, description, products\nALL FROM WEBHOOK — no Serper needed\nProspect: firstName, lastName,\njobTitle, linkedinUrl\nEngagement: postText, postUrl, author"]
end
subgraph GATE1["GATE 1 — Job Title (100%)"]
PARSE --> JT_AI["AI: Job Title Qualification\nGPT-4.1-mini, Flex API\nCACHED: instructions at top\nUNCACHED: {{jobTitle}} at bottom\nCost: .00014/row"]
end
JT_AI --> JT_CHECK{"score >= 5?"}
JT_CHECK -->|"No (~35%)\nscore 1-4"| PUSH_EARLY["DISQUALIFIED: Bad title\n→ Supabase only (no email push)"]
JT_CHECK -->|"Yes (~65%)\nQualified/Maybe"| PARALLEL["PARALLEL EXECUTION"]
subgraph PARALLEL_RUN["Parallel (~65% of rows)"]
direction LR
EMP["Employment Verification\nApify harvestapi\nlinkedin-profile-scraper\n.004/row\n[OPTIONAL — can toggle off]"]
GEO["Geo + Headcount Filter\nCountry: LOOKUP TABLE (free)\nSource: webhook.location\nEmployees: webhook.employee_count\nCheck: target country + 10-300\nALL DETERMINISTIC — NO AI\nCost: FREE"]
end
PARALLEL --> EMP
PARALLEL --> GEO
EMP --> MERGE["Merge"]
GEO --> MERGE
MERGE --> GEO_CHECK{"In target country\nAND 10-300 employees?"}
GEO_CHECK -->|"No (~25%)"| PUSH_GEO["DISQUALIFIED: Geo/Size\n→ Supabase only"]
GEO_CHECK -->|"Yes (~40%)"| COMP_AI["GATE 3: Competitor Analysis\nGPT-4.1-mini, Flex API, CACHED\nInput: webhook.description + products\nScore: is this a digital agency?\nCost: .00010/row"]
COMP_AI --> COMP_CHECK{"Is prospect?\n(not a digital agency)"}
COMP_CHECK -->|"No (~10%)\nagency/competitor"| PUSH_COMP["DISQUALIFIED: Competitor\n→ Supabase only"]
COMP_CHECK -->|"Yes (~30%)\nqualified prospect"| PEOPLE["PEOPLE + EMAIL PHASE"]
subgraph PEOPLE["People + Email Enrichment (~30%)"]
direction LR
AI_ARK["AI Ark\nexport-one\nby LinkedIn URL\n~.001/row"]
BLITZ["Blitz\nEmail finder +\nPeople data finder"]
SUPABASE["Supabase\nInternal lookup\ncontacts table\nFREE"]
MV["MillionVerifier\nEmail verification\n~.002/row"]
KITT["Kitt API\ntrykitt.ai\n~.01/row"]
ICY["Icypeas API\n~.02/row\n(fallback)"]
LM["LeadMagic API\n~.03/row\n(last resort)"]
AI_ARK --> BLITZ
BLITZ --> SUPABASE
SUPABASE --> MV
MV -->|"no valid"| KITT
KITT -->|"no valid"| ICY
ICY -->|"no valid"| LM
LM --> EMAIL_DONE["Best email +\nprovider tag"]
end
MV -->|"valid email"| EMAIL_DONE
KITT -->|"valid email"| EMAIL_DONE
ICY -->|"valid email"| EMAIL_DONE
EMAIL_DONE --> EMAIL_CHECK{"Email found?"}
EMAIL_CHECK -->|"No"| PUSH_NOEMAIL["NO EMAIL\n→ Push all destinations"]
EMAIL_CHECK -->|"Yes"| CLEAN["DATA CLEANING"]
subgraph CLEAN["Data Cleaning (~30%)"]
direction LR
FN["First Name\nREGEX: split(' ') [0]\nOnly if >1 word\nStrip non-alpha\nCost: FREE"]
CN["Company Name\nGPT-4.1-mini, CACHED\nStrip Inc/LLC\nCreate acronyms\nCost: .00014"]
end
FN --> COLD["COLD EMAIL"]
CN --> COLD
COLD["Cold Email Personalization\nGPT-4.1-mini, CACHED\nONE merged call (not two)\nOutput: firstLine (20 words)\n+ companyType (2-3 words)\nCost: .00016"] --> DEST
subgraph DEST["DESTINATIONS (always, free)"]
direction LR
CUL["CUL: create-or-update\nsend.leadgrow.ai"]
BISON["Bison: attach to campaign\nsend.leadgrow.ai"]
HEY["HeyReach: add to campaign"]
SUPA["Supabase: social_post_bank"]
end
PUSH_EARLY --> DEST
PUSH_GEO --> DEST
PUSH_COMP --> DEST
PUSH_NOEMAIL --> DEST
COLD --> CUL
COLD --> BISON
COLD --> HEY
COLD --> SUPA
CUL --> DONE["DONE"]
BISON --> DONE
HEY --> DONE
SUPA --> DONE
style TRIGGER fill:#0d1117,stroke:#3fb950,color:#3fb950
style GATE1 fill:#0d1117,stroke:#a371f7,color:#a371f7
style PARALLEL_RUN fill:#0d1117,stroke:#39d2c0,color:#39d2c0
style PEOPLE fill:#0d1117,stroke:#d2991d,color:#d2991d
style CLEAN fill:#0d1117,stroke:#58a6ff,color:#58a6ff
style DEST fill:#0d1117,stroke:#3fb950,color:#3fb950
11.3 Build Checklist
11.4 Final Cost — All Corrections~.014
Amortized per row (gated)
~$140
Per 10,000 leads/month
~96%
Savings vs Clay ($4,000-5,000/mo)
Employment verification is OPTIONAL. Toggle it off: cost drops to .011/row ($114/month). The HeyDigital webhook data is real-time engagement — the person was at that company at engagement time. Employment verification catches a tiny edge case and costs 19% of the budget. Start without it, add only if bounce rates warrant it.
.005 | 65% |
11. Final Architecture — Build-Ready
Three critical realizations from the webhook payload:
11.1 Final Provider Stack
11.2 Final Mermaid — All Corrections Applied
flowchart TD
subgraph TRIGGER["TRIGGER"]
WH["HeyDigital Webhook\n→ Trigger.dev batch endpoint\n(20 rows/batch, 5s window)"] --> PARSE["Parse Payload\nCompany: domain, employees, industry,\nlocation, description, products\nALL FROM WEBHOOK — no Serper needed\nProspect: firstName, lastName,\njobTitle, linkedinUrl\nEngagement: postText, postUrl, author"]
end
subgraph GATE1["GATE 1 — Job Title (100%)"]
PARSE --> JT_AI["AI: Job Title Qualification\nGPT-4.1-mini, Flex API\nCACHED: instructions at top\nUNCACHED: {{jobTitle}} at bottom\nCost: .00014/row"]
end
JT_AI --> JT_CHECK{"score >= 5?"}
JT_CHECK -->|"No (~35%)\nscore 1-4"| PUSH_EARLY["DISQUALIFIED: Bad title\n→ Supabase only (no email push)"]
JT_CHECK -->|"Yes (~65%)\nQualified/Maybe"| PARALLEL["PARALLEL EXECUTION"]
subgraph PARALLEL_RUN["Parallel (~65% of rows)"]
direction LR
EMP["Employment Verification\nApify harvestapi\nlinkedin-profile-scraper\n.004/row\n[OPTIONAL — can toggle off]"]
GEO["Geo + Headcount Filter\nCountry: LOOKUP TABLE (free)\nSource: webhook.location\nEmployees: webhook.employee_count\nCheck: target country + 10-300\nALL DETERMINISTIC — NO AI\nCost: FREE"]
end
PARALLEL --> EMP
PARALLEL --> GEO
EMP --> MERGE["Merge"]
GEO --> MERGE
MERGE --> GEO_CHECK{"In target country\nAND 10-300 employees?"}
GEO_CHECK -->|"No (~25%)"| PUSH_GEO["DISQUALIFIED: Geo/Size\n→ Supabase only"]
GEO_CHECK -->|"Yes (~40%)"| COMP_AI["GATE 3: Competitor Analysis\nGPT-4.1-mini, Flex API, CACHED\nInput: webhook.description + products\nScore: is this a digital agency?\nCost: .00010/row"]
COMP_AI --> COMP_CHECK{"Is prospect?\n(not a digital agency)"}
COMP_CHECK -->|"No (~10%)\nagency/competitor"| PUSH_COMP["DISQUALIFIED: Competitor\n→ Supabase only"]
COMP_CHECK -->|"Yes (~30%)\nqualified prospect"| PEOPLE["PEOPLE + EMAIL PHASE"]
subgraph PEOPLE["People + Email Enrichment (~30%)"]
direction LR
AI_ARK["AI Ark\nexport-one\nby LinkedIn URL\n~.001/row"]
BLITZ["Blitz\nEmail finder +\nPeople data finder"]
SUPABASE["Supabase\nInternal lookup\ncontacts table\nFREE"]
MV["MillionVerifier\nEmail verification\n~.002/row"]
KITT["Kitt API\ntrykitt.ai\n~.01/row"]
ICY["Icypeas API\n~.02/row\n(fallback)"]
LM["LeadMagic API\n~.03/row\n(last resort)"]
AI_ARK --> BLITZ
BLITZ --> SUPABASE
SUPABASE --> MV
MV -->|"no valid"| KITT
KITT -->|"no valid"| ICY
ICY -->|"no valid"| LM
LM --> EMAIL_DONE["Best email +\nprovider tag"]
end
MV -->|"valid email"| EMAIL_DONE
KITT -->|"valid email"| EMAIL_DONE
ICY -->|"valid email"| EMAIL_DONE
EMAIL_DONE --> EMAIL_CHECK{"Email found?"}
EMAIL_CHECK -->|"No"| PUSH_NOEMAIL["NO EMAIL\n→ Push all destinations"]
EMAIL_CHECK -->|"Yes"| CLEAN["DATA CLEANING"]
subgraph CLEAN["Data Cleaning (~30%)"]
direction LR
FN["First Name\nREGEX: split(' ') [0]\nOnly if >1 word\nStrip non-alpha\nCost: FREE"]
CN["Company Name\nGPT-4.1-mini, CACHED\nStrip Inc/LLC\nCreate acronyms\nCost: .00014"]
end
FN --> COLD["COLD EMAIL"]
CN --> COLD
COLD["Cold Email Personalization\nGPT-4.1-mini, CACHED\nONE merged call (not two)\nOutput: firstLine (20 words)\n+ companyType (2-3 words)\nCost: .00016"] --> DEST
subgraph DEST["DESTINATIONS (always, free)"]
direction LR
CUL["CUL: create-or-update\nsend.leadgrow.ai"]
BISON["Bison: attach to campaign\nsend.leadgrow.ai"]
HEY["HeyReach: add to campaign"]
SUPA["Supabase: social_post_bank"]
end
PUSH_EARLY --> DEST
PUSH_GEO --> DEST
PUSH_COMP --> DEST
PUSH_NOEMAIL --> DEST
COLD --> CUL
COLD --> BISON
COLD --> HEY
COLD --> SUPA
CUL --> DONE["DONE"]
BISON --> DONE
HEY --> DONE
SUPA --> DONE
style TRIGGER fill:#0d1117,stroke:#3fb950,color:#3fb950
style GATE1 fill:#0d1117,stroke:#a371f7,color:#a371f7
style PARALLEL_RUN fill:#0d1117,stroke:#39d2c0,color:#39d2c0
style PEOPLE fill:#0d1117,stroke:#d2991d,color:#d2991d
style CLEAN fill:#0d1117,stroke:#58a6ff,color:#58a6ff
style DEST fill:#0d1117,stroke:#3fb950,color:#3fb950
11.3 Build Checklist
11.4 Final Cost — All Corrections~.014
Amortized per row (gated)
~$140
Per 10,000 leads/month
~96%
Savings vs Clay ($4,000-5,000/mo)
Employment verification is OPTIONAL. Toggle it off: cost drops to .011/row ($114/month). The HeyDigital webhook data is real-time engagement — the person was at that company at engagement time. Employment verification catches a tiny edge case and costs 19% of the budget. Start without it, add only if bounce rates warrant it.
.0033 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Firecrawl (2-3 pages scraped) |
11. Final Architecture — Build-Ready
Three critical realizations from the webhook payload:
11.1 Final Provider Stack
11.2 Final Mermaid — All Corrections Applied
flowchart TD
subgraph TRIGGER["TRIGGER"]
WH["HeyDigital Webhook\n→ Trigger.dev batch endpoint\n(20 rows/batch, 5s window)"] --> PARSE["Parse Payload\nCompany: domain, employees, industry,\nlocation, description, products\nALL FROM WEBHOOK — no Serper needed\nProspect: firstName, lastName,\njobTitle, linkedinUrl\nEngagement: postText, postUrl, author"]
end
subgraph GATE1["GATE 1 — Job Title (100%)"]
PARSE --> JT_AI["AI: Job Title Qualification\nGPT-4.1-mini, Flex API\nCACHED: instructions at top\nUNCACHED: {{jobTitle}} at bottom\nCost: .00014/row"]
end
JT_AI --> JT_CHECK{"score >= 5?"}
JT_CHECK -->|"No (~35%)\nscore 1-4"| PUSH_EARLY["DISQUALIFIED: Bad title\n→ Supabase only (no email push)"]
JT_CHECK -->|"Yes (~65%)\nQualified/Maybe"| PARALLEL["PARALLEL EXECUTION"]
subgraph PARALLEL_RUN["Parallel (~65% of rows)"]
direction LR
EMP["Employment Verification\nApify harvestapi\nlinkedin-profile-scraper\n.004/row\n[OPTIONAL — can toggle off]"]
GEO["Geo + Headcount Filter\nCountry: LOOKUP TABLE (free)\nSource: webhook.location\nEmployees: webhook.employee_count\nCheck: target country + 10-300\nALL DETERMINISTIC — NO AI\nCost: FREE"]
end
PARALLEL --> EMP
PARALLEL --> GEO
EMP --> MERGE["Merge"]
GEO --> MERGE
MERGE --> GEO_CHECK{"In target country\nAND 10-300 employees?"}
GEO_CHECK -->|"No (~25%)"| PUSH_GEO["DISQUALIFIED: Geo/Size\n→ Supabase only"]
GEO_CHECK -->|"Yes (~40%)"| COMP_AI["GATE 3: Competitor Analysis\nGPT-4.1-mini, Flex API, CACHED\nInput: webhook.description + products\nScore: is this a digital agency?\nCost: .00010/row"]
COMP_AI --> COMP_CHECK{"Is prospect?\n(not a digital agency)"}
COMP_CHECK -->|"No (~10%)\nagency/competitor"| PUSH_COMP["DISQUALIFIED: Competitor\n→ Supabase only"]
COMP_CHECK -->|"Yes (~30%)\nqualified prospect"| PEOPLE["PEOPLE + EMAIL PHASE"]
subgraph PEOPLE["People + Email Enrichment (~30%)"]
direction LR
AI_ARK["AI Ark\nexport-one\nby LinkedIn URL\n~.001/row"]
BLITZ["Blitz\nEmail finder +\nPeople data finder"]
SUPABASE["Supabase\nInternal lookup\ncontacts table\nFREE"]
MV["MillionVerifier\nEmail verification\n~.002/row"]
KITT["Kitt API\ntrykitt.ai\n~.01/row"]
ICY["Icypeas API\n~.02/row\n(fallback)"]
LM["LeadMagic API\n~.03/row\n(last resort)"]
AI_ARK --> BLITZ
BLITZ --> SUPABASE
SUPABASE --> MV
MV -->|"no valid"| KITT
KITT -->|"no valid"| ICY
ICY -->|"no valid"| LM
LM --> EMAIL_DONE["Best email +\nprovider tag"]
end
MV -->|"valid email"| EMAIL_DONE
KITT -->|"valid email"| EMAIL_DONE
ICY -->|"valid email"| EMAIL_DONE
EMAIL_DONE --> EMAIL_CHECK{"Email found?"}
EMAIL_CHECK -->|"No"| PUSH_NOEMAIL["NO EMAIL\n→ Push all destinations"]
EMAIL_CHECK -->|"Yes"| CLEAN["DATA CLEANING"]
subgraph CLEAN["Data Cleaning (~30%)"]
direction LR
FN["First Name\nREGEX: split(' ') [0]\nOnly if >1 word\nStrip non-alpha\nCost: FREE"]
CN["Company Name\nGPT-4.1-mini, CACHED\nStrip Inc/LLC\nCreate acronyms\nCost: .00014"]
end
FN --> COLD["COLD EMAIL"]
CN --> COLD
COLD["Cold Email Personalization\nGPT-4.1-mini, CACHED\nONE merged call (not two)\nOutput: firstLine (20 words)\n+ companyType (2-3 words)\nCost: .00016"] --> DEST
subgraph DEST["DESTINATIONS (always, free)"]
direction LR
CUL["CUL: create-or-update\nsend.leadgrow.ai"]
BISON["Bison: attach to campaign\nsend.leadgrow.ai"]
HEY["HeyReach: add to campaign"]
SUPA["Supabase: social_post_bank"]
end
PUSH_EARLY --> DEST
PUSH_GEO --> DEST
PUSH_COMP --> DEST
PUSH_NOEMAIL --> DEST
COLD --> CUL
COLD --> BISON
COLD --> HEY
COLD --> SUPA
CUL --> DONE["DONE"]
BISON --> DONE
HEY --> DONE
SUPA --> DONE
style TRIGGER fill:#0d1117,stroke:#3fb950,color:#3fb950
style GATE1 fill:#0d1117,stroke:#a371f7,color:#a371f7
style PARALLEL_RUN fill:#0d1117,stroke:#39d2c0,color:#39d2c0
style PEOPLE fill:#0d1117,stroke:#d2991d,color:#d2991d
style CLEAN fill:#0d1117,stroke:#58a6ff,color:#58a6ff
style DEST fill:#0d1117,stroke:#3fb950,color:#3fb950
11.3 Build Checklist
11.4 Final Cost — All Corrections~.014
Amortized per row (gated)
~$140
Per 10,000 leads/month
~96%
Savings vs Clay ($4,000-5,000/mo)
Employment verification is OPTIONAL. Toggle it off: cost drops to .011/row ($114/month). The HeyDigital webhook data is real-time engagement — the person was at that company at engagement time. Employment verification catches a tiny edge case and costs 19% of the budget. Start without it, add only if bounce rates warrant it.
.003 | 65% |
11. Final Architecture — Build-Ready
Three critical realizations from the webhook payload:
11.1 Final Provider Stack
11.2 Final Mermaid — All Corrections Applied
flowchart TD
subgraph TRIGGER["TRIGGER"]
WH["HeyDigital Webhook\n→ Trigger.dev batch endpoint\n(20 rows/batch, 5s window)"] --> PARSE["Parse Payload\nCompany: domain, employees, industry,\nlocation, description, products\nALL FROM WEBHOOK — no Serper needed\nProspect: firstName, lastName,\njobTitle, linkedinUrl\nEngagement: postText, postUrl, author"]
end
subgraph GATE1["GATE 1 — Job Title (100%)"]
PARSE --> JT_AI["AI: Job Title Qualification\nGPT-4.1-mini, Flex API\nCACHED: instructions at top\nUNCACHED: {{jobTitle}} at bottom\nCost: .00014/row"]
end
JT_AI --> JT_CHECK{"score >= 5?"}
JT_CHECK -->|"No (~35%)\nscore 1-4"| PUSH_EARLY["DISQUALIFIED: Bad title\n→ Supabase only (no email push)"]
JT_CHECK -->|"Yes (~65%)\nQualified/Maybe"| PARALLEL["PARALLEL EXECUTION"]
subgraph PARALLEL_RUN["Parallel (~65% of rows)"]
direction LR
EMP["Employment Verification\nApify harvestapi\nlinkedin-profile-scraper\n.004/row\n[OPTIONAL — can toggle off]"]
GEO["Geo + Headcount Filter\nCountry: LOOKUP TABLE (free)\nSource: webhook.location\nEmployees: webhook.employee_count\nCheck: target country + 10-300\nALL DETERMINISTIC — NO AI\nCost: FREE"]
end
PARALLEL --> EMP
PARALLEL --> GEO
EMP --> MERGE["Merge"]
GEO --> MERGE
MERGE --> GEO_CHECK{"In target country\nAND 10-300 employees?"}
GEO_CHECK -->|"No (~25%)"| PUSH_GEO["DISQUALIFIED: Geo/Size\n→ Supabase only"]
GEO_CHECK -->|"Yes (~40%)"| COMP_AI["GATE 3: Competitor Analysis\nGPT-4.1-mini, Flex API, CACHED\nInput: webhook.description + products\nScore: is this a digital agency?\nCost: .00010/row"]
COMP_AI --> COMP_CHECK{"Is prospect?\n(not a digital agency)"}
COMP_CHECK -->|"No (~10%)\nagency/competitor"| PUSH_COMP["DISQUALIFIED: Competitor\n→ Supabase only"]
COMP_CHECK -->|"Yes (~30%)\nqualified prospect"| PEOPLE["PEOPLE + EMAIL PHASE"]
subgraph PEOPLE["People + Email Enrichment (~30%)"]
direction LR
AI_ARK["AI Ark\nexport-one\nby LinkedIn URL\n~.001/row"]
BLITZ["Blitz\nEmail finder +\nPeople data finder"]
SUPABASE["Supabase\nInternal lookup\ncontacts table\nFREE"]
MV["MillionVerifier\nEmail verification\n~.002/row"]
KITT["Kitt API\ntrykitt.ai\n~.01/row"]
ICY["Icypeas API\n~.02/row\n(fallback)"]
LM["LeadMagic API\n~.03/row\n(last resort)"]
AI_ARK --> BLITZ
BLITZ --> SUPABASE
SUPABASE --> MV
MV -->|"no valid"| KITT
KITT -->|"no valid"| ICY
ICY -->|"no valid"| LM
LM --> EMAIL_DONE["Best email +\nprovider tag"]
end
MV -->|"valid email"| EMAIL_DONE
KITT -->|"valid email"| EMAIL_DONE
ICY -->|"valid email"| EMAIL_DONE
EMAIL_DONE --> EMAIL_CHECK{"Email found?"}
EMAIL_CHECK -->|"No"| PUSH_NOEMAIL["NO EMAIL\n→ Push all destinations"]
EMAIL_CHECK -->|"Yes"| CLEAN["DATA CLEANING"]
subgraph CLEAN["Data Cleaning (~30%)"]
direction LR
FN["First Name\nREGEX: split(' ') [0]\nOnly if >1 word\nStrip non-alpha\nCost: FREE"]
CN["Company Name\nGPT-4.1-mini, CACHED\nStrip Inc/LLC\nCreate acronyms\nCost: .00014"]
end
FN --> COLD["COLD EMAIL"]
CN --> COLD
COLD["Cold Email Personalization\nGPT-4.1-mini, CACHED\nONE merged call (not two)\nOutput: firstLine (20 words)\n+ companyType (2-3 words)\nCost: .00016"] --> DEST
subgraph DEST["DESTINATIONS (always, free)"]
direction LR
CUL["CUL: create-or-update\nsend.leadgrow.ai"]
BISON["Bison: attach to campaign\nsend.leadgrow.ai"]
HEY["HeyReach: add to campaign"]
SUPA["Supabase: social_post_bank"]
end
PUSH_EARLY --> DEST
PUSH_GEO --> DEST
PUSH_COMP --> DEST
PUSH_NOEMAIL --> DEST
COLD --> CUL
COLD --> BISON
COLD --> HEY
COLD --> SUPA
CUL --> DONE["DONE"]
BISON --> DONE
HEY --> DONE
SUPA --> DONE
style TRIGGER fill:#0d1117,stroke:#3fb950,color:#3fb950
style GATE1 fill:#0d1117,stroke:#a371f7,color:#a371f7
style PARALLEL_RUN fill:#0d1117,stroke:#39d2c0,color:#39d2c0
style PEOPLE fill:#0d1117,stroke:#d2991d,color:#d2991d
style CLEAN fill:#0d1117,stroke:#58a6ff,color:#58a6ff
style DEST fill:#0d1117,stroke:#3fb950,color:#3fb950
11.3 Build Checklist
11.4 Final Cost — All Corrections~.014
Amortized per row (gated)
~$140
Per 10,000 leads/month
~96%
Savings vs Clay ($4,000-5,000/mo)
Employment verification is OPTIONAL. Toggle it off: cost drops to .011/row ($114/month). The HeyDigital webhook data is real-time engagement — the person was at that company at engagement time. Employment verification catches a tiny edge case and costs 19% of the budget. Start without it, add only if bounce rates warrant it.
.0020 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| AI Ark person enrichment | ~
11. Final Architecture — Build-Ready
Three critical realizations from the webhook payload:
11.1 Final Provider Stack
11.2 Final Mermaid — All Corrections Applied
flowchart TD
subgraph TRIGGER["TRIGGER"]
WH["HeyDigital Webhook\n→ Trigger.dev batch endpoint\n(20 rows/batch, 5s window)"] --> PARSE["Parse Payload\nCompany: domain, employees, industry,\nlocation, description, products\nALL FROM WEBHOOK — no Serper needed\nProspect: firstName, lastName,\njobTitle, linkedinUrl\nEngagement: postText, postUrl, author"]
end
subgraph GATE1["GATE 1 — Job Title (100%)"]
PARSE --> JT_AI["AI: Job Title Qualification\nGPT-4.1-mini, Flex API\nCACHED: instructions at top\nUNCACHED: {{jobTitle}} at bottom\nCost: .00014/row"]
end
JT_AI --> JT_CHECK{"score >= 5?"}
JT_CHECK -->|"No (~35%)\nscore 1-4"| PUSH_EARLY["DISQUALIFIED: Bad title\n→ Supabase only (no email push)"]
JT_CHECK -->|"Yes (~65%)\nQualified/Maybe"| PARALLEL["PARALLEL EXECUTION"]
subgraph PARALLEL_RUN["Parallel (~65% of rows)"]
direction LR
EMP["Employment Verification\nApify harvestapi\nlinkedin-profile-scraper\n.004/row\n[OPTIONAL — can toggle off]"]
GEO["Geo + Headcount Filter\nCountry: LOOKUP TABLE (free)\nSource: webhook.location\nEmployees: webhook.employee_count\nCheck: target country + 10-300\nALL DETERMINISTIC — NO AI\nCost: FREE"]
end
PARALLEL --> EMP
PARALLEL --> GEO
EMP --> MERGE["Merge"]
GEO --> MERGE
MERGE --> GEO_CHECK{"In target country\nAND 10-300 employees?"}
GEO_CHECK -->|"No (~25%)"| PUSH_GEO["DISQUALIFIED: Geo/Size\n→ Supabase only"]
GEO_CHECK -->|"Yes (~40%)"| COMP_AI["GATE 3: Competitor Analysis\nGPT-4.1-mini, Flex API, CACHED\nInput: webhook.description + products\nScore: is this a digital agency?\nCost: .00010/row"]
COMP_AI --> COMP_CHECK{"Is prospect?\n(not a digital agency)"}
COMP_CHECK -->|"No (~10%)\nagency/competitor"| PUSH_COMP["DISQUALIFIED: Competitor\n→ Supabase only"]
COMP_CHECK -->|"Yes (~30%)\nqualified prospect"| PEOPLE["PEOPLE + EMAIL PHASE"]
subgraph PEOPLE["People + Email Enrichment (~30%)"]
direction LR
AI_ARK["AI Ark\nexport-one\nby LinkedIn URL\n~.001/row"]
BLITZ["Blitz\nEmail finder +\nPeople data finder"]
SUPABASE["Supabase\nInternal lookup\ncontacts table\nFREE"]
MV["MillionVerifier\nEmail verification\n~.002/row"]
KITT["Kitt API\ntrykitt.ai\n~.01/row"]
ICY["Icypeas API\n~.02/row\n(fallback)"]
LM["LeadMagic API\n~.03/row\n(last resort)"]
AI_ARK --> BLITZ
BLITZ --> SUPABASE
SUPABASE --> MV
MV -->|"no valid"| KITT
KITT -->|"no valid"| ICY
ICY -->|"no valid"| LM
LM --> EMAIL_DONE["Best email +\nprovider tag"]
end
MV -->|"valid email"| EMAIL_DONE
KITT -->|"valid email"| EMAIL_DONE
ICY -->|"valid email"| EMAIL_DONE
EMAIL_DONE --> EMAIL_CHECK{"Email found?"}
EMAIL_CHECK -->|"No"| PUSH_NOEMAIL["NO EMAIL\n→ Push all destinations"]
EMAIL_CHECK -->|"Yes"| CLEAN["DATA CLEANING"]
subgraph CLEAN["Data Cleaning (~30%)"]
direction LR
FN["First Name\nREGEX: split(' ') [0]\nOnly if >1 word\nStrip non-alpha\nCost: FREE"]
CN["Company Name\nGPT-4.1-mini, CACHED\nStrip Inc/LLC\nCreate acronyms\nCost: .00014"]
end
FN --> COLD["COLD EMAIL"]
CN --> COLD
COLD["Cold Email Personalization\nGPT-4.1-mini, CACHED\nONE merged call (not two)\nOutput: firstLine (20 words)\n+ companyType (2-3 words)\nCost: .00016"] --> DEST
subgraph DEST["DESTINATIONS (always, free)"]
direction LR
CUL["CUL: create-or-update\nsend.leadgrow.ai"]
BISON["Bison: attach to campaign\nsend.leadgrow.ai"]
HEY["HeyReach: add to campaign"]
SUPA["Supabase: social_post_bank"]
end
PUSH_EARLY --> DEST
PUSH_GEO --> DEST
PUSH_COMP --> DEST
PUSH_NOEMAIL --> DEST
COLD --> CUL
COLD --> BISON
COLD --> HEY
COLD --> SUPA
CUL --> DONE["DONE"]
BISON --> DONE
HEY --> DONE
SUPA --> DONE
style TRIGGER fill:#0d1117,stroke:#3fb950,color:#3fb950
style GATE1 fill:#0d1117,stroke:#a371f7,color:#a371f7
style PARALLEL_RUN fill:#0d1117,stroke:#39d2c0,color:#39d2c0
style PEOPLE fill:#0d1117,stroke:#d2991d,color:#d2991d
style CLEAN fill:#0d1117,stroke:#58a6ff,color:#58a6ff
style DEST fill:#0d1117,stroke:#3fb950,color:#3fb950
11.3 Build Checklist
11.4 Final Cost — All Corrections~.014
Amortized per row (gated)
~$140
Per 10,000 leads/month
~96%
Savings vs Clay ($4,000-5,000/mo)
Employment verification is OPTIONAL. Toggle it off: cost drops to .011/row ($114/month). The HeyDigital webhook data is real-time engagement — the person was at that company at engagement time. Employment verification catches a tiny edge case and costs 19% of the budget. Start without it, add only if bounce rates warrant it.
.001 | 30% |
11. Final Architecture — Build-Ready
Three critical realizations from the webhook payload:
11.1 Final Provider Stack
11.2 Final Mermaid — All Corrections Applied
flowchart TD
subgraph TRIGGER["TRIGGER"]
WH["HeyDigital Webhook\n→ Trigger.dev batch endpoint\n(20 rows/batch, 5s window)"] --> PARSE["Parse Payload\nCompany: domain, employees, industry,\nlocation, description, products\nALL FROM WEBHOOK — no Serper needed\nProspect: firstName, lastName,\njobTitle, linkedinUrl\nEngagement: postText, postUrl, author"]
end
subgraph GATE1["GATE 1 — Job Title (100%)"]
PARSE --> JT_AI["AI: Job Title Qualification\nGPT-4.1-mini, Flex API\nCACHED: instructions at top\nUNCACHED: {{jobTitle}} at bottom\nCost: .00014/row"]
end
JT_AI --> JT_CHECK{"score >= 5?"}
JT_CHECK -->|"No (~35%)\nscore 1-4"| PUSH_EARLY["DISQUALIFIED: Bad title\n→ Supabase only (no email push)"]
JT_CHECK -->|"Yes (~65%)\nQualified/Maybe"| PARALLEL["PARALLEL EXECUTION"]
subgraph PARALLEL_RUN["Parallel (~65% of rows)"]
direction LR
EMP["Employment Verification\nApify harvestapi\nlinkedin-profile-scraper\n.004/row\n[OPTIONAL — can toggle off]"]
GEO["Geo + Headcount Filter\nCountry: LOOKUP TABLE (free)\nSource: webhook.location\nEmployees: webhook.employee_count\nCheck: target country + 10-300\nALL DETERMINISTIC — NO AI\nCost: FREE"]
end
PARALLEL --> EMP
PARALLEL --> GEO
EMP --> MERGE["Merge"]
GEO --> MERGE
MERGE --> GEO_CHECK{"In target country\nAND 10-300 employees?"}
GEO_CHECK -->|"No (~25%)"| PUSH_GEO["DISQUALIFIED: Geo/Size\n→ Supabase only"]
GEO_CHECK -->|"Yes (~40%)"| COMP_AI["GATE 3: Competitor Analysis\nGPT-4.1-mini, Flex API, CACHED\nInput: webhook.description + products\nScore: is this a digital agency?\nCost: .00010/row"]
COMP_AI --> COMP_CHECK{"Is prospect?\n(not a digital agency)"}
COMP_CHECK -->|"No (~10%)\nagency/competitor"| PUSH_COMP["DISQUALIFIED: Competitor\n→ Supabase only"]
COMP_CHECK -->|"Yes (~30%)\nqualified prospect"| PEOPLE["PEOPLE + EMAIL PHASE"]
subgraph PEOPLE["People + Email Enrichment (~30%)"]
direction LR
AI_ARK["AI Ark\nexport-one\nby LinkedIn URL\n~.001/row"]
BLITZ["Blitz\nEmail finder +\nPeople data finder"]
SUPABASE["Supabase\nInternal lookup\ncontacts table\nFREE"]
MV["MillionVerifier\nEmail verification\n~.002/row"]
KITT["Kitt API\ntrykitt.ai\n~.01/row"]
ICY["Icypeas API\n~.02/row\n(fallback)"]
LM["LeadMagic API\n~.03/row\n(last resort)"]
AI_ARK --> BLITZ
BLITZ --> SUPABASE
SUPABASE --> MV
MV -->|"no valid"| KITT
KITT -->|"no valid"| ICY
ICY -->|"no valid"| LM
LM --> EMAIL_DONE["Best email +\nprovider tag"]
end
MV -->|"valid email"| EMAIL_DONE
KITT -->|"valid email"| EMAIL_DONE
ICY -->|"valid email"| EMAIL_DONE
EMAIL_DONE --> EMAIL_CHECK{"Email found?"}
EMAIL_CHECK -->|"No"| PUSH_NOEMAIL["NO EMAIL\n→ Push all destinations"]
EMAIL_CHECK -->|"Yes"| CLEAN["DATA CLEANING"]
subgraph CLEAN["Data Cleaning (~30%)"]
direction LR
FN["First Name\nREGEX: split(' ') [0]\nOnly if >1 word\nStrip non-alpha\nCost: FREE"]
CN["Company Name\nGPT-4.1-mini, CACHED\nStrip Inc/LLC\nCreate acronyms\nCost: .00014"]
end
FN --> COLD["COLD EMAIL"]
CN --> COLD
COLD["Cold Email Personalization\nGPT-4.1-mini, CACHED\nONE merged call (not two)\nOutput: firstLine (20 words)\n+ companyType (2-3 words)\nCost: .00016"] --> DEST
subgraph DEST["DESTINATIONS (always, free)"]
direction LR
CUL["CUL: create-or-update\nsend.leadgrow.ai"]
BISON["Bison: attach to campaign\nsend.leadgrow.ai"]
HEY["HeyReach: add to campaign"]
SUPA["Supabase: social_post_bank"]
end
PUSH_EARLY --> DEST
PUSH_GEO --> DEST
PUSH_COMP --> DEST
PUSH_NOEMAIL --> DEST
COLD --> CUL
COLD --> BISON
COLD --> HEY
COLD --> SUPA
CUL --> DONE["DONE"]
BISON --> DONE
HEY --> DONE
SUPA --> DONE
style TRIGGER fill:#0d1117,stroke:#3fb950,color:#3fb950
style GATE1 fill:#0d1117,stroke:#a371f7,color:#a371f7
style PARALLEL_RUN fill:#0d1117,stroke:#39d2c0,color:#39d2c0
style PEOPLE fill:#0d1117,stroke:#d2991d,color:#d2991d
style CLEAN fill:#0d1117,stroke:#58a6ff,color:#58a6ff
style DEST fill:#0d1117,stroke:#3fb950,color:#3fb950
11.3 Build Checklist
11.4 Final Cost — All Corrections~.014
Amortized per row (gated)
~$140
Per 10,000 leads/month
~96%
Savings vs Clay ($4,000-5,000/mo)
Employment verification is OPTIONAL. Toggle it off: cost drops to .011/row ($114/month). The HeyDigital webhook data is real-time engagement — the person was at that company at engagement time. Employment verification catches a tiny edge case and costs 19% of the budget. Start without it, add only if bounce rates warrant it.
.0003 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Email waterfall (Kitt → Icypeas → LeadMagic) |
11. Final Architecture — Build-Ready
Three critical realizations from the webhook payload:
11.1 Final Provider Stack
11.2 Final Mermaid — All Corrections Applied
flowchart TD
subgraph TRIGGER["TRIGGER"]
WH["HeyDigital Webhook\n→ Trigger.dev batch endpoint\n(20 rows/batch, 5s window)"] --> PARSE["Parse Payload\nCompany: domain, employees, industry,\nlocation, description, products\nALL FROM WEBHOOK — no Serper needed\nProspect: firstName, lastName,\njobTitle, linkedinUrl\nEngagement: postText, postUrl, author"]
end
subgraph GATE1["GATE 1 — Job Title (100%)"]
PARSE --> JT_AI["AI: Job Title Qualification\nGPT-4.1-mini, Flex API\nCACHED: instructions at top\nUNCACHED: {{jobTitle}} at bottom\nCost: .00014/row"]
end
JT_AI --> JT_CHECK{"score >= 5?"}
JT_CHECK -->|"No (~35%)\nscore 1-4"| PUSH_EARLY["DISQUALIFIED: Bad title\n→ Supabase only (no email push)"]
JT_CHECK -->|"Yes (~65%)\nQualified/Maybe"| PARALLEL["PARALLEL EXECUTION"]
subgraph PARALLEL_RUN["Parallel (~65% of rows)"]
direction LR
EMP["Employment Verification\nApify harvestapi\nlinkedin-profile-scraper\n.004/row\n[OPTIONAL — can toggle off]"]
GEO["Geo + Headcount Filter\nCountry: LOOKUP TABLE (free)\nSource: webhook.location\nEmployees: webhook.employee_count\nCheck: target country + 10-300\nALL DETERMINISTIC — NO AI\nCost: FREE"]
end
PARALLEL --> EMP
PARALLEL --> GEO
EMP --> MERGE["Merge"]
GEO --> MERGE
MERGE --> GEO_CHECK{"In target country\nAND 10-300 employees?"}
GEO_CHECK -->|"No (~25%)"| PUSH_GEO["DISQUALIFIED: Geo/Size\n→ Supabase only"]
GEO_CHECK -->|"Yes (~40%)"| COMP_AI["GATE 3: Competitor Analysis\nGPT-4.1-mini, Flex API, CACHED\nInput: webhook.description + products\nScore: is this a digital agency?\nCost: .00010/row"]
COMP_AI --> COMP_CHECK{"Is prospect?\n(not a digital agency)"}
COMP_CHECK -->|"No (~10%)\nagency/competitor"| PUSH_COMP["DISQUALIFIED: Competitor\n→ Supabase only"]
COMP_CHECK -->|"Yes (~30%)\nqualified prospect"| PEOPLE["PEOPLE + EMAIL PHASE"]
subgraph PEOPLE["People + Email Enrichment (~30%)"]
direction LR
AI_ARK["AI Ark\nexport-one\nby LinkedIn URL\n~.001/row"]
BLITZ["Blitz\nEmail finder +\nPeople data finder"]
SUPABASE["Supabase\nInternal lookup\ncontacts table\nFREE"]
MV["MillionVerifier\nEmail verification\n~.002/row"]
KITT["Kitt API\ntrykitt.ai\n~.01/row"]
ICY["Icypeas API\n~.02/row\n(fallback)"]
LM["LeadMagic API\n~.03/row\n(last resort)"]
AI_ARK --> BLITZ
BLITZ --> SUPABASE
SUPABASE --> MV
MV -->|"no valid"| KITT
KITT -->|"no valid"| ICY
ICY -->|"no valid"| LM
LM --> EMAIL_DONE["Best email +\nprovider tag"]
end
MV -->|"valid email"| EMAIL_DONE
KITT -->|"valid email"| EMAIL_DONE
ICY -->|"valid email"| EMAIL_DONE
EMAIL_DONE --> EMAIL_CHECK{"Email found?"}
EMAIL_CHECK -->|"No"| PUSH_NOEMAIL["NO EMAIL\n→ Push all destinations"]
EMAIL_CHECK -->|"Yes"| CLEAN["DATA CLEANING"]
subgraph CLEAN["Data Cleaning (~30%)"]
direction LR
FN["First Name\nREGEX: split(' ') [0]\nOnly if >1 word\nStrip non-alpha\nCost: FREE"]
CN["Company Name\nGPT-4.1-mini, CACHED\nStrip Inc/LLC\nCreate acronyms\nCost: .00014"]
end
FN --> COLD["COLD EMAIL"]
CN --> COLD
COLD["Cold Email Personalization\nGPT-4.1-mini, CACHED\nONE merged call (not two)\nOutput: firstLine (20 words)\n+ companyType (2-3 words)\nCost: .00016"] --> DEST
subgraph DEST["DESTINATIONS (always, free)"]
direction LR
CUL["CUL: create-or-update\nsend.leadgrow.ai"]
BISON["Bison: attach to campaign\nsend.leadgrow.ai"]
HEY["HeyReach: add to campaign"]
SUPA["Supabase: social_post_bank"]
end
PUSH_EARLY --> DEST
PUSH_GEO --> DEST
PUSH_COMP --> DEST
PUSH_NOEMAIL --> DEST
COLD --> CUL
COLD --> BISON
COLD --> HEY
COLD --> SUPA
CUL --> DONE["DONE"]
BISON --> DONE
HEY --> DONE
SUPA --> DONE
style TRIGGER fill:#0d1117,stroke:#3fb950,color:#3fb950
style GATE1 fill:#0d1117,stroke:#a371f7,color:#a371f7
style PARALLEL_RUN fill:#0d1117,stroke:#39d2c0,color:#39d2c0
style PEOPLE fill:#0d1117,stroke:#d2991d,color:#d2991d
style CLEAN fill:#0d1117,stroke:#58a6ff,color:#58a6ff
style DEST fill:#0d1117,stroke:#3fb950,color:#3fb950
11.3 Build Checklist
11.4 Final Cost — All Corrections~.014
Amortized per row (gated)
~$140
Per 10,000 leads/month
~96%
Savings vs Clay ($4,000-5,000/mo)
Employment verification is OPTIONAL. Toggle it off: cost drops to .011/row ($114/month). The HeyDigital webhook data is real-time engagement — the person was at that company at engagement time. Employment verification catches a tiny edge case and costs 19% of the budget. Start without it, add only if bounce rates warrant it.
.035 | 30% |
11. Final Architecture — Build-Ready
Three critical realizations from the webhook payload:
11.1 Final Provider Stack
11.2 Final Mermaid — All Corrections Applied
flowchart TD
subgraph TRIGGER["TRIGGER"]
WH["HeyDigital Webhook\n→ Trigger.dev batch endpoint\n(20 rows/batch, 5s window)"] --> PARSE["Parse Payload\nCompany: domain, employees, industry,\nlocation, description, products\nALL FROM WEBHOOK — no Serper needed\nProspect: firstName, lastName,\njobTitle, linkedinUrl\nEngagement: postText, postUrl, author"]
end
subgraph GATE1["GATE 1 — Job Title (100%)"]
PARSE --> JT_AI["AI: Job Title Qualification\nGPT-4.1-mini, Flex API\nCACHED: instructions at top\nUNCACHED: {{jobTitle}} at bottom\nCost: .00014/row"]
end
JT_AI --> JT_CHECK{"score >= 5?"}
JT_CHECK -->|"No (~35%)\nscore 1-4"| PUSH_EARLY["DISQUALIFIED: Bad title\n→ Supabase only (no email push)"]
JT_CHECK -->|"Yes (~65%)\nQualified/Maybe"| PARALLEL["PARALLEL EXECUTION"]
subgraph PARALLEL_RUN["Parallel (~65% of rows)"]
direction LR
EMP["Employment Verification\nApify harvestapi\nlinkedin-profile-scraper\n.004/row\n[OPTIONAL — can toggle off]"]
GEO["Geo + Headcount Filter\nCountry: LOOKUP TABLE (free)\nSource: webhook.location\nEmployees: webhook.employee_count\nCheck: target country + 10-300\nALL DETERMINISTIC — NO AI\nCost: FREE"]
end
PARALLEL --> EMP
PARALLEL --> GEO
EMP --> MERGE["Merge"]
GEO --> MERGE
MERGE --> GEO_CHECK{"In target country\nAND 10-300 employees?"}
GEO_CHECK -->|"No (~25%)"| PUSH_GEO["DISQUALIFIED: Geo/Size\n→ Supabase only"]
GEO_CHECK -->|"Yes (~40%)"| COMP_AI["GATE 3: Competitor Analysis\nGPT-4.1-mini, Flex API, CACHED\nInput: webhook.description + products\nScore: is this a digital agency?\nCost: .00010/row"]
COMP_AI --> COMP_CHECK{"Is prospect?\n(not a digital agency)"}
COMP_CHECK -->|"No (~10%)\nagency/competitor"| PUSH_COMP["DISQUALIFIED: Competitor\n→ Supabase only"]
COMP_CHECK -->|"Yes (~30%)\nqualified prospect"| PEOPLE["PEOPLE + EMAIL PHASE"]
subgraph PEOPLE["People + Email Enrichment (~30%)"]
direction LR
AI_ARK["AI Ark\nexport-one\nby LinkedIn URL\n~.001/row"]
BLITZ["Blitz\nEmail finder +\nPeople data finder"]
SUPABASE["Supabase\nInternal lookup\ncontacts table\nFREE"]
MV["MillionVerifier\nEmail verification\n~.002/row"]
KITT["Kitt API\ntrykitt.ai\n~.01/row"]
ICY["Icypeas API\n~.02/row\n(fallback)"]
LM["LeadMagic API\n~.03/row\n(last resort)"]
AI_ARK --> BLITZ
BLITZ --> SUPABASE
SUPABASE --> MV
MV -->|"no valid"| KITT
KITT -->|"no valid"| ICY
ICY -->|"no valid"| LM
LM --> EMAIL_DONE["Best email +\nprovider tag"]
end
MV -->|"valid email"| EMAIL_DONE
KITT -->|"valid email"| EMAIL_DONE
ICY -->|"valid email"| EMAIL_DONE
EMAIL_DONE --> EMAIL_CHECK{"Email found?"}
EMAIL_CHECK -->|"No"| PUSH_NOEMAIL["NO EMAIL\n→ Push all destinations"]
EMAIL_CHECK -->|"Yes"| CLEAN["DATA CLEANING"]
subgraph CLEAN["Data Cleaning (~30%)"]
direction LR
FN["First Name\nREGEX: split(' ') [0]\nOnly if >1 word\nStrip non-alpha\nCost: FREE"]
CN["Company Name\nGPT-4.1-mini, CACHED\nStrip Inc/LLC\nCreate acronyms\nCost: .00014"]
end
FN --> COLD["COLD EMAIL"]
CN --> COLD
COLD["Cold Email Personalization\nGPT-4.1-mini, CACHED\nONE merged call (not two)\nOutput: firstLine (20 words)\n+ companyType (2-3 words)\nCost: .00016"] --> DEST
subgraph DEST["DESTINATIONS (always, free)"]
direction LR
CUL["CUL: create-or-update\nsend.leadgrow.ai"]
BISON["Bison: attach to campaign\nsend.leadgrow.ai"]
HEY["HeyReach: add to campaign"]
SUPA["Supabase: social_post_bank"]
end
PUSH_EARLY --> DEST
PUSH_GEO --> DEST
PUSH_COMP --> DEST
PUSH_NOEMAIL --> DEST
COLD --> CUL
COLD --> BISON
COLD --> HEY
COLD --> SUPA
CUL --> DONE["DONE"]
BISON --> DONE
HEY --> DONE
SUPA --> DONE
style TRIGGER fill:#0d1117,stroke:#3fb950,color:#3fb950
style GATE1 fill:#0d1117,stroke:#a371f7,color:#a371f7
style PARALLEL_RUN fill:#0d1117,stroke:#39d2c0,color:#39d2c0
style PEOPLE fill:#0d1117,stroke:#d2991d,color:#d2991d
style CLEAN fill:#0d1117,stroke:#58a6ff,color:#58a6ff
style DEST fill:#0d1117,stroke:#3fb950,color:#3fb950
11.3 Build Checklist
11.4 Final Cost — All Corrections~.014
Amortized per row (gated)
~$140
Per 10,000 leads/month
~96%
Savings vs Clay ($4,000-5,000/mo)
Employment verification is OPTIONAL. Toggle it off: cost drops to .011/row ($114/month). The HeyDigital webhook data is real-time engagement — the person was at that company at engagement time. Employment verification catches a tiny edge case and costs 19% of the budget. Start without it, add only if bounce rates warrant it.
.0105 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| All AI calls (Flex + cached) |
11. Final Architecture — Build-Ready
Three critical realizations from the webhook payload:
11.1 Final Provider Stack
11.2 Final Mermaid — All Corrections Applied
flowchart TD
subgraph TRIGGER["TRIGGER"]
WH["HeyDigital Webhook\n→ Trigger.dev batch endpoint\n(20 rows/batch, 5s window)"] --> PARSE["Parse Payload\nCompany: domain, employees, industry,\nlocation, description, products\nALL FROM WEBHOOK — no Serper needed\nProspect: firstName, lastName,\njobTitle, linkedinUrl\nEngagement: postText, postUrl, author"]
end
subgraph GATE1["GATE 1 — Job Title (100%)"]
PARSE --> JT_AI["AI: Job Title Qualification\nGPT-4.1-mini, Flex API\nCACHED: instructions at top\nUNCACHED: {{jobTitle}} at bottom\nCost: .00014/row"]
end
JT_AI --> JT_CHECK{"score >= 5?"}
JT_CHECK -->|"No (~35%)\nscore 1-4"| PUSH_EARLY["DISQUALIFIED: Bad title\n→ Supabase only (no email push)"]
JT_CHECK -->|"Yes (~65%)\nQualified/Maybe"| PARALLEL["PARALLEL EXECUTION"]
subgraph PARALLEL_RUN["Parallel (~65% of rows)"]
direction LR
EMP["Employment Verification\nApify harvestapi\nlinkedin-profile-scraper\n.004/row\n[OPTIONAL — can toggle off]"]
GEO["Geo + Headcount Filter\nCountry: LOOKUP TABLE (free)\nSource: webhook.location\nEmployees: webhook.employee_count\nCheck: target country + 10-300\nALL DETERMINISTIC — NO AI\nCost: FREE"]
end
PARALLEL --> EMP
PARALLEL --> GEO
EMP --> MERGE["Merge"]
GEO --> MERGE
MERGE --> GEO_CHECK{"In target country\nAND 10-300 employees?"}
GEO_CHECK -->|"No (~25%)"| PUSH_GEO["DISQUALIFIED: Geo/Size\n→ Supabase only"]
GEO_CHECK -->|"Yes (~40%)"| COMP_AI["GATE 3: Competitor Analysis\nGPT-4.1-mini, Flex API, CACHED\nInput: webhook.description + products\nScore: is this a digital agency?\nCost: .00010/row"]
COMP_AI --> COMP_CHECK{"Is prospect?\n(not a digital agency)"}
COMP_CHECK -->|"No (~10%)\nagency/competitor"| PUSH_COMP["DISQUALIFIED: Competitor\n→ Supabase only"]
COMP_CHECK -->|"Yes (~30%)\nqualified prospect"| PEOPLE["PEOPLE + EMAIL PHASE"]
subgraph PEOPLE["People + Email Enrichment (~30%)"]
direction LR
AI_ARK["AI Ark\nexport-one\nby LinkedIn URL\n~.001/row"]
BLITZ["Blitz\nEmail finder +\nPeople data finder"]
SUPABASE["Supabase\nInternal lookup\ncontacts table\nFREE"]
MV["MillionVerifier\nEmail verification\n~.002/row"]
KITT["Kitt API\ntrykitt.ai\n~.01/row"]
ICY["Icypeas API\n~.02/row\n(fallback)"]
LM["LeadMagic API\n~.03/row\n(last resort)"]
AI_ARK --> BLITZ
BLITZ --> SUPABASE
SUPABASE --> MV
MV -->|"no valid"| KITT
KITT -->|"no valid"| ICY
ICY -->|"no valid"| LM
LM --> EMAIL_DONE["Best email +\nprovider tag"]
end
MV -->|"valid email"| EMAIL_DONE
KITT -->|"valid email"| EMAIL_DONE
ICY -->|"valid email"| EMAIL_DONE
EMAIL_DONE --> EMAIL_CHECK{"Email found?"}
EMAIL_CHECK -->|"No"| PUSH_NOEMAIL["NO EMAIL\n→ Push all destinations"]
EMAIL_CHECK -->|"Yes"| CLEAN["DATA CLEANING"]
subgraph CLEAN["Data Cleaning (~30%)"]
direction LR
FN["First Name\nREGEX: split(' ') [0]\nOnly if >1 word\nStrip non-alpha\nCost: FREE"]
CN["Company Name\nGPT-4.1-mini, CACHED\nStrip Inc/LLC\nCreate acronyms\nCost: .00014"]
end
FN --> COLD["COLD EMAIL"]
CN --> COLD
COLD["Cold Email Personalization\nGPT-4.1-mini, CACHED\nONE merged call (not two)\nOutput: firstLine (20 words)\n+ companyType (2-3 words)\nCost: .00016"] --> DEST
subgraph DEST["DESTINATIONS (always, free)"]
direction LR
CUL["CUL: create-or-update\nsend.leadgrow.ai"]
BISON["Bison: attach to campaign\nsend.leadgrow.ai"]
HEY["HeyReach: add to campaign"]
SUPA["Supabase: social_post_bank"]
end
PUSH_EARLY --> DEST
PUSH_GEO --> DEST
PUSH_COMP --> DEST
PUSH_NOEMAIL --> DEST
COLD --> CUL
COLD --> BISON
COLD --> HEY
COLD --> SUPA
CUL --> DONE["DONE"]
BISON --> DONE
HEY --> DONE
SUPA --> DONE
style TRIGGER fill:#0d1117,stroke:#3fb950,color:#3fb950
style GATE1 fill:#0d1117,stroke:#a371f7,color:#a371f7
style PARALLEL_RUN fill:#0d1117,stroke:#39d2c0,color:#39d2c0
style PEOPLE fill:#0d1117,stroke:#d2991d,color:#d2991d
style CLEAN fill:#0d1117,stroke:#58a6ff,color:#58a6ff
style DEST fill:#0d1117,stroke:#3fb950,color:#3fb950
11.3 Build Checklist
11.4 Final Cost — All Corrections~.014
Amortized per row (gated)
~$140
Per 10,000 leads/month
~96%
Savings vs Clay ($4,000-5,000/mo)
Employment verification is OPTIONAL. Toggle it off: cost drops to .011/row ($114/month). The HeyDigital webhook data is real-time engagement — the person was at that company at engagement time. Employment verification catches a tiny edge case and costs 19% of the budget. Start without it, add only if bounce rates warrant it.
.00028 | varies |
11. Final Architecture — Build-Ready
Three critical realizations from the webhook payload:
11.1 Final Provider Stack
11.2 Final Mermaid — All Corrections Applied
flowchart TD
subgraph TRIGGER["TRIGGER"]
WH["HeyDigital Webhook\n→ Trigger.dev batch endpoint\n(20 rows/batch, 5s window)"] --> PARSE["Parse Payload\nCompany: domain, employees, industry,\nlocation, description, products\nALL FROM WEBHOOK — no Serper needed\nProspect: firstName, lastName,\njobTitle, linkedinUrl\nEngagement: postText, postUrl, author"]
end
subgraph GATE1["GATE 1 — Job Title (100%)"]
PARSE --> JT_AI["AI: Job Title Qualification\nGPT-4.1-mini, Flex API\nCACHED: instructions at top\nUNCACHED: {{jobTitle}} at bottom\nCost: .00014/row"]
end
JT_AI --> JT_CHECK{"score >= 5?"}
JT_CHECK -->|"No (~35%)\nscore 1-4"| PUSH_EARLY["DISQUALIFIED: Bad title\n→ Supabase only (no email push)"]
JT_CHECK -->|"Yes (~65%)\nQualified/Maybe"| PARALLEL["PARALLEL EXECUTION"]
subgraph PARALLEL_RUN["Parallel (~65% of rows)"]
direction LR
EMP["Employment Verification\nApify harvestapi\nlinkedin-profile-scraper\n.004/row\n[OPTIONAL — can toggle off]"]
GEO["Geo + Headcount Filter\nCountry: LOOKUP TABLE (free)\nSource: webhook.location\nEmployees: webhook.employee_count\nCheck: target country + 10-300\nALL DETERMINISTIC — NO AI\nCost: FREE"]
end
PARALLEL --> EMP
PARALLEL --> GEO
EMP --> MERGE["Merge"]
GEO --> MERGE
MERGE --> GEO_CHECK{"In target country\nAND 10-300 employees?"}
GEO_CHECK -->|"No (~25%)"| PUSH_GEO["DISQUALIFIED: Geo/Size\n→ Supabase only"]
GEO_CHECK -->|"Yes (~40%)"| COMP_AI["GATE 3: Competitor Analysis\nGPT-4.1-mini, Flex API, CACHED\nInput: webhook.description + products\nScore: is this a digital agency?\nCost: .00010/row"]
COMP_AI --> COMP_CHECK{"Is prospect?\n(not a digital agency)"}
COMP_CHECK -->|"No (~10%)\nagency/competitor"| PUSH_COMP["DISQUALIFIED: Competitor\n→ Supabase only"]
COMP_CHECK -->|"Yes (~30%)\nqualified prospect"| PEOPLE["PEOPLE + EMAIL PHASE"]
subgraph PEOPLE["People + Email Enrichment (~30%)"]
direction LR
AI_ARK["AI Ark\nexport-one\nby LinkedIn URL\n~.001/row"]
BLITZ["Blitz\nEmail finder +\nPeople data finder"]
SUPABASE["Supabase\nInternal lookup\ncontacts table\nFREE"]
MV["MillionVerifier\nEmail verification\n~.002/row"]
KITT["Kitt API\ntrykitt.ai\n~.01/row"]
ICY["Icypeas API\n~.02/row\n(fallback)"]
LM["LeadMagic API\n~.03/row\n(last resort)"]
AI_ARK --> BLITZ
BLITZ --> SUPABASE
SUPABASE --> MV
MV -->|"no valid"| KITT
KITT -->|"no valid"| ICY
ICY -->|"no valid"| LM
LM --> EMAIL_DONE["Best email +\nprovider tag"]
end
MV -->|"valid email"| EMAIL_DONE
KITT -->|"valid email"| EMAIL_DONE
ICY -->|"valid email"| EMAIL_DONE
EMAIL_DONE --> EMAIL_CHECK{"Email found?"}
EMAIL_CHECK -->|"No"| PUSH_NOEMAIL["NO EMAIL\n→ Push all destinations"]
EMAIL_CHECK -->|"Yes"| CLEAN["DATA CLEANING"]
subgraph CLEAN["Data Cleaning (~30%)"]
direction LR
FN["First Name\nREGEX: split(' ') [0]\nOnly if >1 word\nStrip non-alpha\nCost: FREE"]
CN["Company Name\nGPT-4.1-mini, CACHED\nStrip Inc/LLC\nCreate acronyms\nCost: .00014"]
end
FN --> COLD["COLD EMAIL"]
CN --> COLD
COLD["Cold Email Personalization\nGPT-4.1-mini, CACHED\nONE merged call (not two)\nOutput: firstLine (20 words)\n+ companyType (2-3 words)\nCost: .00016"] --> DEST
subgraph DEST["DESTINATIONS (always, free)"]
direction LR
CUL["CUL: create-or-update\nsend.leadgrow.ai"]
BISON["Bison: attach to campaign\nsend.leadgrow.ai"]
HEY["HeyReach: add to campaign"]
SUPA["Supabase: social_post_bank"]
end
PUSH_EARLY --> DEST
PUSH_GEO --> DEST
PUSH_COMP --> DEST
PUSH_NOEMAIL --> DEST
COLD --> CUL
COLD --> BISON
COLD --> HEY
COLD --> SUPA
CUL --> DONE["DONE"]
BISON --> DONE
HEY --> DONE
SUPA --> DONE
style TRIGGER fill:#0d1117,stroke:#3fb950,color:#3fb950
style GATE1 fill:#0d1117,stroke:#a371f7,color:#a371f7
style PARALLEL_RUN fill:#0d1117,stroke:#39d2c0,color:#39d2c0
style PEOPLE fill:#0d1117,stroke:#d2991d,color:#d2991d
style CLEAN fill:#0d1117,stroke:#58a6ff,color:#58a6ff
style DEST fill:#0d1117,stroke:#3fb950,color:#3fb950
11.3 Build Checklist
11.4 Final Cost — All Corrections~.014
Amortized per row (gated)
~$140
Per 10,000 leads/month
~96%
Savings vs Clay ($4,000-5,000/mo)
Employment verification is OPTIONAL. Toggle it off: cost drops to .011/row ($114/month). The HeyDigital webhook data is real-time engagement — the person was at that company at engagement time. Employment verification catches a tiny edge case and costs 19% of the budget. Start without it, add only if bounce rates warrant it.
.0003 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Trigger.dev (batched 20x) |
11. Final Architecture — Build-Ready
Three critical realizations from the webhook payload:
11.1 Final Provider Stack
11.2 Final Mermaid — All Corrections Applied
flowchart TD
subgraph TRIGGER["TRIGGER"]
WH["HeyDigital Webhook\n→ Trigger.dev batch endpoint\n(20 rows/batch, 5s window)"] --> PARSE["Parse Payload\nCompany: domain, employees, industry,\nlocation, description, products\nALL FROM WEBHOOK — no Serper needed\nProspect: firstName, lastName,\njobTitle, linkedinUrl\nEngagement: postText, postUrl, author"]
end
subgraph GATE1["GATE 1 — Job Title (100%)"]
PARSE --> JT_AI["AI: Job Title Qualification\nGPT-4.1-mini, Flex API\nCACHED: instructions at top\nUNCACHED: {{jobTitle}} at bottom\nCost: .00014/row"]
end
JT_AI --> JT_CHECK{"score >= 5?"}
JT_CHECK -->|"No (~35%)\nscore 1-4"| PUSH_EARLY["DISQUALIFIED: Bad title\n→ Supabase only (no email push)"]
JT_CHECK -->|"Yes (~65%)\nQualified/Maybe"| PARALLEL["PARALLEL EXECUTION"]
subgraph PARALLEL_RUN["Parallel (~65% of rows)"]
direction LR
EMP["Employment Verification\nApify harvestapi\nlinkedin-profile-scraper\n.004/row\n[OPTIONAL — can toggle off]"]
GEO["Geo + Headcount Filter\nCountry: LOOKUP TABLE (free)\nSource: webhook.location\nEmployees: webhook.employee_count\nCheck: target country + 10-300\nALL DETERMINISTIC — NO AI\nCost: FREE"]
end
PARALLEL --> EMP
PARALLEL --> GEO
EMP --> MERGE["Merge"]
GEO --> MERGE
MERGE --> GEO_CHECK{"In target country\nAND 10-300 employees?"}
GEO_CHECK -->|"No (~25%)"| PUSH_GEO["DISQUALIFIED: Geo/Size\n→ Supabase only"]
GEO_CHECK -->|"Yes (~40%)"| COMP_AI["GATE 3: Competitor Analysis\nGPT-4.1-mini, Flex API, CACHED\nInput: webhook.description + products\nScore: is this a digital agency?\nCost: .00010/row"]
COMP_AI --> COMP_CHECK{"Is prospect?\n(not a digital agency)"}
COMP_CHECK -->|"No (~10%)\nagency/competitor"| PUSH_COMP["DISQUALIFIED: Competitor\n→ Supabase only"]
COMP_CHECK -->|"Yes (~30%)\nqualified prospect"| PEOPLE["PEOPLE + EMAIL PHASE"]
subgraph PEOPLE["People + Email Enrichment (~30%)"]
direction LR
AI_ARK["AI Ark\nexport-one\nby LinkedIn URL\n~.001/row"]
BLITZ["Blitz\nEmail finder +\nPeople data finder"]
SUPABASE["Supabase\nInternal lookup\ncontacts table\nFREE"]
MV["MillionVerifier\nEmail verification\n~.002/row"]
KITT["Kitt API\ntrykitt.ai\n~.01/row"]
ICY["Icypeas API\n~.02/row\n(fallback)"]
LM["LeadMagic API\n~.03/row\n(last resort)"]
AI_ARK --> BLITZ
BLITZ --> SUPABASE
SUPABASE --> MV
MV -->|"no valid"| KITT
KITT -->|"no valid"| ICY
ICY -->|"no valid"| LM
LM --> EMAIL_DONE["Best email +\nprovider tag"]
end
MV -->|"valid email"| EMAIL_DONE
KITT -->|"valid email"| EMAIL_DONE
ICY -->|"valid email"| EMAIL_DONE
EMAIL_DONE --> EMAIL_CHECK{"Email found?"}
EMAIL_CHECK -->|"No"| PUSH_NOEMAIL["NO EMAIL\n→ Push all destinations"]
EMAIL_CHECK -->|"Yes"| CLEAN["DATA CLEANING"]
subgraph CLEAN["Data Cleaning (~30%)"]
direction LR
FN["First Name\nREGEX: split(' ') [0]\nOnly if >1 word\nStrip non-alpha\nCost: FREE"]
CN["Company Name\nGPT-4.1-mini, CACHED\nStrip Inc/LLC\nCreate acronyms\nCost: .00014"]
end
FN --> COLD["COLD EMAIL"]
CN --> COLD
COLD["Cold Email Personalization\nGPT-4.1-mini, CACHED\nONE merged call (not two)\nOutput: firstLine (20 words)\n+ companyType (2-3 words)\nCost: .00016"] --> DEST
subgraph DEST["DESTINATIONS (always, free)"]
direction LR
CUL["CUL: create-or-update\nsend.leadgrow.ai"]
BISON["Bison: attach to campaign\nsend.leadgrow.ai"]
HEY["HeyReach: add to campaign"]
SUPA["Supabase: social_post_bank"]
end
PUSH_EARLY --> DEST
PUSH_GEO --> DEST
PUSH_COMP --> DEST
PUSH_NOEMAIL --> DEST
COLD --> CUL
COLD --> BISON
COLD --> HEY
COLD --> SUPA
CUL --> DONE["DONE"]
BISON --> DONE
HEY --> DONE
SUPA --> DONE
style TRIGGER fill:#0d1117,stroke:#3fb950,color:#3fb950
style GATE1 fill:#0d1117,stroke:#a371f7,color:#a371f7
style PARALLEL_RUN fill:#0d1117,stroke:#39d2c0,color:#39d2c0
style PEOPLE fill:#0d1117,stroke:#d2991d,color:#d2991d
style CLEAN fill:#0d1117,stroke:#58a6ff,color:#58a6ff
style DEST fill:#0d1117,stroke:#3fb950,color:#3fb950
11.3 Build Checklist
11.4 Final Cost — All Corrections~.014
Amortized per row (gated)
~$140
Per 10,000 leads/month
~96%
Savings vs Clay ($4,000-5,000/mo)
Employment verification is OPTIONAL. Toggle it off: cost drops to .011/row ($114/month). The HeyDigital webhook data is real-time engagement — the person was at that company at engagement time. Employment verification catches a tiny edge case and costs 19% of the budget. Start without it, add only if bounce rates warrant it.
.002 | 100% |
11. Final Architecture — Build-Ready
Three critical realizations from the webhook payload:
11.1 Final Provider Stack
11.2 Final Mermaid — All Corrections Applied
flowchart TD
subgraph TRIGGER["TRIGGER"]
WH["HeyDigital Webhook\n→ Trigger.dev batch endpoint\n(20 rows/batch, 5s window)"] --> PARSE["Parse Payload\nCompany: domain, employees, industry,\nlocation, description, products\nALL FROM WEBHOOK — no Serper needed\nProspect: firstName, lastName,\njobTitle, linkedinUrl\nEngagement: postText, postUrl, author"]
end
subgraph GATE1["GATE 1 — Job Title (100%)"]
PARSE --> JT_AI["AI: Job Title Qualification\nGPT-4.1-mini, Flex API\nCACHED: instructions at top\nUNCACHED: {{jobTitle}} at bottom\nCost: .00014/row"]
end
JT_AI --> JT_CHECK{"score >= 5?"}
JT_CHECK -->|"No (~35%)\nscore 1-4"| PUSH_EARLY["DISQUALIFIED: Bad title\n→ Supabase only (no email push)"]
JT_CHECK -->|"Yes (~65%)\nQualified/Maybe"| PARALLEL["PARALLEL EXECUTION"]
subgraph PARALLEL_RUN["Parallel (~65% of rows)"]
direction LR
EMP["Employment Verification\nApify harvestapi\nlinkedin-profile-scraper\n.004/row\n[OPTIONAL — can toggle off]"]
GEO["Geo + Headcount Filter\nCountry: LOOKUP TABLE (free)\nSource: webhook.location\nEmployees: webhook.employee_count\nCheck: target country + 10-300\nALL DETERMINISTIC — NO AI\nCost: FREE"]
end
PARALLEL --> EMP
PARALLEL --> GEO
EMP --> MERGE["Merge"]
GEO --> MERGE
MERGE --> GEO_CHECK{"In target country\nAND 10-300 employees?"}
GEO_CHECK -->|"No (~25%)"| PUSH_GEO["DISQUALIFIED: Geo/Size\n→ Supabase only"]
GEO_CHECK -->|"Yes (~40%)"| COMP_AI["GATE 3: Competitor Analysis\nGPT-4.1-mini, Flex API, CACHED\nInput: webhook.description + products\nScore: is this a digital agency?\nCost: .00010/row"]
COMP_AI --> COMP_CHECK{"Is prospect?\n(not a digital agency)"}
COMP_CHECK -->|"No (~10%)\nagency/competitor"| PUSH_COMP["DISQUALIFIED: Competitor\n→ Supabase only"]
COMP_CHECK -->|"Yes (~30%)\nqualified prospect"| PEOPLE["PEOPLE + EMAIL PHASE"]
subgraph PEOPLE["People + Email Enrichment (~30%)"]
direction LR
AI_ARK["AI Ark\nexport-one\nby LinkedIn URL\n~.001/row"]
BLITZ["Blitz\nEmail finder +\nPeople data finder"]
SUPABASE["Supabase\nInternal lookup\ncontacts table\nFREE"]
MV["MillionVerifier\nEmail verification\n~.002/row"]
KITT["Kitt API\ntrykitt.ai\n~.01/row"]
ICY["Icypeas API\n~.02/row\n(fallback)"]
LM["LeadMagic API\n~.03/row\n(last resort)"]
AI_ARK --> BLITZ
BLITZ --> SUPABASE
SUPABASE --> MV
MV -->|"no valid"| KITT
KITT -->|"no valid"| ICY
ICY -->|"no valid"| LM
LM --> EMAIL_DONE["Best email +\nprovider tag"]
end
MV -->|"valid email"| EMAIL_DONE
KITT -->|"valid email"| EMAIL_DONE
ICY -->|"valid email"| EMAIL_DONE
EMAIL_DONE --> EMAIL_CHECK{"Email found?"}
EMAIL_CHECK -->|"No"| PUSH_NOEMAIL["NO EMAIL\n→ Push all destinations"]
EMAIL_CHECK -->|"Yes"| CLEAN["DATA CLEANING"]
subgraph CLEAN["Data Cleaning (~30%)"]
direction LR
FN["First Name\nREGEX: split(' ') [0]\nOnly if >1 word\nStrip non-alpha\nCost: FREE"]
CN["Company Name\nGPT-4.1-mini, CACHED\nStrip Inc/LLC\nCreate acronyms\nCost: .00014"]
end
FN --> COLD["COLD EMAIL"]
CN --> COLD
COLD["Cold Email Personalization\nGPT-4.1-mini, CACHED\nONE merged call (not two)\nOutput: firstLine (20 words)\n+ companyType (2-3 words)\nCost: .00016"] --> DEST
subgraph DEST["DESTINATIONS (always, free)"]
direction LR
CUL["CUL: create-or-update\nsend.leadgrow.ai"]
BISON["Bison: attach to campaign\nsend.leadgrow.ai"]
HEY["HeyReach: add to campaign"]
SUPA["Supabase: social_post_bank"]
end
PUSH_EARLY --> DEST
PUSH_GEO --> DEST
PUSH_COMP --> DEST
PUSH_NOEMAIL --> DEST
COLD --> CUL
COLD --> BISON
COLD --> HEY
COLD --> SUPA
CUL --> DONE["DONE"]
BISON --> DONE
HEY --> DONE
SUPA --> DONE
style TRIGGER fill:#0d1117,stroke:#3fb950,color:#3fb950
style GATE1 fill:#0d1117,stroke:#a371f7,color:#a371f7
style PARALLEL_RUN fill:#0d1117,stroke:#39d2c0,color:#39d2c0
style PEOPLE fill:#0d1117,stroke:#d2991d,color:#d2991d
style CLEAN fill:#0d1117,stroke:#58a6ff,color:#58a6ff
style DEST fill:#0d1117,stroke:#3fb950,color:#3fb950
11.3 Build Checklist
11.4 Final Cost — All Corrections~.014
Amortized per row (gated)
~$140
Per 10,000 leads/month
~96%
Savings vs Clay ($4,000-5,000/mo)
Employment verification is OPTIONAL. Toggle it off: cost drops to .011/row ($114/month). The HeyDigital webhook data is real-time engagement — the person was at that company at engagement time. Employment verification catches a tiny edge case and costs 19% of the budget. Start without it, add only if bounce rates warrant it.
.0020 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| TOTAL |
11. Final Architecture — Build-Ready
Three critical realizations from the webhook payload:
11.1 Final Provider Stack
11.2 Final Mermaid — All Corrections Applied
flowchart TD
subgraph TRIGGER["TRIGGER"]
WH["HeyDigital Webhook\n→ Trigger.dev batch endpoint\n(20 rows/batch, 5s window)"] --> PARSE["Parse Payload\nCompany: domain, employees, industry,\nlocation, description, products\nALL FROM WEBHOOK — no Serper needed\nProspect: firstName, lastName,\njobTitle, linkedinUrl\nEngagement: postText, postUrl, author"]
end
subgraph GATE1["GATE 1 — Job Title (100%)"]
PARSE --> JT_AI["AI: Job Title Qualification\nGPT-4.1-mini, Flex API\nCACHED: instructions at top\nUNCACHED: {{jobTitle}} at bottom\nCost: .00014/row"]
end
JT_AI --> JT_CHECK{"score >= 5?"}
JT_CHECK -->|"No (~35%)\nscore 1-4"| PUSH_EARLY["DISQUALIFIED: Bad title\n→ Supabase only (no email push)"]
JT_CHECK -->|"Yes (~65%)\nQualified/Maybe"| PARALLEL["PARALLEL EXECUTION"]
subgraph PARALLEL_RUN["Parallel (~65% of rows)"]
direction LR
EMP["Employment Verification\nApify harvestapi\nlinkedin-profile-scraper\n.004/row\n[OPTIONAL — can toggle off]"]
GEO["Geo + Headcount Filter\nCountry: LOOKUP TABLE (free)\nSource: webhook.location\nEmployees: webhook.employee_count\nCheck: target country + 10-300\nALL DETERMINISTIC — NO AI\nCost: FREE"]
end
PARALLEL --> EMP
PARALLEL --> GEO
EMP --> MERGE["Merge"]
GEO --> MERGE
MERGE --> GEO_CHECK{"In target country\nAND 10-300 employees?"}
GEO_CHECK -->|"No (~25%)"| PUSH_GEO["DISQUALIFIED: Geo/Size\n→ Supabase only"]
GEO_CHECK -->|"Yes (~40%)"| COMP_AI["GATE 3: Competitor Analysis\nGPT-4.1-mini, Flex API, CACHED\nInput: webhook.description + products\nScore: is this a digital agency?\nCost: .00010/row"]
COMP_AI --> COMP_CHECK{"Is prospect?\n(not a digital agency)"}
COMP_CHECK -->|"No (~10%)\nagency/competitor"| PUSH_COMP["DISQUALIFIED: Competitor\n→ Supabase only"]
COMP_CHECK -->|"Yes (~30%)\nqualified prospect"| PEOPLE["PEOPLE + EMAIL PHASE"]
subgraph PEOPLE["People + Email Enrichment (~30%)"]
direction LR
AI_ARK["AI Ark\nexport-one\nby LinkedIn URL\n~.001/row"]
BLITZ["Blitz\nEmail finder +\nPeople data finder"]
SUPABASE["Supabase\nInternal lookup\ncontacts table\nFREE"]
MV["MillionVerifier\nEmail verification\n~.002/row"]
KITT["Kitt API\ntrykitt.ai\n~.01/row"]
ICY["Icypeas API\n~.02/row\n(fallback)"]
LM["LeadMagic API\n~.03/row\n(last resort)"]
AI_ARK --> BLITZ
BLITZ --> SUPABASE
SUPABASE --> MV
MV -->|"no valid"| KITT
KITT -->|"no valid"| ICY
ICY -->|"no valid"| LM
LM --> EMAIL_DONE["Best email +\nprovider tag"]
end
MV -->|"valid email"| EMAIL_DONE
KITT -->|"valid email"| EMAIL_DONE
ICY -->|"valid email"| EMAIL_DONE
EMAIL_DONE --> EMAIL_CHECK{"Email found?"}
EMAIL_CHECK -->|"No"| PUSH_NOEMAIL["NO EMAIL\n→ Push all destinations"]
EMAIL_CHECK -->|"Yes"| CLEAN["DATA CLEANING"]
subgraph CLEAN["Data Cleaning (~30%)"]
direction LR
FN["First Name\nREGEX: split(' ') [0]\nOnly if >1 word\nStrip non-alpha\nCost: FREE"]
CN["Company Name\nGPT-4.1-mini, CACHED\nStrip Inc/LLC\nCreate acronyms\nCost: .00014"]
end
FN --> COLD["COLD EMAIL"]
CN --> COLD
COLD["Cold Email Personalization\nGPT-4.1-mini, CACHED\nONE merged call (not two)\nOutput: firstLine (20 words)\n+ companyType (2-3 words)\nCost: .00016"] --> DEST
subgraph DEST["DESTINATIONS (always, free)"]
direction LR
CUL["CUL: create-or-update\nsend.leadgrow.ai"]
BISON["Bison: attach to campaign\nsend.leadgrow.ai"]
HEY["HeyReach: add to campaign"]
SUPA["Supabase: social_post_bank"]
end
PUSH_EARLY --> DEST
PUSH_GEO --> DEST
PUSH_COMP --> DEST
PUSH_NOEMAIL --> DEST
COLD --> CUL
COLD --> BISON
COLD --> HEY
COLD --> SUPA
CUL --> DONE["DONE"]
BISON --> DONE
HEY --> DONE
SUPA --> DONE
style TRIGGER fill:#0d1117,stroke:#3fb950,color:#3fb950
style GATE1 fill:#0d1117,stroke:#a371f7,color:#a371f7
style PARALLEL_RUN fill:#0d1117,stroke:#39d2c0,color:#39d2c0
style PEOPLE fill:#0d1117,stroke:#d2991d,color:#d2991d
style CLEAN fill:#0d1117,stroke:#58a6ff,color:#58a6ff
style DEST fill:#0d1117,stroke:#3fb950,color:#3fb950
11.3 Build Checklist
11.4 Final Cost — All Corrections~.014
Amortized per row (gated)
~$140
Per 10,000 leads/month
~96%
Savings vs Clay ($4,000-5,000/mo)
Employment verification is OPTIONAL. Toggle it off: cost drops to .011/row ($114/month). The HeyDigital webhook data is real-time engagement — the person was at that company at engagement time. Employment verification catches a tiny edge case and costs 19% of the budget. Start without it, add only if bounce rates warrant it.
.021 |
company.domain, company.employee_count, company.description, company.industry, company.location, company.products_services, company.linkedin_url — all the data Clay spends 5 Claygent + Serper + Firecrawl to discover is handed to us for free.waterfall-enrich.ts in the GTM orchestrator runs Supabase → MillionVerifier → Kitt → Icypeas → LeadMagic with 20-contact concurrency on Trigger.dev. We wire it in, not build it.| Phase | Provider | Cost | Gate |
|---|---|---|---|
| People enrichment | AI Ark (export-one by LinkedIn URL) | ~.001 | 30% of rows (pre-email) |
| Email + people find | Blitz | TBD | 30% of rows |
| Internal lookup | Supabase contacts table | FREE | 30% of rows |
| Email verification | MillionVerifier | ~.002 | 30% of rows |
| Email find | Kitt (trykitt.ai) | ~.01 | 30% of rows |
| Email find | Icypeas | ~.02 | fallback only |
| Email find (last resort) | LeadMagic | ~.03 | fallback only |
| Employment verification | Apify harvestapi | .004 | 65% (optional toggle) |
| AI calls (x4) | OpenAI Flex API + caching | .00028 | varies by gate |
| Orchestration | Trigger.dev (batched 20x) | .002 | 100% |
| # | Task | Uses | Status |
|---|---|---|---|
| 1 | HeyDigital webhook → Trigger.dev batch endpoint | Trigger.dev HTTP trigger + batch | To build |
| 2 | Job title qualification prompt | OpenAI Flex API + caching pattern | To build |
| 3 | Country lookup table (JSON) | Static file, deterministic | To build (60 lines) |
| 4 | Competitor analysis prompt | OpenAI Flex API + caching | To build |
| 5 | AI Ark export-one integration | ai-ark CLI (own tool) | Exists |
| 6 | Blitz integration | Blitz API | Need API details |
| 7 | Email waterfall | waterfall-enrich.ts (GTM orchestrator) | Exists — wire in |
| 8 | harvestapi integration | Apify harvestapi (paid account) | To build |
| 9 | Name cleaning (regex) | split() + replace() | Trivial (5 lines) |
| 10 | Company name cleaning prompt | OpenAI Flex API + caching | To build |
| 11 | Cold email personalization prompt | OpenAI Flex API + caching | To build |
| 12 | CUL push | Existing LeadGrow API | Exists |
| 13 | Bison push | Existing LeadGrow API | Exists |
| 14 | HeyReach push | HeyReach API | Exists |
| 15 | Supabase social_post_bank | Supabase INSERT | Need schema |
| Cost Line | Amortized/Row | Monthly (10K leads) | % |
|---|---|---|---|
| Email waterfall (amortized across 30% of rows) | .0099 | $99 | 71% |
| harvestapi employment verification (65% of rows) | .0026 | $26 | 19% |
| Trigger.dev platform (batched 20x) | .0010 | $10 | 7% |
| AI Ark person enrichment (30% of rows) | .0003 | $3 | 2% |
| All AI calls combined (Flex + cached, gated) | .0003 | $3 | 2% |
| TOTAL | .014 | $140 |
if (!passes) return early — no wasted API calls.company.domain, company.employee_count, company.description, company.industry, company.location, company.products_services, company.linkedin_url — all the data Clay spends 5 Claygent + Serper + Firecrawl to discover is handed to us for free.waterfall-enrich.ts in the GTM orchestrator runs Supabase → MillionVerifier → Kitt → Icypeas → LeadMagic with 20-contact concurrency on Trigger.dev. We wire it in, not build it.| Phase | Provider | Cost | Gate |
|---|---|---|---|
| People enrichment | AI Ark (export-one by LinkedIn URL) | ~.001 | 30% of rows (pre-email) |
| Email + people find | Blitz | TBD | 30% of rows |
| Internal lookup | Supabase contacts table | FREE | 30% of rows |
| Email verification | MillionVerifier | ~.002 | 30% of rows |
| Email find | Kitt (trykitt.ai) | ~.01 | 30% of rows |
| Email find | Icypeas | ~.02 | fallback only |
| Email find (last resort) | LeadMagic | ~.03 | fallback only |
| Employment verification | Apify harvestapi | .004 | 65% (optional toggle) |
| AI calls (x4) | OpenAI Flex API + caching | .00028 | varies by gate |
| Orchestration | Trigger.dev (batched 20x) | .002 | 100% |
| # | Task | Uses | Status |
|---|---|---|---|
| 1 | HeyDigital webhook → Trigger.dev batch endpoint | Trigger.dev HTTP trigger + batch | To build |
| 2 | Job title qualification prompt | OpenAI Flex API + caching pattern | To build |
| 3 | Country lookup table (JSON) | Static file, deterministic | To build (60 lines) |
| 4 | Competitor analysis prompt | OpenAI Flex API + caching | To build |
| 5 | AI Ark export-one integration | ai-ark CLI (own tool) | Exists |
| 6 | Blitz integration | Blitz API | Need API details |
| 7 | Email waterfall | waterfall-enrich.ts (GTM orchestrator) | Exists — wire in |
| 8 | harvestapi integration | Apify harvestapi (paid account) | To build |
| 9 | Name cleaning (regex) | split() + replace() | Trivial (5 lines) |
| 10 | Company name cleaning prompt | OpenAI Flex API + caching | To build |
| 11 | Cold email personalization prompt | OpenAI Flex API + caching | To build |
| 12 | CUL push | Existing LeadGrow API | Exists |
| 13 | Bison push | Existing LeadGrow API | Exists |
| 14 | HeyReach push | HeyReach API | Exists |
| 15 | Supabase social_post_bank | Supabase INSERT | Need schema |
| Cost Line | Amortized/Row | Monthly (10K leads) | % |
|---|---|---|---|
| Email waterfall (amortized across 30% of rows) | .0099 | $99 | 71% |
| harvestapi employment verification (65% of rows) | .0026 | $26 | 19% |
| Trigger.dev platform (batched 20x) | .0010 | $10 | 7% |
| AI Ark person enrichment (30% of rows) | .0003 | $3 | 2% |
| All AI calls combined (Flex + cached, gated) | .0003 | $3 | 2% |
| TOTAL | .014 | $140 |
company.domain, company.employee_count, company.description, company.industry, company.location, company.products_services, company.linkedin_url — all the data Clay spends 5 Claygent + Serper + Firecrawl to discover is handed to us for free.waterfall-enrich.ts in the GTM orchestrator runs Supabase → MillionVerifier → Kitt → Icypeas → LeadMagic with 20-contact concurrency on Trigger.dev. We wire it in, not build it.| Phase | Provider | Cost | Gate |
|---|---|---|---|
| People enrichment | AI Ark (export-one by LinkedIn URL) | ~.001 | 30% of rows (pre-email) |
| Email + people find | Blitz | TBD | 30% of rows |
| Internal lookup | Supabase contacts table | FREE | 30% of rows |
| Email verification | MillionVerifier | ~.002 | 30% of rows |
| Email find | Kitt (trykitt.ai) | ~.01 | 30% of rows |
| Email find | Icypeas | ~.02 | fallback only |
| Email find (last resort) | LeadMagic | ~.03 | fallback only |
| Employment verification | Apify harvestapi | .004 | 65% (optional toggle) |
| AI calls (x4) | OpenAI Flex API + caching | .00028 | varies by gate |
| Orchestration | Trigger.dev (batched 20x) | .002 | 100% |
| # | Task | Uses | Status |
|---|---|---|---|
| 1 | HeyDigital webhook → Trigger.dev batch endpoint | Trigger.dev HTTP trigger + batch | To build |
| 2 | Job title qualification prompt | OpenAI Flex API + caching pattern | To build |
| 3 | Country lookup table (JSON) | Static file, deterministic | To build (60 lines) |
| 4 | Competitor analysis prompt | OpenAI Flex API + caching | To build |
| 5 | AI Ark export-one integration | ai-ark CLI (own tool) | Exists |
| 6 | Blitz integration | Blitz API | Need API details |
| 7 | Email waterfall | waterfall-enrich.ts (GTM orchestrator) | Exists — wire in |
| 8 | harvestapi integration | Apify harvestapi (paid account) | To build |
| 9 | Name cleaning (regex) | split() + replace() | Trivial (5 lines) |
| 10 | Company name cleaning prompt | OpenAI Flex API + caching | To build |
| 11 | Cold email personalization prompt | OpenAI Flex API + caching | To build |
| 12 | CUL push | Existing LeadGrow API | Exists |
| 13 | Bison push | Existing LeadGrow API | Exists |
| 14 | HeyReach push | HeyReach API | Exists |
| 15 | Supabase social_post_bank | Supabase INSERT | Need schema |
| Cost Line | Amortized/Row | Monthly (10K leads) | % |
|---|---|---|---|
| Email waterfall (amortized across 30% of rows) | .0099 | $99 | 71% |
| harvestapi employment verification (65% of rows) | .0026 | $26 | 19% |
| Trigger.dev platform (batched 20x) | .0010 | $10 | 7% |
| AI Ark person enrichment (30% of rows) | .0003 | $3 | 2% |
| All AI calls combined (Flex + cached, gated) | .0003 | $3 | 2% |
| TOTAL | .014 | $140 |
company.domain, company.employee_count, company.description, company.industry, company.location, company.products_services, company.linkedin_url — all the data Clay spends 5 Claygent + Serper + Firecrawl to discover is handed to us for free.waterfall-enrich.ts in the GTM orchestrator runs Supabase → MillionVerifier → Kitt → Icypeas → LeadMagic with 20-contact concurrency on Trigger.dev. We wire it in, not build it.| Phase | Provider | Cost | Gate |
|---|---|---|---|
| People enrichment | AI Ark (export-one by LinkedIn URL) | ~.001 | 30% of rows (pre-email) |
| Email + people find | Blitz | TBD | 30% of rows |
| Internal lookup | Supabase contacts table | FREE | 30% of rows |
| Email verification | MillionVerifier | ~.002 | 30% of rows |
| Email find | Kitt (trykitt.ai) | ~.01 | 30% of rows |
| Email find | Icypeas | ~.02 | fallback only |
| Email find (last resort) | LeadMagic | ~.03 | fallback only |
| Employment verification | Apify harvestapi | .004 | 65% (optional toggle) |
| AI calls (x4) | OpenAI Flex API + caching | .00028 | varies by gate |
| Orchestration | Trigger.dev (batched 20x) | .002 | 100% |
| # | Task | Uses | Status |
|---|---|---|---|
| 1 | HeyDigital webhook → Trigger.dev batch endpoint | Trigger.dev HTTP trigger + batch | To build |
| 2 | Job title qualification prompt | OpenAI Flex API + caching pattern | To build |
| 3 | Country lookup table (JSON) | Static file, deterministic | To build (60 lines) |
| 4 | Competitor analysis prompt | OpenAI Flex API + caching | To build |
| 5 | AI Ark export-one integration | ai-ark CLI (own tool) | Exists |
| 6 | Blitz integration | Blitz API | Need API details |
| 7 | Email waterfall | waterfall-enrich.ts (GTM orchestrator) | Exists — wire in |
| 8 | harvestapi integration | Apify harvestapi (paid account) | To build |
| 9 | Name cleaning (regex) | split() + replace() | Trivial (5 lines) |
| 10 | Company name cleaning prompt | OpenAI Flex API + caching | To build |
| 11 | Cold email personalization prompt | OpenAI Flex API + caching | To build |
| 12 | CUL push | Existing LeadGrow API | Exists |
| 13 | Bison push | Existing LeadGrow API | Exists |
| 14 | HeyReach push | HeyReach API | Exists |
| 15 | Supabase social_post_bank | Supabase INSERT | Need schema |
| Cost Line | Amortized/Row | Monthly (10K leads) | % |
|---|---|---|---|
| Email waterfall (amortized across 30% of rows) | .0099 | $99 | 71% |
| harvestapi employment verification (65% of rows) | .0026 | $26 | 19% |
| Trigger.dev platform (batched 20x) | .0010 | $10 | 7% |
| AI Ark person enrichment (30% of rows) | .0003 | $3 | 2% |
| All AI calls combined (Flex + cached, gated) | .0003 | $3 | 2% |
| TOTAL | .014 | $140 |
company.domain, company.employee_count, company.description, company.industry, company.location, company.products_services, company.linkedin_url — all the data Clay spends 5 Claygent + Serper + Firecrawl to discover is handed to us for free.waterfall-enrich.ts in the GTM orchestrator runs Supabase → MillionVerifier → Kitt → Icypeas → LeadMagic with 20-contact concurrency on Trigger.dev. We wire it in, not build it.| Phase | Provider | Cost | Gate |
|---|---|---|---|
| People enrichment | AI Ark (export-one by LinkedIn URL) | ~.001 | 30% of rows (pre-email) |
| Email + people find | Blitz | TBD | 30% of rows |
| Internal lookup | Supabase contacts table | FREE | 30% of rows |
| Email verification | MillionVerifier | ~.002 | 30% of rows |
| Email find | Kitt (trykitt.ai) | ~.01 | 30% of rows |
| Email find | Icypeas | ~.02 | fallback only |
| Email find (last resort) | LeadMagic | ~.03 | fallback only |
| Employment verification | Apify harvestapi | .004 | 65% (optional toggle) |
| AI calls (x4) | OpenAI Flex API + caching | .00028 | varies by gate |
| Orchestration | Trigger.dev (batched 20x) | .002 | 100% |
| # | Task | Uses | Status |
|---|---|---|---|
| 1 | HeyDigital webhook → Trigger.dev batch endpoint | Trigger.dev HTTP trigger + batch | To build |
| 2 | Job title qualification prompt | OpenAI Flex API + caching pattern | To build |
| 3 | Country lookup table (JSON) | Static file, deterministic | To build (60 lines) |
| 4 | Competitor analysis prompt | OpenAI Flex API + caching | To build |
| 5 | AI Ark export-one integration | ai-ark CLI (own tool) | Exists |
| 6 | Blitz integration | Blitz API | Need API details |
| 7 | Email waterfall | waterfall-enrich.ts (GTM orchestrator) | Exists — wire in |
| 8 | harvestapi integration | Apify harvestapi (paid account) | To build |
| 9 | Name cleaning (regex) | split() + replace() | Trivial (5 lines) |
| 10 | Company name cleaning prompt | OpenAI Flex API + caching | To build |
| 11 | Cold email personalization prompt | OpenAI Flex API + caching | To build |
| 12 | CUL push | Existing LeadGrow API | Exists |
| 13 | Bison push | Existing LeadGrow API | Exists |
| 14 | HeyReach push | HeyReach API | Exists |
| 15 | Supabase social_post_bank | Supabase INSERT | Need schema |
| Cost Line | Amortized/Row | Monthly (10K leads) | % |
|---|---|---|---|
| Email waterfall (amortized across 30% of rows) | .0099 | $99 | 71% |
| harvestapi employment verification (65% of rows) | .0026 | $26 | 19% |
| Trigger.dev platform (batched 20x) | .0010 | $10 | 7% |
| AI Ark person enrichment (30% of rows) | .0003 | $3 | 2% |
| All AI calls combined (Flex + cached, gated) | .0003 | $3 | 2% |
| TOTAL | .014 | $140 |
company.domain, company.employee_count, company.description, company.industry, company.location, company.products_services, company.linkedin_url — all the data Clay spends 5 Claygent + Serper + Firecrawl to discover is handed to us for free.waterfall-enrich.ts in the GTM orchestrator runs Supabase → MillionVerifier → Kitt → Icypeas → LeadMagic with 20-contact concurrency on Trigger.dev. We wire it in, not build it.| Phase | Provider | Cost | Gate |
|---|---|---|---|
| People enrichment | AI Ark (export-one by LinkedIn URL) | ~.001 | 30% of rows (pre-email) |
| Email + people find | Blitz | TBD | 30% of rows |
| Internal lookup | Supabase contacts table | FREE | 30% of rows |
| Email verification | MillionVerifier | ~.002 | 30% of rows |
| Email find | Kitt (trykitt.ai) | ~.01 | 30% of rows |
| Email find | Icypeas | ~.02 | fallback only |
| Email find (last resort) | LeadMagic | ~.03 | fallback only |
| Employment verification | Apify harvestapi | .004 | 65% (optional toggle) |
| AI calls (x4) | OpenAI Flex API + caching | .00028 | varies by gate |
| Orchestration | Trigger.dev (batched 20x) | .002 | 100% |
| # | Task | Uses | Status |
|---|---|---|---|
| 1 | HeyDigital webhook → Trigger.dev batch endpoint | Trigger.dev HTTP trigger + batch | To build |
| 2 | Job title qualification prompt | OpenAI Flex API + caching pattern | To build |
| 3 | Country lookup table (JSON) | Static file, deterministic | To build (60 lines) |
| 4 | Competitor analysis prompt | OpenAI Flex API + caching | To build |
| 5 | AI Ark export-one integration | ai-ark CLI (own tool) | Exists |
| 6 | Blitz integration | Blitz API | Need API details |
| 7 | Email waterfall | waterfall-enrich.ts (GTM orchestrator) | Exists — wire in |
| 8 | harvestapi integration | Apify harvestapi (paid account) | To build |
| 9 | Name cleaning (regex) | split() + replace() | Trivial (5 lines) |
| 10 | Company name cleaning prompt | OpenAI Flex API + caching | To build |
| 11 | Cold email personalization prompt | OpenAI Flex API + caching | To build |
| 12 | CUL push | Existing LeadGrow API | Exists |
| 13 | Bison push | Existing LeadGrow API | Exists |
| 14 | HeyReach push | HeyReach API | Exists |
| 15 | Supabase social_post_bank | Supabase INSERT | Need schema |
| Cost Line | Amortized/Row | Monthly (10K leads) | % |
|---|---|---|---|
| Email waterfall (amortized across 30% of rows) | .0099 | $99 | 71% |
| harvestapi employment verification (65% of rows) | .0026 | $26 | 19% |
| Trigger.dev platform (batched 20x) | .0010 | $10 | 7% |
| AI Ark person enrichment (30% of rows) | .0003 | $3 | 2% |
| All AI calls combined (Flex + cached, gated) | .0003 | $3 | 2% |
| TOTAL | .014 | $140 |
company.domain, company.employee_count, company.description, company.industry, company.location, company.products_services, company.linkedin_url — all the data Clay spends 5 Claygent + Serper + Firecrawl to discover is handed to us for free.waterfall-enrich.ts in the GTM orchestrator runs Supabase → MillionVerifier → Kitt → Icypeas → LeadMagic with 20-contact concurrency on Trigger.dev. We wire it in, not build it.| Phase | Provider | Cost | Gate |
|---|---|---|---|
| People enrichment | AI Ark (export-one by LinkedIn URL) | ~.001 | 30% of rows (pre-email) |
| Email + people find | Blitz | TBD | 30% of rows |
| Internal lookup | Supabase contacts table | FREE | 30% of rows |
| Email verification | MillionVerifier | ~.002 | 30% of rows |
| Email find | Kitt (trykitt.ai) | ~.01 | 30% of rows |
| Email find | Icypeas | ~.02 | fallback only |
| Email find (last resort) | LeadMagic | ~.03 | fallback only |
| Employment verification | Apify harvestapi | .004 | 65% (optional toggle) |
| AI calls (x4) | OpenAI Flex API + caching | .00028 | varies by gate |
| Orchestration | Trigger.dev (batched 20x) | .002 | 100% |
| # | Task | Uses | Status |
|---|---|---|---|
| 1 | HeyDigital webhook → Trigger.dev batch endpoint | Trigger.dev HTTP trigger + batch | To build |
| 2 | Job title qualification prompt | OpenAI Flex API + caching pattern | To build |
| 3 | Country lookup table (JSON) | Static file, deterministic | To build (60 lines) |
| 4 | Competitor analysis prompt | OpenAI Flex API + caching | To build |
| 5 | AI Ark export-one integration | ai-ark CLI (own tool) | Exists |
| 6 | Blitz integration | Blitz API | Need API details |
| 7 | Email waterfall | waterfall-enrich.ts (GTM orchestrator) | Exists — wire in |
| 8 | harvestapi integration | Apify harvestapi (paid account) | To build |
| 9 | Name cleaning (regex) | split() + replace() | Trivial (5 lines) |
| 10 | Company name cleaning prompt | OpenAI Flex API + caching | To build |
| 11 | Cold email personalization prompt | OpenAI Flex API + caching | To build |
| 12 | CUL push | Existing LeadGrow API | Exists |
| 13 | Bison push | Existing LeadGrow API | Exists |
| 14 | HeyReach push | HeyReach API | Exists |
| 15 | Supabase social_post_bank | Supabase INSERT | Need schema |
| Cost Line | Amortized/Row | Monthly (10K leads) | % |
|---|---|---|---|
| Email waterfall (amortized across 30% of rows) | .0099 | $99 | 71% |
| harvestapi employment verification (65% of rows) | .0026 | $26 | 19% |
| Trigger.dev platform (batched 20x) | .0010 | $10 | 7% |
| AI Ark person enrichment (30% of rows) | .0003 | $3 | 2% |
| All AI calls combined (Flex + cached, gated) | .0003 | $3 | 2% |
| TOTAL | .014 | $140 |
company.domain, company.employee_count, company.description, company.industry, company.location, company.products_services, company.linkedin_url — all the data Clay spends 5 Claygent + Serper + Firecrawl to discover is handed to us for free.waterfall-enrich.ts in the GTM orchestrator runs Supabase → MillionVerifier → Kitt → Icypeas → LeadMagic with 20-contact concurrency on Trigger.dev. We wire it in, not build it.| Phase | Provider | Cost | Gate |
|---|---|---|---|
| People enrichment | AI Ark (export-one by LinkedIn URL) | ~.001 | 30% of rows (pre-email) |
| Email + people find | Blitz | TBD | 30% of rows |
| Internal lookup | Supabase contacts table | FREE | 30% of rows |
| Email verification | MillionVerifier | ~.002 | 30% of rows |
| Email find | Kitt (trykitt.ai) | ~.01 | 30% of rows |
| Email find | Icypeas | ~.02 | fallback only |
| Email find (last resort) | LeadMagic | ~.03 | fallback only |
| Employment verification | Apify harvestapi | .004 | 65% (optional toggle) |
| AI calls (x4) | OpenAI Flex API + caching | .00028 | varies by gate |
| Orchestration | Trigger.dev (batched 20x) | .002 | 100% |
| # | Task | Uses | Status |
|---|---|---|---|
| 1 | HeyDigital webhook → Trigger.dev batch endpoint | Trigger.dev HTTP trigger + batch | To build |
| 2 | Job title qualification prompt | OpenAI Flex API + caching pattern | To build |
| 3 | Country lookup table (JSON) | Static file, deterministic | To build (60 lines) |
| 4 | Competitor analysis prompt | OpenAI Flex API + caching | To build |
| 5 | AI Ark export-one integration | ai-ark CLI (own tool) | Exists |
| 6 | Blitz integration | Blitz API | Need API details |
| 7 | Email waterfall | waterfall-enrich.ts (GTM orchestrator) | Exists — wire in |
| 8 | harvestapi integration | Apify harvestapi (paid account) | To build |
| 9 | Name cleaning (regex) | split() + replace() | Trivial (5 lines) |
| 10 | Company name cleaning prompt | OpenAI Flex API + caching | To build |
| 11 | Cold email personalization prompt | OpenAI Flex API + caching | To build |
| 12 | CUL push | Existing LeadGrow API | Exists |
| 13 | Bison push | Existing LeadGrow API | Exists |
| 14 | HeyReach push | HeyReach API | Exists |
| 15 | Supabase social_post_bank | Supabase INSERT | Need schema |
| Cost Line | Amortized/Row | Monthly (10K leads) | % |
|---|---|---|---|
| Email waterfall (amortized across 30% of rows) | .0099 | $99 | 71% |
| harvestapi employment verification (65% of rows) | .0026 | $26 | 19% |
| Trigger.dev platform (batched 20x) | .0010 | $10 | 7% |
| AI Ark person enrichment (30% of rows) | .0003 | $3 | 2% |
| All AI calls combined (Flex + cached, gated) | .0003 | $3 | 2% |
| TOTAL | .014 | $140 |
company.domain, company.employee_count, company.description, company.industry, company.location, company.products_services, company.linkedin_url — all the data Clay spends 5 Claygent + Serper + Firecrawl to discover is handed to us for free.waterfall-enrich.ts in the GTM orchestrator runs Supabase → MillionVerifier → Kitt → Icypeas → LeadMagic with 20-contact concurrency on Trigger.dev. We wire it in, not build it.| Phase | Provider | Cost | Gate |
|---|---|---|---|
| People enrichment | AI Ark (export-one by LinkedIn URL) | ~.001 | 30% of rows (pre-email) |
| Email + people find | Blitz | TBD | 30% of rows |
| Internal lookup | Supabase contacts table | FREE | 30% of rows |
| Email verification | MillionVerifier | ~.002 | 30% of rows |
| Email find | Kitt (trykitt.ai) | ~.01 | 30% of rows |
| Email find | Icypeas | ~.02 | fallback only |
| Email find (last resort) | LeadMagic | ~.03 | fallback only |
| Employment verification | Apify harvestapi | .004 | 65% (optional toggle) |
| AI calls (x4) | OpenAI Flex API + caching | .00028 | varies by gate |
| Orchestration | Trigger.dev (batched 20x) | .002 | 100% |
| # | Task | Uses | Status |
|---|---|---|---|
| 1 | HeyDigital webhook → Trigger.dev batch endpoint | Trigger.dev HTTP trigger + batch | To build |
| 2 | Job title qualification prompt | OpenAI Flex API + caching pattern | To build |
| 3 | Country lookup table (JSON) | Static file, deterministic | To build (60 lines) |
| 4 | Competitor analysis prompt | OpenAI Flex API + caching | To build |
| 5 | AI Ark export-one integration | ai-ark CLI (own tool) | Exists |
| 6 | Blitz integration | Blitz API | Need API details |
| 7 | Email waterfall | waterfall-enrich.ts (GTM orchestrator) | Exists — wire in |
| 8 | harvestapi integration | Apify harvestapi (paid account) | To build |
| 9 | Name cleaning (regex) | split() + replace() | Trivial (5 lines) |
| 10 | Company name cleaning prompt | OpenAI Flex API + caching | To build |
| 11 | Cold email personalization prompt | OpenAI Flex API + caching | To build |
| 12 | CUL push | Existing LeadGrow API | Exists |
| 13 | Bison push | Existing LeadGrow API | Exists |
| 14 | HeyReach push | HeyReach API | Exists |
| 15 | Supabase social_post_bank | Supabase INSERT | Need schema |
| Cost Line | Amortized/Row | Monthly (10K leads) | % |
|---|---|---|---|
| Email waterfall (amortized across 30% of rows) | .0099 | $99 | 71% |
| harvestapi employment verification (65% of rows) | .0026 | $26 | 19% |
| Trigger.dev platform (batched 20x) | .0010 | $10 | 7% |
| AI Ark person enrichment (30% of rows) | .0003 | $3 | 2% |
| All AI calls combined (Flex + cached, gated) | .0003 | $3 | 2% |
| TOTAL | .014 | $140 |
company.domain, company.employee_count, company.description, company.industry, company.location, company.products_services, company.linkedin_url — all the data Clay spends 5 Claygent + Serper + Firecrawl to discover is handed to us for free.waterfall-enrich.ts in the GTM orchestrator runs Supabase → MillionVerifier → Kitt → Icypeas → LeadMagic with 20-contact concurrency on Trigger.dev. We wire it in, not build it.| Phase | Provider | Cost | Gate |
|---|---|---|---|
| People enrichment | AI Ark (export-one by LinkedIn URL) | ~.001 | 30% of rows (pre-email) |
| Email + people find | Blitz | TBD | 30% of rows |
| Internal lookup | Supabase contacts table | FREE | 30% of rows |
| Email verification | MillionVerifier | ~.002 | 30% of rows |
| Email find | Kitt (trykitt.ai) | ~.01 | 30% of rows |
| Email find | Icypeas | ~.02 | fallback only |
| Email find (last resort) | LeadMagic | ~.03 | fallback only |
| Employment verification | Apify harvestapi | .004 | 65% (optional toggle) |
| AI calls (x4) | OpenAI Flex API + caching | .00028 | varies by gate |
| Orchestration | Trigger.dev (batched 20x) | .002 | 100% |
| # | Task | Uses | Status |
|---|---|---|---|
| 1 | HeyDigital webhook → Trigger.dev batch endpoint | Trigger.dev HTTP trigger + batch | To build |
| 2 | Job title qualification prompt | OpenAI Flex API + caching pattern | To build |
| 3 | Country lookup table (JSON) | Static file, deterministic | To build (60 lines) |
| 4 | Competitor analysis prompt | OpenAI Flex API + caching | To build |
| 5 | AI Ark export-one integration | ai-ark CLI (own tool) | Exists |
| 6 | Blitz integration | Blitz API | Need API details |
| 7 | Email waterfall | waterfall-enrich.ts (GTM orchestrator) | Exists — wire in |
| 8 | harvestapi integration | Apify harvestapi (paid account) | To build |
| 9 | Name cleaning (regex) | split() + replace() | Trivial (5 lines) |
| 10 | Company name cleaning prompt | OpenAI Flex API + caching | To build |
| 11 | Cold email personalization prompt | OpenAI Flex API + caching | To build |
| 12 | CUL push | Existing LeadGrow API | Exists |
| 13 | Bison push | Existing LeadGrow API | Exists |
| 14 | HeyReach push | HeyReach API | Exists |
| 15 | Supabase social_post_bank | Supabase INSERT | Need schema |
| Cost Line | Amortized/Row | Monthly (10K leads) | % |
|---|---|---|---|
| Email waterfall (amortized across 30% of rows) | .0099 | $99 | 71% |
| harvestapi employment verification (65% of rows) | .0026 | $26 | 19% |
| Trigger.dev platform (batched 20x) | .0010 | $10 | 7% |
| AI Ark person enrichment (30% of rows) | .0003 | $3 | 2% |
| All AI calls combined (Flex + cached, gated) | .0003 | $3 | 2% |
| TOTAL | .014 | $140 |
company.domain, company.employee_count, company.description, company.industry, company.location, company.products_services, company.linkedin_url — all the data Clay spends 5 Claygent + Serper + Firecrawl to discover is handed to us for free.waterfall-enrich.ts in the GTM orchestrator runs Supabase → MillionVerifier → Kitt → Icypeas → LeadMagic with 20-contact concurrency on Trigger.dev. We wire it in, not build it.| Phase | Provider | Cost | Gate |
|---|---|---|---|
| People enrichment | AI Ark (export-one by LinkedIn URL) | ~.001 | 30% of rows (pre-email) |
| Email + people find | Blitz | TBD | 30% of rows |
| Internal lookup | Supabase contacts table | FREE | 30% of rows |
| Email verification | MillionVerifier | ~.002 | 30% of rows |
| Email find | Kitt (trykitt.ai) | ~.01 | 30% of rows |
| Email find | Icypeas | ~.02 | fallback only |
| Email find (last resort) | LeadMagic | ~.03 | fallback only |
| Employment verification | Apify harvestapi | .004 | 65% (optional toggle) |
| AI calls (x4) | OpenAI Flex API + caching | .00028 | varies by gate |
| Orchestration | Trigger.dev (batched 20x) | .002 | 100% |
| # | Task | Uses | Status |
|---|---|---|---|
| 1 | HeyDigital webhook → Trigger.dev batch endpoint | Trigger.dev HTTP trigger + batch | To build |
| 2 | Job title qualification prompt | OpenAI Flex API + caching pattern | To build |
| 3 | Country lookup table (JSON) | Static file, deterministic | To build (60 lines) |
| 4 | Competitor analysis prompt | OpenAI Flex API + caching | To build |
| 5 | AI Ark export-one integration | ai-ark CLI (own tool) | Exists |
| 6 | Blitz integration | Blitz API | Need API details |
| 7 | Email waterfall | waterfall-enrich.ts (GTM orchestrator) | Exists — wire in |
| 8 | harvestapi integration | Apify harvestapi (paid account) | To build |
| 9 | Name cleaning (regex) | split() + replace() | Trivial (5 lines) |
| 10 | Company name cleaning prompt | OpenAI Flex API + caching | To build |
| 11 | Cold email personalization prompt | OpenAI Flex API + caching | To build |
| 12 | CUL push | Existing LeadGrow API | Exists |
| 13 | Bison push | Existing LeadGrow API | Exists |
| 14 | HeyReach push | HeyReach API | Exists |
| 15 | Supabase social_post_bank | Supabase INSERT | Need schema |
| Cost Line | Amortized/Row | Monthly (10K leads) | % |
|---|---|---|---|
| Email waterfall (amortized across 30% of rows) | .0099 | $99 | 71% |
| harvestapi employment verification (65% of rows) | .0026 | $26 | 19% |
| Trigger.dev platform (batched 20x) | .0010 | $10 | 7% |
| AI Ark person enrichment (30% of rows) | .0003 | $3 | 2% |
| All AI calls combined (Flex + cached, gated) | .0003 | $3 | 2% |
| TOTAL | .014 | $140 |
// trigger.ts — parent task
import { task, batch } from "@trigger.dev/sdk";
export const processHeyDigitalBatch = task({
id: "process-heydigital-batch",
retry: { maxAttempts: 3 },
run: async (payload: { rows: HeyDigitalWebhookRow[] }, { ctx }) => {
const results = [];
for (const row of payload.rows) {
// GATE 1: Job title qualification (always)
const { score, qualification } = await qualifyJobTitle(row.jobTitle);
if (score < 5) {
await pushToSupabase(row, { status: "disqualified_title" });
continue; // 35% stop here
}
// PARALLEL: employment + company enrichment
const [empResult, companyResult] = await Promise.all([
verifyEmployment(row.linkedinUrl), // Apify harvestapi
enrichCompany(row.companyName), // Serper + Firecrawl
]);
// GATE 2: Geo + headcount
const country = COUNTRY_LOOKUP[companyResult.location]; // free
if (!TARGET_COUNTRIES.has(country) || companyResult.employees < 10 || companyResult.employees > 300) {
await pushToSupabase(row, { status: "disqualified_geo" });
continue; // 25% stop here
}
// GATE 3: Competitor analysis
const { isProspect } = await analyzeCompetitor(companyResult);
if (!isProspect) {
await pushToSupabase(row, { status: "disqualified_competitor" });
continue; // 10% stop here
}
// EMAIL WATERFALL
const email = await findEmail({
fullName: row.firstName + " " + row.lastName,
domain: companyResult.domain,
linkedinUrl: row.linkedinUrl,
}); // AI Ark -> Blitz -> Kitt -> Icypeas -> LeadMagic
// CLEANING
const cleanFirstName = row.firstName.split(" ").length > 1
? row.firstName.split(" ")[0].replace(/[^a-zA-Z]/g, "")
: row.firstName;
const cleanCompany = await cleanCompanyName(companyResult.companyName);
// COLD EMAIL (merged call, not two)
const { firstLine, companyType } = await personalizeEmail({
authorName: resolveAuthor(row.postAuthorLinkedinUrl),
postText: row.postText,
companyName: cleanCompany,
companyDescription: companyResult.description,
products: companyResult.products,
});
// PUSH TO ALL DESTINATIONS
await Promise.all([
pushToCUL({ firstName: cleanFirstName, lastName: row.lastName, email, company: cleanCompany, ... }),
pushToBison({ email, campaignId: process.env.CAMPAIGN_ID }),
pushToHeyReach({ firstName: cleanFirstName, lastName: row.lastName, linkedinUrl: row.linkedinUrl, ... }),
pushToSupabase(row, { status: "qualified", firstLine, companyType, email }),
]);
}
},
});
// Trigger: HeyDigital webhook -> batch of 20 rows
export const heydigitalWebhook = batch({
id: "heydigital-webhook",
trigger: httpEndpoint(),
batch: { maxItems: 20, maxWaitMs: 5000 },
run: async (items, { ctx }) => {
await processHeyDigitalBatch.trigger({ rows: items });
},
});
export-one consume per person enrichment? 33K credits suggests ~$330 if
company.domain, company.employee_count, company.description, company.industry, company.location, company.products_services, company.linkedin_url — all the data Clay spends 5 Claygent + Serper + Firecrawl to discover is handed to us for free.waterfall-enrich.ts in the GTM orchestrator runs Supabase → MillionVerifier → Kitt → Icypeas → LeadMagic with 20-contact concurrency on Trigger.dev. We wire it in, not build it.| Phase | Provider | Cost | Gate |
|---|---|---|---|
| People enrichment | AI Ark (export-one by LinkedIn URL) | ~.001 | 30% of rows (pre-email) |
| Email + people find | Blitz | TBD | 30% of rows |
| Internal lookup | Supabase contacts table | FREE | 30% of rows |
| Email verification | MillionVerifier | ~.002 | 30% of rows |
| Email find | Kitt (trykitt.ai) | ~.01 | 30% of rows |
| Email find | Icypeas | ~.02 | fallback only |
| Email find (last resort) | LeadMagic | ~.03 | fallback only |
| Employment verification | Apify harvestapi | .004 | 65% (optional toggle) |
| AI calls (x4) | OpenAI Flex API + caching | .00028 | varies by gate |
| Orchestration | Trigger.dev (batched 20x) | .002 | 100% |
| # | Task | Uses | Status |
|---|---|---|---|
| 1 | HeyDigital webhook → Trigger.dev batch endpoint | Trigger.dev HTTP trigger + batch | To build |
| 2 | Job title qualification prompt | OpenAI Flex API + caching pattern | To build |
| 3 | Country lookup table (JSON) | Static file, deterministic | To build (60 lines) |
| 4 | Competitor analysis prompt | OpenAI Flex API + caching | To build |
| 5 | AI Ark export-one integration | ai-ark CLI (own tool) | Exists |
| 6 | Blitz integration | Blitz API | Need API details |
| 7 | Email waterfall | waterfall-enrich.ts (GTM orchestrator) | Exists — wire in |
| 8 | harvestapi integration | Apify harvestapi (paid account) | To build |
| 9 | Name cleaning (regex) | split() + replace() | Trivial (5 lines) |
| 10 | Company name cleaning prompt | OpenAI Flex API + caching | To build |
| 11 | Cold email personalization prompt | OpenAI Flex API + caching | To build |
| 12 | CUL push | Existing LeadGrow API | Exists |
| 13 | Bison push | Existing LeadGrow API | Exists |
| 14 | HeyReach push | HeyReach API | Exists |
| 15 | Supabase social_post_bank | Supabase INSERT | Need schema |
| Cost Line | Amortized/Row | Monthly (10K leads) | % |
|---|---|---|---|
| Email waterfall (amortized across 30% of rows) | .0099 | $99 | 71% |
| harvestapi employment verification (65% of rows) | .0026 | $26 | 19% |
| Trigger.dev platform (batched 20x) | .0010 | $10 | 7% |
| AI Ark person enrichment (30% of rows) | .0003 | $3 | 2% |
| All AI calls combined (Flex + cached, gated) | .0003 | $3 | 2% |
| TOTAL | .014 | $140 |
company.domain, company.employee_count, company.description, company.industry, company.location, company.products_services, company.linkedin_url — all the data Clay spends 5 Claygent + Serper + Firecrawl to discover is handed to us for free.waterfall-enrich.ts in the GTM orchestrator runs Supabase → MillionVerifier → Kitt → Icypeas → LeadMagic with 20-contact concurrency on Trigger.dev. We wire it in, not build it.| Phase | Provider | Cost | Gate |
|---|---|---|---|
| People enrichment | AI Ark (export-one by LinkedIn URL) | ~.001 | 30% of rows (pre-email) |
| Email + people find | Blitz | TBD | 30% of rows |
| Internal lookup | Supabase contacts table | FREE | 30% of rows |
| Email verification | MillionVerifier | ~.002 | 30% of rows |
| Email find | Kitt (trykitt.ai) | ~.01 | 30% of rows |
| Email find | Icypeas | ~.02 | fallback only |
| Email find (last resort) | LeadMagic | ~.03 | fallback only |
| Employment verification | Apify harvestapi | .004 | 65% (optional toggle) |
| AI calls (x4) | OpenAI Flex API + caching | .00028 | varies by gate |
| Orchestration | Trigger.dev (batched 20x) | .002 | 100% |
| # | Task | Uses | Status |
|---|---|---|---|
| 1 | HeyDigital webhook → Trigger.dev batch endpoint | Trigger.dev HTTP trigger + batch | To build |
| 2 | Job title qualification prompt | OpenAI Flex API + caching pattern | To build |
| 3 | Country lookup table (JSON) | Static file, deterministic | To build (60 lines) |
| 4 | Competitor analysis prompt | OpenAI Flex API + caching | To build |
| 5 | AI Ark export-one integration | ai-ark CLI (own tool) | Exists |
| 6 | Blitz integration | Blitz API | Need API details |
| 7 | Email waterfall | waterfall-enrich.ts (GTM orchestrator) | Exists — wire in |
| 8 | harvestapi integration | Apify harvestapi (paid account) | To build |
| 9 | Name cleaning (regex) | split() + replace() | Trivial (5 lines) |
| 10 | Company name cleaning prompt | OpenAI Flex API + caching | To build |
| 11 | Cold email personalization prompt | OpenAI Flex API + caching | To build |
| 12 | CUL push | Existing LeadGrow API | Exists |
| 13 | Bison push | Existing LeadGrow API | Exists |
| 14 | HeyReach push | HeyReach API | Exists |
| 15 | Supabase social_post_bank | Supabase INSERT | Need schema |
| Cost Line | Amortized/Row | Monthly (10K leads) | % |
|---|---|---|---|
| Email waterfall (amortized across 30% of rows) | .0099 | $99 | 71% |
| harvestapi employment verification (65% of rows) | .0026 | $26 | 19% |
| Trigger.dev platform (batched 20x) | .0010 | $10 | 7% |
| AI Ark person enrichment (30% of rows) | .0003 | $3 | 2% |
| All AI calls combined (Flex + cached, gated) | .0003 | $3 | 2% |
| TOTAL | .014 | $140 |
social_post_bank table created. What fields beyond postUrl + postText?company.domain, company.employee_count, company.description, company.industry, company.location, company.products_services, company.linkedin_url — all the data Clay spends 5 Claygent + Serper + Firecrawl to discover is handed to us for free.waterfall-enrich.ts in the GTM orchestrator runs Supabase → MillionVerifier → Kitt → Icypeas → LeadMagic with 20-contact concurrency on Trigger.dev. We wire it in, not build it.| Phase | Provider | Cost | Gate |
|---|---|---|---|
| People enrichment | AI Ark (export-one by LinkedIn URL) | ~.001 | 30% of rows (pre-email) |
| Email + people find | Blitz | TBD | 30% of rows |
| Internal lookup | Supabase contacts table | FREE | 30% of rows |
| Email verification | MillionVerifier | ~.002 | 30% of rows |
| Email find | Kitt (trykitt.ai) | ~.01 | 30% of rows |
| Email find | Icypeas | ~.02 | fallback only |
| Email find (last resort) | LeadMagic | ~.03 | fallback only |
| Employment verification | Apify harvestapi | .004 | 65% (optional toggle) |
| AI calls (x4) | OpenAI Flex API + caching | .00028 | varies by gate |
| Orchestration | Trigger.dev (batched 20x) | .002 | 100% |
| # | Task | Uses | Status |
|---|---|---|---|
| 1 | HeyDigital webhook → Trigger.dev batch endpoint | Trigger.dev HTTP trigger + batch | To build |
| 2 | Job title qualification prompt | OpenAI Flex API + caching pattern | To build |
| 3 | Country lookup table (JSON) | Static file, deterministic | To build (60 lines) |
| 4 | Competitor analysis prompt | OpenAI Flex API + caching | To build |
| 5 | AI Ark export-one integration | ai-ark CLI (own tool) | Exists |
| 6 | Blitz integration | Blitz API | Need API details |
| 7 | Email waterfall | waterfall-enrich.ts (GTM orchestrator) | Exists — wire in |
| 8 | harvestapi integration | Apify harvestapi (paid account) | To build |
| 9 | Name cleaning (regex) | split() + replace() | Trivial (5 lines) |
| 10 | Company name cleaning prompt | OpenAI Flex API + caching | To build |
| 11 | Cold email personalization prompt | OpenAI Flex API + caching | To build |
| 12 | CUL push | Existing LeadGrow API | Exists |
| 13 | Bison push | Existing LeadGrow API | Exists |
| 14 | HeyReach push | HeyReach API | Exists |
| 15 | Supabase social_post_bank | Supabase INSERT | Need schema |
| Cost Line | Amortized/Row | Monthly (10K leads) | % |
|---|---|---|---|
| Email waterfall (amortized across 30% of rows) | .0099 | $99 | 71% |
| harvestapi employment verification (65% of rows) | .0026 | $26 | 19% |
| Trigger.dev platform (batched 20x) | .0010 | $10 | 7% |
| AI Ark person enrichment (30% of rows) | .0003 | $3 | 2% |
| All AI calls combined (Flex + cached, gated) | .0003 | $3 | 2% |
| TOTAL | .014 | $140 |