GTM Enrichment — Smart (Multi-Provider Waterfall)
Multi-provider waterfall lead enrichment.
How to use it
- Hit Copy the whole skill.
- Claude: ⋯ → Download .md, then Customize → Skills → Add → Upload skill.
ChatGPT: make a Project and paste it into Instructions.
Neither? Paste it at the top of a new chat — it works for that chat. - Describe your job in plain words. The AI follows the skill from there.
npx degit gooseworks-ai/goose-skills/skills/lead-generation/capabilities/gtm-enrichment-smart#main ~/.claude/skills/gtm-enrichment-smartFor one project only, change the path to .claude/skills/gtm-enrichment-smart.
Not working?
- Check which app you pasted it into — the steps above name the right one.
- Some skills need the paid tier of Claude or ChatGPT.
Paste into Claude, ChatGPT or Cursor.
Show the full text424 lines
GTM Enrichment — Smart (Multi-Provider Waterfall)
Setup
Choose the available runtime before doing any credential setup:
- Terminal-free client: skip the shell commands below. Use connected MCP tools. For ScrapeCreators operations, read
scrapecreators-apiand prefercall_data_provider. If a required enrichment provider has no connected tool, report that part of the waterfall as unavailable rather than fabricating enrichment data. - Local terminal: use the GooseWorks credentials and proxy commands below.
Read your credentials from ~/.gooseworks/credentials.json:
export GOOSEWORKS_API_KEY=$(python3 -c "import json;print(json.load(open('$HOME/.gooseworks/credentials.json'))['api_key'])")
export GOOSEWORKS_API_BASE=$(python3 -c "import json;print(json.load(open('$HOME/.gooseworks/credentials.json')).get('api_base','https://api.gooseworks.ai'))")
If ~/.gooseworks/credentials.json does not exist, tell the user to run: npx gooseworks login
The local proxy endpoints use Bearer auth: -H "Authorization: Bearer $GOOSEWORKS_API_KEY". ScrapeCreators operation descriptions below remain environment-neutral in both runtimes.
Enrich a lead from an email address (+ optional name) using a waterfall strategy: start with cheap APIs ($0.01 each), cross-reference for confidence, then use expensive AI agents only for gaps. Spends proportionally to lead quality.
Cost: $0.04 (best) to ~$0.12 (typical with buying signals) to ~$0.26 (worst, Sixtyfour fallback) Latency: ~5-15s typical, up to 60s if Sixtyfour fallback triggers
Input
Required:
- email — the lead's email address (e.g.,
[email protected])
Optional:
- name — full name if known (improves match rate)
Workflow
Step 0: Extract Domain + Free Email Check
Extract the domain from the email. Check if it's a free email provider.
Free email providers (skip Brand.dev if match): gmail.com, yahoo.com, hotmail.com, outlook.com, aol.com, icloud.com, mail.com, protonmail.com, zoho.com, yandex.com, gmx.com, live.com
Set is_free_email = true/false — this gates whether Brand.dev runs in Phase 1.
PHASE 1 — Core (always run, parallel) — ~$0.03-$0.06
Run ALL of these simultaneously:
1a. Apollo People Match ($0.01):
curl -s -X POST $GOOSEWORKS_API_BASE/v1/proxy/orthogonal/run \
-H "Authorization: Bearer $GOOSEWORKS_API_KEY" \
-H "Content-Type: application/json" \
-d '{"api":"apollo","path":"/api/v1/people/match"}'
"email": "{email}",
"reveal_personal_emails": true
}'
Extract: person.name, person.title, person.linkedin_url, person.city, person.state, person.country, person.organization.name, person.organization.id (save org_id for Phase 4), person.organization.industry, person.organization.estimated_num_employees, person.organization.keywords, person.organization.funding_events, person.organization.total_funding.
1b. Hunter Combined Enrichment ($0.01):
curl -s -X POST $GOOSEWORKS_API_BASE/v1/proxy/orthogonal/run \
-H "Authorization: Bearer $GOOSEWORKS_API_KEY" \
-H "Content-Type: application/json" \
-d '{"api":"hunter","path":"/v2/combined/find","query":{"email":"{email}"}}'
Extract: data.person.first_name, data.person.last_name, data.person.linkedin_handle, data.person.title, data.company.name, data.company.domain, data.company.industry, data.company.description, data.company.headcount, data.company.technologies, data.company.twitter, data.company.category.
1c. Brand.dev Retrieve ($0.03 — CONDITIONAL: only if is_free_email == false):
curl -s -X POST $GOOSEWORKS_API_BASE/v1/proxy/orthogonal/run \
-H "Authorization: Bearer $GOOSEWORKS_API_KEY" \
-H "Content-Type: application/json" \
-d '{"api":"brand-dev","path":"/v1/brand/retrieve","query":{"domain":"{domain}"}}'
Extract: title (company name), description, industries (including eic code), socials (twitter URL, github URL, linkedin URL), employeeCount, foundedYear, location.
SKIP this call if is_free_email == true — saves $0.03.
1d. Hunter Email Verifier ($0.01):
curl -s -X POST $GOOSEWORKS_API_BASE/v1/proxy/orthogonal/run \
-H "Authorization: Bearer $GOOSEWORKS_API_KEY" \
-H "Content-Type: application/json" \
-d '{"api":"hunter","path":"/v2/email-verifier","query":{"email":"{email}"}}'
Extract: data.status (valid/invalid/accept_all/webmail/disposable/unknown), data.result (deliverable/undeliverable/risky).
PHASE 1 MERGE — Cross-Reference & Confidence
After all Phase 1 calls complete, merge data:
Person merge rules:
- Full name: prefer Apollo (structured), cross-ref with Hunter
- Title: prefer Apollo, cross-ref with Hunter
- LinkedIn URL: prefer Apollo
linkedin_url, fallback to Hunterlinkedin_handle(prependhttps://linkedin.com/in/) - Location: prefer Apollo (structured city/state/country)
- If Apollo and Hunter agree on name+title:
confidence = "high" - If only one source has data:
confidence = "medium" - If they disagree on name or title: flag conflict, keep both,
confidence = "low"
Company merge rules:
- Name: prefer Apollo org name, cross-ref with Hunter + Brand.dev
- LinkedIn URL: prefer Brand.dev socials, fallback Apollo
- Description: prefer Brand.dev (richer), fallback Hunter
- Employee count: prefer Apollo, cross-ref with Brand.dev + Hunter headcount
- Funding: use Apollo
funding_eventsandtotal_funding - Geo: prefer Apollo org location, cross-ref with Brand.dev
- Tech stack: use Hunter
technologies - Social URLs: use Brand.dev
socials(twitter, github)
AI/B2B Classification (zero extra cost):
Cross-reference three sources from Phase 1:
| Source | AI Signals | B2B Signals |
|---|---|---|
Brand.dev description + industries.eic |
Parse description for: AI, ML, machine learning, deep learning, neural, LLM, GPT, NLP, computer vision | Parse for: SaaS, B2B, enterprise, platform, API, developer tools, infrastructure |
Apollo keywords[] + industry |
Match keywords against AI terms | Match keywords against B2B terms |
Hunter category + company description |
Check for AI/ML terms | Check for software/SaaS/B2B terms |
Confidence rules:
high: 2+ sources agreemedium: 1 source has signallow: weak inference only (e.g., "tech company" but no explicit AI/B2B terms)
PHASE 2 — Gap-Fill (conditional) — $0.00-$0.02
2a. Apollo Organization Enrich ($0.01 — ONLY if Apollo Phase 1 returned NO funding_events or funding data is empty):
curl -s -X POST $GOOSEWORKS_API_BASE/v1/proxy/orthogonal/run \
-H "Authorization: Bearer $GOOSEWORKS_API_KEY" \
-H "Content-Type: application/json" \
-d '{"api":"apollo","path":"/api/v1/organizations/enrich","query":{"domain":"{domain}"}}'
Extract: organization.funding_events[], organization.total_funding, organization.latest_funding_stage, organization.latest_funding_amount, organization.estimated_num_employees, organization.annual_revenue.
2b. Tomba Enrich ($0.01 — ONLY if Apollo and Hunter disagree on person name OR title):
curl -s -X POST $GOOSEWORKS_API_BASE/v1/proxy/orthogonal/run \
-H "Authorization: Bearer $GOOSEWORKS_API_KEY" \
-H "Content-Type: application/json" \
-d '{"api":"tomba","path":"/v1/enrich","query":{"email":"{email}"}}'
Use as tie-breaker. If Tomba agrees with Apollo: use Apollo data. If Tomba agrees with Hunter: use Hunter data. If all three disagree: keep Apollo as primary, flag conflict.
PHASE 3 — Sixtyfour Fallback (conditional, expensive) — $0.00-$0.20
3a. Sixtyfour Enrich Lead ($0.10 — ONLY if person NOT found after Phases 1-2, meaning no name AND no title AND no LinkedIn URL from any source):
curl -s -X POST $GOOSEWORKS_API_BASE/v1/proxy/orthogonal/run \
-H "Authorization: Bearer $GOOSEWORKS_API_KEY" \
-H "Content-Type: application/json" \
-d '{"api":"sixtyfour","path":"/enrich-lead"}'
"lead_info": {
"email": "{email}",
"domain": "{domain}"
},
"struct": {
"full_name": "Full legal name of this person",
"title": "Current job title",
"linkedin_url": "LinkedIn profile URL (full URL)",
"city": "City",
"state": "State or region",
"country": "Country"
}
}'
3b. Sixtyfour Enrich Company ($0.10 — ONLY if company has major gaps AND org has >500 employees):
Major gaps = missing 2+ of: LinkedIn URL, description, employee count, funding data.
curl -s -X POST $GOOSEWORKS_API_BASE/v1/proxy/orthogonal/run \
-H "Authorization: Bearer $GOOSEWORKS_API_KEY" \
-H "Content-Type: application/json" \
-d '{"api":"sixtyfour","path":"/enrich-company"}'
"target_company": {
"domain": "{domain}"
},
"struct": {
"company_name": "Official company name",
"description": "One-paragraph description",
"linkedin_url": "LinkedIn company page URL",
"employee_count": "Number of employees",
"total_funding_usd": "Total funding raised in USD",
"latest_funding_date": "Most recent funding round date",
"latest_funding_stage": "Most recent round stage",
"latest_funding_amount_usd": "Most recent round amount"
}
}'
PHASE 4 — Buying Signals (qualified leads only) — $0.00-$0.04
Gate: Only run Phase 4 if the company is:
- Funded (total_funding > 0) AND
- Classified as B2B (is_b2b_saas = true) AND
- Has >50 employees
4a. Brand.dev AI Products ($0.03 — extracts products, pricing tiers, and features from the website):
curl -s -X POST $GOOSEWORKS_API_BASE/v1/proxy/orthogonal/run \
-H "Authorization: Bearer $GOOSEWORKS_API_KEY" \
-H "Content-Type: application/json" \
-d '{"api":"brand-dev","path":"/v1/brand/ai/products"}'
"domain": "{domain}"
}'
From the products response, extract buying signals:
- has_enterprise_plan: Check if any product has "enterprise" in name, tier, or target_audience
- has_self_serve: Check if any product has a listed price (self-serve) vs "Contact sales" pricing
- target_market: Infer from
target_audiencearrays across products
4b. Apollo Job Postings ($0.01 — ONLY if organization_id was captured from Phase 1):
curl -s -X POST $GOOSEWORKS_API_BASE/v1/proxy/orthogonal/run \
-H "Authorization: Bearer $GOOSEWORKS_API_KEY" \
-H "Content-Type: application/json" \
-d '{"api":"apollo","path":"/api/v1/organizations/{organization_id}/job_postings","query":{"organization_id":"{organization_id}"}}'
Search job postings for enterprise sales signals: titles containing "Enterprise", "Account Executive", "Solutions Engineer", "Sales Director", "Customer Success". If found, set hiring_enterprise_reps = true.
PHASE 5 — Cheap/Free Signals — $0.00-$0.01
5a. GitHub Stars (free — ONLY if Brand.dev socials or Apollo data returned a GitHub URL):
# Extract org name from GitHub URL, e.g., https://github.com/ngrok -> ngrok
# Use the GitHub public API (no auth needed for public repos):
curl -s "https://api.github.com/orgs/{org_name}/repos?sort=stars&per_page=5" | jq '[.[] | {name: .name, stars: .stargazers_count}]'
Sum the top repo stars or report the flagship repo star count.
5b. Twitter/X Followers (Scrape Creators — ONLY if a Twitter handle was found in Brand.dev socials or Apollo data):
provider: scrapecreators
method: GET
path: /v1/twitter/profile
query:
handle: "{twitter_handle}"
Extract: legacy.followers_count, legacy.friends_count, legacy.statuses_count, legacy.description.
FINAL — Compile & Output
Merge all phase results into the output format. Track which phases ran.
Output Format
Present the results as a JSON code block:
{
"person": {
"full_name": "string",
"title": "string",
"linkedin_url": "string",
"location": {"city": "string", "state": "string", "country": "string"},
"email_verified": "deliverable | undeliverable | risky | unknown",
"confidence": "high | medium | low",
"source": "apollo | hunter | sixtyfour | tomba | merged"
},
"company": {
"name": "string",
"domain": "string",
"linkedin_url": "string",
"description": "string",
"geo": {"city": "string", "state": "string", "country": "string"},
"employee_count": "number | null",
"founded_year": "number | null",
"funding": {
"total_amount": "number | null",
"total_amount_printed": "string | null",
"latest_round_date": "string | null",
"latest_round_stage": "string | null",
"latest_round_amount": "number | null",
"rounds": [{"date": "", "type": "", "amount": 0, "investors": ""}],
"confidence": "high | medium | low"
},
"classification": {
"is_ai": {"value": true, "confidence": "high", "evidence": ["Brand.dev description mentions ML", "Apollo keywords include 'artificial intelligence'"]},
"is_b2b_saas": {"value": true, "confidence": "high", "evidence": ["Hunter category: software", "Apollo industry: SaaS"]}
},
"buying_signals": {
"has_enterprise_plan": "boolean | null",
"has_self_serve": "boolean | null",
"hiring_enterprise_reps": "boolean | null",
"website_traffic_rank": "number | null",
"github_stars": "number | null",
"twitter_followers": "number | null",
"tech_stack": ["array | null"]
},
"confidence": "high | medium | low",
"source": "apollo | hunter | brand-dev | sixtyfour | merged"
},
"meta": {
"total_cost": "$0.XX",
"api_calls": [
{
"api": "apollo",
"endpoint": "/api/v1/people/match",
"status": "success",
"cost": "$0.01",
"latency_ms": 1200,
"fields_returned": ["name", "title", "linkedin_url", "organization"],
"fields_missing": [],
"error": null
}
],
"phases_run": [1, 2, 4, 5],
"enrichment_timestamp": "ISO datetime"
}
}
Error Visibility
Track EVERY API call in the meta.api_calls array with this structure:
{
"api": "string (apollo | hunter | brand-dev | sixtyfour | tomba | scrapecreators | github)",
"endpoint": "string",
"status": "success | partial | error | skipped",
"cost": "$0.XX",
"latency_ms": 0,
"fields_returned": [],
"fields_missing": [],
"error": "string | null"
}
Rules:
- If an API call fails, returns empty data, or times out: include it with
status='error'and a clear error message. Never silently skip failures. - If an API call was skipped due to gating logic (e.g., Brand.dev skipped for free email): include it with
status='skipped',cost='$0.00', and reason in error field (e.g., "Skipped: free email provider"). - If an API call returns partial data: use
status='partial', list what was returned and what was missing.
Cost Tracking
Sum all API call costs and report in meta.total_cost:
| API | Endpoint | Cost | When |
|---|---|---|---|
| Apollo | /api/v1/people/match | $0.01 | Always (Phase 1) |
| Hunter | /v2/combined/find | $0.01 | Always (Phase 1) |
| Brand.dev | /v1/brand/retrieve | $0.03 | Phase 1, skip for free email |
| Hunter | /v2/email-verifier | $0.01 | Always (Phase 1) |
| Apollo | /api/v1/organizations/enrich | $0.01 | Phase 2, only if funding missing |
| Tomba | /v1/enrich | $0.01 | Phase 2, only if person data conflicts |
| Sixtyfour | /enrich-lead | $0.10 | Phase 3, only if person not found |
| Sixtyfour | /enrich-company | $0.10 | Phase 3, only if major gaps + >500 employees |
| Brand.dev | /v1/brand/ai/products | $0.03 | Phase 4, only if funded + B2B + >50 employees |
| Apollo | /organizations/{id}/job_postings | $0.01 | Phase 4, only if org_id available |
| Scrape Creators | /v1/twitter/profile | ~$0.01 | Phase 5, only if Twitter handle found |
| GitHub API | public | $0.00 | Phase 5, only if GitHub URL found |
Example
Input: [email protected]
Expected flow:
- Domain:
acme.com,is_free_email = false - Phase 1 (parallel): Apollo people/match, Hunter combined, Brand.dev retrieve, Hunter email-verifier
- Phase 1 merge: Cross-reference person data, classify AI/B2B from descriptions+keywords
- Phase 2: Check if Apollo returned funding — if not, call Apollo org enrich. Check if person data conflicts — if so, call Tomba.
- Phase 3: Skip if person found and company data sufficient
- Phase 4: If company is funded + B2B + >50 employees, run Brand.dev AI products + Apollo job postings
- Phase 5: If GitHub URL found, grab star counts. If Twitter handle found, grab follower count via Scrape Creators
- Compile and output JSON
Tips
- Phase 1 calls should all fire simultaneously — they're independent
- Apollo's people/match is the single best-value call — it returns person AND embedded company data including funding events
- Brand.dev is the richest source for company description, industry classification, and social URLs — but costs 3x more than Apollo/Hunter, so skip it for free email providers
- The AI/B2B classification uses data already returned by Phase 1 — no extra API calls needed
- Hunter's
technologiesarray is the only source of tech stack data — valuable for technical buyers - Phase 3 (Sixtyfour) should be rare — Apollo + Hunter find most people. Only trigger for truly obscure leads
- Phase 4 buying signals are the most actionable data for GTM — but gate them to avoid wasting $0.04 on unqualified leads
- GitHub stars and Twitter followers are cheap/free social proof signals — always grab them if URLs/handles are available
| 1 | |
| 2 | name gtm-enrichment-smart |
| 3 | description Multi-provider waterfall lead enrichment. Takes an email (+ optional name) and returns person + company data by cross-referencing cheap APIs first, using expensive AI agents only as fallback. Cost-efficient (~$0.04-$0.10/lead) with confidence scoring and full error visibility. |
| 4 | source orthogonal |
| 5 | |
| 6 | |
| 7 | |
| 8 | # GTM Enrichment — Smart (Multi-Provider Waterfall) |
| 9 | |
| 10 | ## Setup |
| 11 | |
| 12 | Choose the available runtime before doing any credential setup: |
| 13 | |
| 14 | **Terminal-free client:** skip the shell commands below. Use connected MCP tools. For ScrapeCreators operations, read `scrapecreators-api` and prefer `call_data_provider`. If a required enrichment provider has no connected tool, report that part of the waterfall as unavailable rather than fabricating enrichment data. |
| 15 | **Local terminal:** use the GooseWorks credentials and proxy commands below. |
| 16 | |
| 17 | Read your credentials from ~/.gooseworks/credentials.json: |
| 18 | |
| 19 | export GOOSEWORKS_API_KEY=$(python3 -c "import json;print(json.load(open('$HOME/.gooseworks/credentials.json'))['api_key'])") |
| 20 | export GOOSEWORKS_API_BASE=$(python3 -c "import json;print(json.load(open('$HOME/.gooseworks/credentials.json')).get('api_base','https://api.gooseworks.ai'))") |
| 21 | |
| 22 | |
| 23 | If ~/.gooseworks/credentials.json does not exist, tell the user to run: `npx gooseworks login` |
| 24 | |
| 25 | The local proxy endpoints use Bearer auth: `-H "Authorization: Bearer $GOOSEWORKS_API_KEY"`. ScrapeCreators operation descriptions below remain environment-neutral in both runtimes. |
| 26 | |
| 27 | |
| 28 | Enrich a lead from an email address (+ optional name) using a waterfall strategy: start with cheap APIs ($0.01 each), cross-reference for confidence, then use expensive AI agents only for gaps. Spends proportionally to lead quality. |
| 29 | |
| 30 | **Cost**: $0.04 (best) to ~$0.12 (typical with buying signals) to ~$0.26 (worst, Sixtyfour fallback) |
| 31 | **Latency**: ~5-15s typical, up to 60s if Sixtyfour fallback triggers |
| 32 | |
| 33 | ## Input |
| 34 | |
| 35 | Required: |
| 36 | **email** — the lead's email address (e.g., `[email protected]`) |
| 37 | |
| 38 | Optional: |
| 39 | **name** — full name if known (improves match rate) |
| 40 | |
| 41 | ## Workflow |
| 42 | |
| 43 | ### Step 0: Extract Domain + Free Email Check |
| 44 | |
| 45 | Extract the domain from the email. Check if it's a free email provider. |
| 46 | |
| 47 | **Free email providers** (skip Brand.dev if match): `gmail.com`, `yahoo.com`, `hotmail.com`, `outlook.com`, `aol.com`, `icloud.com`, `mail.com`, `protonmail.com`, `zoho.com`, `yandex.com`, `gmx.com`, `live.com` |
| 48 | |
| 49 | Set `is_free_email = true/false` — this gates whether Brand.dev runs in Phase 1. |
| 50 | |
| 51 | |
| 52 | |
| 53 | ### PHASE 1 — Core (always run, parallel) — ~$0.03-$0.06 |
| 54 | |
| 55 | Run ALL of these simultaneously: |
| 56 | |
| 57 | **1a. Apollo People Match** ($0.01): |
| 58 | |
| 59 | |
| 60 | curl -s -X POST $GOOSEWORKS_API_BASE/v1/proxy/orthogonal/run \ |
| 61 | -H "Authorization: Bearer $GOOSEWORKS_API_KEY" \ |
| 62 | -H "Content-Type: application/json" \ |
| 63 | -d '{"api":"apollo","path":"/api/v1/people/match"}' |
| 64 | "email": "{email}", |
| 65 | "reveal_personal_emails": true |
| 66 | }' |
| 67 | |
| 68 | |
| 69 | Extract: `person.name`, `person.title`, `person.linkedin_url`, `person.city`, `person.state`, `person.country`, `person.organization.name`, `person.organization.id` (save org_id for Phase 4), `person.organization.industry`, `person.organization.estimated_num_employees`, `person.organization.keywords`, `person.organization.funding_events`, `person.organization.total_funding`. |
| 70 | |
| 71 | **1b. Hunter Combined Enrichment** ($0.01): |
| 72 | |
| 73 | |
| 74 | curl -s -X POST $GOOSEWORKS_API_BASE/v1/proxy/orthogonal/run \ |
| 75 | -H "Authorization: Bearer $GOOSEWORKS_API_KEY" \ |
| 76 | -H "Content-Type: application/json" \ |
| 77 | -d '{"api":"hunter","path":"/v2/combined/find","query":{"email":"{email}"}}' |
| 78 | |
| 79 | |
| 80 | Extract: `data.person.first_name`, `data.person.last_name`, `data.person.linkedin_handle`, `data.person.title`, `data.company.name`, `data.company.domain`, `data.company.industry`, `data.company.description`, `data.company.headcount`, `data.company.technologies`, `data.company.twitter`, `data.company.category`. |
| 81 | |
| 82 | **1c. Brand.dev Retrieve** ($0.03 — CONDITIONAL: only if `is_free_email == false`): |
| 83 | |
| 84 | |
| 85 | curl -s -X POST $GOOSEWORKS_API_BASE/v1/proxy/orthogonal/run \ |
| 86 | -H "Authorization: Bearer $GOOSEWORKS_API_KEY" \ |
| 87 | -H "Content-Type: application/json" \ |
| 88 | -d '{"api":"brand-dev","path":"/v1/brand/retrieve","query":{"domain":"{domain}"}}' |
| 89 | |
| 90 | |
| 91 | Extract: `title` (company name), `description`, `industries` (including `eic` code), `socials` (twitter URL, github URL, linkedin URL), `employeeCount`, `foundedYear`, `location`. |
| 92 | |
| 93 | **SKIP this call if `is_free_email == true`** — saves $0.03. |
| 94 | |
| 95 | **1d. Hunter Email Verifier** ($0.01): |
| 96 | |
| 97 | |
| 98 | curl -s -X POST $GOOSEWORKS_API_BASE/v1/proxy/orthogonal/run \ |
| 99 | -H "Authorization: Bearer $GOOSEWORKS_API_KEY" \ |
| 100 | -H "Content-Type: application/json" \ |
| 101 | -d '{"api":"hunter","path":"/v2/email-verifier","query":{"email":"{email}"}}' |
| 102 | |
| 103 | |
| 104 | Extract: `data.status` (valid/invalid/accept_all/webmail/disposable/unknown), `data.result` (deliverable/undeliverable/risky). |
| 105 | |
| 106 | |
| 107 | |
| 108 | ### PHASE 1 MERGE — Cross-Reference & Confidence |
| 109 | |
| 110 | After all Phase 1 calls complete, merge data: |
| 111 | |
| 112 | **Person merge rules:** |
| 113 | Full name: prefer Apollo (structured), cross-ref with Hunter |
| 114 | Title: prefer Apollo, cross-ref with Hunter |
| 115 | LinkedIn URL: prefer Apollo `linkedin_url`, fallback to Hunter `linkedin_handle` (prepend `https://linkedin.com/in/`) |
| 116 | Location: prefer Apollo (structured city/state/country) |
| 117 | If Apollo and Hunter **agree** on name+title: `confidence = "high"` |
| 118 | If only one source has data: `confidence = "medium"` |
| 119 | If they **disagree** on name or title: flag conflict, keep both, `confidence = "low"` |
| 120 | |
| 121 | **Company merge rules:** |
| 122 | Name: prefer Apollo org name, cross-ref with Hunter + Brand.dev |
| 123 | LinkedIn URL: prefer Brand.dev socials, fallback Apollo |
| 124 | Description: prefer Brand.dev (richer), fallback Hunter |
| 125 | Employee count: prefer Apollo, cross-ref with Brand.dev + Hunter headcount |
| 126 | Funding: use Apollo `funding_events` and `total_funding` |
| 127 | Geo: prefer Apollo org location, cross-ref with Brand.dev |
| 128 | Tech stack: use Hunter `technologies` |
| 129 | Social URLs: use Brand.dev `socials` (twitter, github) |
| 130 | |
| 131 | **AI/B2B Classification (zero extra cost):** |
| 132 | |
| 133 | Cross-reference three sources from Phase 1: |
| 134 | |
| 135 | | Source | AI Signals | B2B Signals | |
| 136 | |--------|-----------|-------------| |
| 137 | | Brand.dev `description` + `industries.eic` | Parse description for: AI, ML, machine learning, deep learning, neural, LLM, GPT, NLP, computer vision | Parse for: SaaS, B2B, enterprise, platform, API, developer tools, infrastructure | |
| 138 | | Apollo `keywords[]` + `industry` | Match keywords against AI terms | Match keywords against B2B terms | |
| 139 | | Hunter `category` + company description | Check for AI/ML terms | Check for software/SaaS/B2B terms | |
| 140 | |
| 141 | Confidence rules: |
| 142 | `high`: 2+ sources agree |
| 143 | `medium`: 1 source has signal |
| 144 | `low`: weak inference only (e.g., "tech company" but no explicit AI/B2B terms) |
| 145 | |
| 146 | |
| 147 | |
| 148 | ### PHASE 2 — Gap-Fill (conditional) — $0.00-$0.02 |
| 149 | |
| 150 | **2a. Apollo Organization Enrich** ($0.01 — ONLY if Apollo Phase 1 returned NO `funding_events` or funding data is empty): |
| 151 | |
| 152 | |
| 153 | curl -s -X POST $GOOSEWORKS_API_BASE/v1/proxy/orthogonal/run \ |
| 154 | -H "Authorization: Bearer $GOOSEWORKS_API_KEY" \ |
| 155 | -H "Content-Type: application/json" \ |
| 156 | -d '{"api":"apollo","path":"/api/v1/organizations/enrich","query":{"domain":"{domain}"}}' |
| 157 | |
| 158 | |
| 159 | Extract: `organization.funding_events[]`, `organization.total_funding`, `organization.latest_funding_stage`, `organization.latest_funding_amount`, `organization.estimated_num_employees`, `organization.annual_revenue`. |
| 160 | |
| 161 | **2b. Tomba Enrich** ($0.01 — ONLY if Apollo and Hunter **disagree** on person name OR title): |
| 162 | |
| 163 | |
| 164 | curl -s -X POST $GOOSEWORKS_API_BASE/v1/proxy/orthogonal/run \ |
| 165 | -H "Authorization: Bearer $GOOSEWORKS_API_KEY" \ |
| 166 | -H "Content-Type: application/json" \ |
| 167 | -d '{"api":"tomba","path":"/v1/enrich","query":{"email":"{email}"}}' |
| 168 | |
| 169 | |
| 170 | Use as tie-breaker. If Tomba agrees with Apollo: use Apollo data. If Tomba agrees with Hunter: use Hunter data. If all three disagree: keep Apollo as primary, flag conflict. |
| 171 | |
| 172 | |
| 173 | |
| 174 | ### PHASE 3 — Sixtyfour Fallback (conditional, expensive) — $0.00-$0.20 |
| 175 | |
| 176 | **3a. Sixtyfour Enrich Lead** ($0.10 — ONLY if person NOT found after Phases 1-2, meaning no name AND no title AND no LinkedIn URL from any source): |
| 177 | |
| 178 | |
| 179 | curl -s -X POST $GOOSEWORKS_API_BASE/v1/proxy/orthogonal/run \ |
| 180 | -H "Authorization: Bearer $GOOSEWORKS_API_KEY" \ |
| 181 | -H "Content-Type: application/json" \ |
| 182 | -d '{"api":"sixtyfour","path":"/enrich-lead"}' |
| 183 | "lead_info": { |
| 184 | "email": "{email}", |
| 185 | "domain": "{domain}" |
| 186 | }, |
| 187 | "struct": { |
| 188 | "full_name": "Full legal name of this person", |
| 189 | "title": "Current job title", |
| 190 | "linkedin_url": "LinkedIn profile URL (full URL)", |
| 191 | "city": "City", |
| 192 | "state": "State or region", |
| 193 | "country": "Country" |
| 194 | } |
| 195 | }' |
| 196 | |
| 197 | |
| 198 | **3b. Sixtyfour Enrich Company** ($0.10 — ONLY if company has major gaps AND org has >500 employees): |
| 199 | |
| 200 | Major gaps = missing 2+ of: LinkedIn URL, description, employee count, funding data. |
| 201 | |
| 202 | |
| 203 | curl -s -X POST $GOOSEWORKS_API_BASE/v1/proxy/orthogonal/run \ |
| 204 | -H "Authorization: Bearer $GOOSEWORKS_API_KEY" \ |
| 205 | -H "Content-Type: application/json" \ |
| 206 | -d '{"api":"sixtyfour","path":"/enrich-company"}' |
| 207 | "target_company": { |
| 208 | "domain": "{domain}" |
| 209 | }, |
| 210 | "struct": { |
| 211 | "company_name": "Official company name", |
| 212 | "description": "One-paragraph description", |
| 213 | "linkedin_url": "LinkedIn company page URL", |
| 214 | "employee_count": "Number of employees", |
| 215 | "total_funding_usd": "Total funding raised in USD", |
| 216 | "latest_funding_date": "Most recent funding round date", |
| 217 | "latest_funding_stage": "Most recent round stage", |
| 218 | "latest_funding_amount_usd": "Most recent round amount" |
| 219 | } |
| 220 | }' |
| 221 | |
| 222 | |
| 223 | |
| 224 | |
| 225 | ### PHASE 4 — Buying Signals (qualified leads only) — $0.00-$0.04 |
| 226 | |
| 227 | **Gate**: Only run Phase 4 if the company is: |
| 228 | Funded (total_funding > 0) AND |
| 229 | Classified as B2B (is_b2b_saas = true) AND |
| 230 | Has >50 employees |
| 231 | |
| 232 | **4a. Brand.dev AI Products** ($0.03 — extracts products, pricing tiers, and features from the website): |
| 233 | |
| 234 | |
| 235 | curl -s -X POST $GOOSEWORKS_API_BASE/v1/proxy/orthogonal/run \ |
| 236 | -H "Authorization: Bearer $GOOSEWORKS_API_KEY" \ |
| 237 | -H "Content-Type: application/json" \ |
| 238 | -d '{"api":"brand-dev","path":"/v1/brand/ai/products"}' |
| 239 | "domain": "{domain}" |
| 240 | }' |
| 241 | |
| 242 | |
| 243 | From the products response, extract buying signals: |
| 244 | **has_enterprise_plan**: Check if any product has "enterprise" in name, tier, or target_audience |
| 245 | **has_self_serve**: Check if any product has a listed price (self-serve) vs "Contact sales" pricing |
| 246 | **target_market**: Infer from `target_audience` arrays across products |
| 247 | |
| 248 | **4b. Apollo Job Postings** ($0.01 — ONLY if `organization_id` was captured from Phase 1): |
| 249 | |
| 250 | |
| 251 | curl -s -X POST $GOOSEWORKS_API_BASE/v1/proxy/orthogonal/run \ |
| 252 | -H "Authorization: Bearer $GOOSEWORKS_API_KEY" \ |
| 253 | -H "Content-Type: application/json" \ |
| 254 | -d '{"api":"apollo","path":"/api/v1/organizations/{organization_id}/job_postings","query":{"organization_id":"{organization_id}"}}' |
| 255 | |
| 256 | |
| 257 | Search job postings for enterprise sales signals: titles containing "Enterprise", "Account Executive", "Solutions Engineer", "Sales Director", "Customer Success". If found, set `hiring_enterprise_reps = true`. |
| 258 | |
| 259 | |
| 260 | |
| 261 | ### PHASE 5 — Cheap/Free Signals — $0.00-$0.01 |
| 262 | |
| 263 | **5a. GitHub Stars** (free — ONLY if Brand.dev socials or Apollo data returned a GitHub URL): |
| 264 | |
| 265 | |
| 266 | # Extract org name from GitHub URL, e.g., https://github.com/ngrok -> ngrok |
| 267 | # Use the GitHub public API (no auth needed for public repos): |
| 268 | curl -s "https://api.github.com/orgs/{org_name}/repos?sort=stars&per_page=5" | jq '[.[] | {name: .name, stars: .stargazers_count}]' |
| 269 | |
| 270 | |
| 271 | Sum the top repo stars or report the flagship repo star count. |
| 272 | |
| 273 | **5b. Twitter/X Followers** (Scrape Creators — ONLY if a Twitter handle was found in Brand.dev socials or Apollo data): |
| 274 | |
| 275 | |
| 276 | provider: scrapecreators |
| 277 | method: GET |
| 278 | path: /v1/twitter/profile |
| 279 | query: |
| 280 | handle: "{twitter_handle}" |
| 281 | |
| 282 | |
| 283 | Extract: `legacy.followers_count`, `legacy.friends_count`, `legacy.statuses_count`, `legacy.description`. |
| 284 | |
| 285 | |
| 286 | |
| 287 | ### FINAL — Compile & Output |
| 288 | |
| 289 | Merge all phase results into the output format. Track which phases ran. |
| 290 | |
| 291 | ## Output Format |
| 292 | |
| 293 | Present the results as a JSON code block: |
| 294 | |
| 295 | |
| 296 | { |
| 297 | "person": { |
| 298 | "full_name": "string", |
| 299 | "title": "string", |
| 300 | "linkedin_url": "string", |
| 301 | "location": {"city": "string", "state": "string", "country": "string"}, |
| 302 | "email_verified": "deliverable | undeliverable | risky | unknown", |
| 303 | "confidence": "high | medium | low", |
| 304 | "source": "apollo | hunter | sixtyfour | tomba | merged" |
| 305 | }, |
| 306 | "company": { |
| 307 | "name": "string", |
| 308 | "domain": "string", |
| 309 | "linkedin_url": "string", |
| 310 | "description": "string", |
| 311 | "geo": {"city": "string", "state": "string", "country": "string"}, |
| 312 | "employee_count": "number | null", |
| 313 | "founded_year": "number | null", |
| 314 | "funding": { |
| 315 | "total_amount": "number | null", |
| 316 | "total_amount_printed": "string | null", |
| 317 | "latest_round_date": "string | null", |
| 318 | "latest_round_stage": "string | null", |
| 319 | "latest_round_amount": "number | null", |
| 320 | "rounds": [{"date": "", "type": "", "amount": 0, "investors": ""}], |
| 321 | "confidence": "high | medium | low" |
| 322 | }, |
| 323 | "classification": { |
| 324 | "is_ai": {"value": true, "confidence": "high", "evidence": ["Brand.dev description mentions ML", "Apollo keywords include 'artificial intelligence'"]}, |
| 325 | "is_b2b_saas": {"value": true, "confidence": "high", "evidence": ["Hunter category: software", "Apollo industry: SaaS"]} |
| 326 | }, |
| 327 | "buying_signals": { |
| 328 | "has_enterprise_plan": "boolean | null", |
| 329 | "has_self_serve": "boolean | null", |
| 330 | "hiring_enterprise_reps": "boolean | null", |
| 331 | "website_traffic_rank": "number | null", |
| 332 | "github_stars": "number | null", |
| 333 | "twitter_followers": "number | null", |
| 334 | "tech_stack": ["array | null"] |
| 335 | }, |
| 336 | "confidence": "high | medium | low", |
| 337 | "source": "apollo | hunter | brand-dev | sixtyfour | merged" |
| 338 | }, |
| 339 | "meta": { |
| 340 | "total_cost": "$0.XX", |
| 341 | "api_calls": [ |
| 342 | { |
| 343 | "api": "apollo", |
| 344 | "endpoint": "/api/v1/people/match", |
| 345 | "status": "success", |
| 346 | "cost": "$0.01", |
| 347 | "latency_ms": 1200, |
| 348 | "fields_returned": ["name", "title", "linkedin_url", "organization"], |
| 349 | "fields_missing": [], |
| 350 | "error": null |
| 351 | } |
| 352 | ], |
| 353 | "phases_run": [1, 2, 4, 5], |
| 354 | "enrichment_timestamp": "ISO datetime" |
| 355 | } |
| 356 | } |
| 357 | |
| 358 | |
| 359 | ## Error Visibility |
| 360 | |
| 361 | Track EVERY API call in the `meta.api_calls` array with this structure: |
| 362 | |
| 363 | |
| 364 | { |
| 365 | "api": "string (apollo | hunter | brand-dev | sixtyfour | tomba | scrapecreators | github)", |
| 366 | "endpoint": "string", |
| 367 | "status": "success | partial | error | skipped", |
| 368 | "cost": "$0.XX", |
| 369 | "latency_ms": 0, |
| 370 | "fields_returned": [], |
| 371 | "fields_missing": [], |
| 372 | "error": "string | null" |
| 373 | } |
| 374 | |
| 375 | |
| 376 | Rules: |
| 377 | **If an API call fails, returns empty data, or times out**: include it with `status='error'` and a clear error message. Never silently skip failures. |
| 378 | **If an API call was skipped due to gating logic** (e.g., Brand.dev skipped for free email): include it with `status='skipped'`, `cost='$0.00'`, and reason in error field (e.g., "Skipped: free email provider"). |
| 379 | **If an API call returns partial data**: use `status='partial'`, list what was returned and what was missing. |
| 380 | |
| 381 | ## Cost Tracking |
| 382 | |
| 383 | Sum all API call costs and report in `meta.total_cost`: |
| 384 | |
| 385 | | API | Endpoint | Cost | When | |
| 386 | |-----|----------|------|------| |
| 387 | | Apollo | /api/v1/people/match | $0.01 | Always (Phase 1) | |
| 388 | | Hunter | /v2/combined/find | $0.01 | Always (Phase 1) | |
| 389 | | Brand.dev | /v1/brand/retrieve | $0.03 | Phase 1, skip for free email | |
| 390 | | Hunter | /v2/email-verifier | $0.01 | Always (Phase 1) | |
| 391 | | Apollo | /api/v1/organizations/enrich | $0.01 | Phase 2, only if funding missing | |
| 392 | | Tomba | /v1/enrich | $0.01 | Phase 2, only if person data conflicts | |
| 393 | | Sixtyfour | /enrich-lead | $0.10 | Phase 3, only if person not found | |
| 394 | | Sixtyfour | /enrich-company | $0.10 | Phase 3, only if major gaps + >500 employees | |
| 395 | | Brand.dev | /v1/brand/ai/products | $0.03 | Phase 4, only if funded + B2B + >50 employees | |
| 396 | | Apollo | /organizations/{id}/job_postings | $0.01 | Phase 4, only if org_id available | |
| 397 | | Scrape Creators | /v1/twitter/profile | ~$0.01 | Phase 5, only if Twitter handle found | |
| 398 | | GitHub API | public | $0.00 | Phase 5, only if GitHub URL found | |
| 399 | |
| 400 | ## Example |
| 401 | |
| 402 | **Input**: `[email protected]` |
| 403 | |
| 404 | **Expected flow**: |
| 405 | Domain: `acme.com`, `is_free_email = false` |
| 406 | **Phase 1** (parallel): Apollo people/match, Hunter combined, Brand.dev retrieve, Hunter email-verifier |
| 407 | **Phase 1 merge**: Cross-reference person data, classify AI/B2B from descriptions+keywords |
| 408 | **Phase 2**: Check if Apollo returned funding — if not, call Apollo org enrich. Check if person data conflicts — if so, call Tomba. |
| 409 | **Phase 3**: Skip if person found and company data sufficient |
| 410 | **Phase 4**: If company is funded + B2B + >50 employees, run Brand.dev AI products + Apollo job postings |
| 411 | **Phase 5**: If GitHub URL found, grab star counts. If Twitter handle found, grab follower count via Scrape Creators |
| 412 | Compile and output JSON |
| 413 | |
| 414 | ## Tips |
| 415 | |
| 416 | Phase 1 calls should all fire simultaneously — they're independent |
| 417 | Apollo's people/match is the single best-value call — it returns person AND embedded company data including funding events |
| 418 | Brand.dev is the richest source for company description, industry classification, and social URLs — but costs 3x more than Apollo/Hunter, so skip it for free email providers |
| 419 | The AI/B2B classification uses data already returned by Phase 1 — no extra API calls needed |
| 420 | Hunter's `technologies` array is the only source of tech stack data — valuable for technical buyers |
| 421 | Phase 3 (Sixtyfour) should be rare — Apollo + Hunter find most people. Only trigger for truly obscure leads |
| 422 | Phase 4 buying signals are the most actionable data for GTM — but gate them to avoid wasting $0.04 on unqualified leads |
| 423 | GitHub stars and Twitter followers are cheap/free social proof signals — always grab them if URLs/handles are available |
| 424 |