contact-hunter-skill
About contact-hunter-skill
contact-hunter-skill finds, enriches, and qualifies people’s contact details using HubSpot, Apollo, and Clay. Designed for BDR and sales workflows, it applies qualification before enrichment and returns contact cards or CSV data.
Key features
- 01Searches HubSpot for existing contacts first
- 02Qualifies each candidate before enrichment
- 03Enriches details through Apollo and Clay
- 04Returns cards with tiers and suppression status
- 05Supports CSV handoff or Gmail drafts
Use cases
- 01Finding people in a specific role at a company
- 02Checking and enriching existing contact records
- 03Preparing call lists or sequence-load CSVs
MCP support
The skill calls MCP tools for HubSpot, Apollo, and Clay to search contacts, qualify candidates, and enrich contact details. It returns contact cards, CSV handoffs, or Gmail drafts.
Install
Copy the command and run it in a terminal.
npx skills add https:/ / github.com/ scientiacapital/ skills --skill contact-hunter-skillAfter installing, type/contact-hunter-skillin Claude Code to confirm it loaded.
Bundled filesconfig.json
Details
From the project READMEContact Hunter
Find, enrich, and qualify contact information for specific people or companies using live MCP tool calls to HubSpot, Apollo, and Clay. Returns contact cards with email, phone, title, ATL/BTL tier, and suppression status — ready for the BDR dial list or sequence load. Keywords: `contact hunter`, `find contacts`, `who works at`, `enrich contact`<quick_start> Trigger: "Find the VP of Sales at [Company]" or "Get contact info for [Name] at [Company]" or "Hunt contacts at [Company]" Input: Person name, company, job title, location, or LinkedIn URL Output: Qualified contact cards with email, phone, LinkedIn, ATL/BTL tier, suppression status, and HubSpot record link
Tool order:
hubspot_search_contacts(Epiphan AI) — does the contact / company already exist?qualify_lead(Epiphan AI) — Gate Q on every candidate: exclude / STALE AE LEAD / ATL-BTL tier. Nothing passes to enrichment without a verdict.apollo_contacts_search(Apollo plugin) →find-and-enrich-contacts-at-company(Clay) — waterfall only for contacts that passed Gate Q.- Deliver: contact cards + summary line; Gmail
create_draftper lead when the request is a call list (Gate D), or CSV handoff tosequence-load-skill. </quick_start>
<house_rules> Customer-facing skill. Before output, run the gates in order (details: ~/.claude/skills/epiphan-house-rules-skill/reference/gates.md): S: search_product_catalog (Epiphan AI) or Epiphan Knowledge search before any product claim. B: check_my_copy on every customer-facing string. Q: qualify_lead right after the candidate pull, before tiering/enrichment/drafts. D: outbound text becomes a Gmail draft, never a send. Identifiers (owner IDs, portal, emails, seats) come from reference/identifiers.md in that skill — do not restate them here. On any gate failure follow reference/self-healing.md (retry, degrade with a flag, alert Tim, halt) and append a line to ~/.claude/skill-runs/.jsonl. </house_rules>
<success_criteria>
- HubSpot searched first (check if contact already exists)
-
qualify_leadrun on every candidate before enrichment — customers, channel partners, product-only engagers, active AE-owned, suppressed, and non-NA excluded; stale AE leads surfaced asSTALE AE LEAD - Apollo waterfall run only if HubSpot returns no result and Gate Q passed
- Clay enrichment run for phone/LinkedIn if Apollo has email only
- ATL/BTL tier taken from the
qualify_leadverdict (no inline keyword matching) - Contact card output with HubSpot record URL; summary line with excluded/suppressed counts
- Empty result vs tool error distinguished in the summary and the sidecar
- Outcome sidecar + run-log line written </success_criteria>
Stage 1: Search HubSpot First
MCP Tool: mcp__claude_ai_Epiphan_Ai__hubspot_search_contacts
Search by: company name OR person name OR email domain
Return: contactId, firstName, lastName, email, phone, jobtitle, hubspot_owner_id,
lifecyclestage, device_count, bdr_suppression_until, last_activity_date, country
Found or not, every candidate (existing record or the search target itself) goes to Stage 2. If the target company resolves to an existing customer at this point, stop, tell Tim, do not enrich.
Stage 2: Gate Q — qualify_lead (source of truth for dedupe, tier, suppression)
MCP Tool: mcp__claude_ai_Epiphan_Ai__qualify_lead — one call per candidate contact (or per
company when hunting a role, then again per returned person).
The verdict decides, in this order:
- Exclude — existing customer, channel partner, product-only engager, AE-owned with activity < 90 days, suppressed, or outside USA/Canada (unless the request names another geo).
- STALE AE LEAD — AE-owned with activity > 90 days (NA-only for Lex and Phil): surface to Tim with tier and recommended action; Tim decides. Do not enrich until he says so.
- Tier —
power_levelatl / btl / unknown is the ATL/BTL tier on the card.unknown→ markGRAYfor manual review. Do not re-implement the keyword tables; they live in CLAUDE.md.
Golden Rules detail and AE owner IDs: ~/.claude/skills/epiphan-house-rules-skill/reference/gates.md
and reference/identifiers.md. qualify_lead is a dry run by default — never pass
writeToHubSpot: true from this skill.
If qualify_lead is unavailable after one retry: halt this hunt, report "Gate Q unavailable",
alert Tim. A list the gate has not seen is never produced.
Contacts found in HubSpot that pass Gate Q skip to Stage 4 (they are already enriched enough). Role hunts with no HubSpot match continue to Stage 3.
Stage 3: Enrichment Waterfall (Gate Q passed only)
MCP Tool: mcp__plugin_sales_apollo__apollo_contacts_search
Search by: person_title OR organization_name
Filters: country IN [US, CA], seniority IN [director, vp, c_suite, owner]
Limit: 10 results
New people returned by Apollo go back through Stage 2 (qualify_lead) before they are enriched
further or shown.
If Apollo has email but no phone → run Clay enrichment:
MCP Tool: mcp__claude_ai_Clay__find-and-enrich-contacts-at-company
company: [company name]
title_keywords: [from search parameters]
enrich_phone: true
Tier ladder: no result at a tier → next tier. All tiers empty → report zero with the reason per tier ("Apollo: 0 matches", "Clay: no phone"). A tier that errors (auth, rate limit, timeout) is retried once, then skipped and named in the summary — never reported as "0 found". No email-pattern guessing, no web dorking: verified sources only (HubSpot, Apollo, Clay).
Stage 4: Output Contact Cards
For each contact that passed Gate Q, output one card (single format):
┌─────────────────────────────────────────────────────┐
│ JOHN SMITH [ATL — VP] │
│ VP of Engineering @ Acme Corp │
├─────────────────────────────────────────────────────┤
│ Email: [email protected] (Apollo, verified) │
│ Phone: +1 (415) 555-0123 (Clay waterfall) │
│ LinkedIn: linkedin.com/in/johnsmith │
│ Location: San Francisco, CA │
├─────────────────────────────────────────────────────┤
│ HubSpot: <contact URL per identifiers.md> │
│ Owner: Unowned (eligible for BDR outreach) │
│ Qualify: pass · tier ATL · not suppressed │
└─────────────────────────────────────────────────────┘
Bulk output (CSV format for sequence-load):
First,Last,Title,Company,Email,Phone,LinkedIn,ATL_Tier,HubSpot_ID
John,Smith,VP Engineering,Acme Corp,[email protected],+14155550123,linkedin.com/in/johnsmith,ATL,{id}
Summary line after each hunt:
Found: [N] contacts | [A] ATL | [G] Gray | [B] BTL | [X] excluded by qualify_lead | [S] stale AE surfaced | tiers skipped: [none|Apollo error|Clay error]
Stage 5: Delivery (Gate D)
- Call list request ("hunt contacts and set up outreach"): for every card, stage a Gmail draft via
mcp__claude_ai_Gmail__create_draftfrom[email protected]. Any product line in the draft passes Gate S first; the body passescheck_my_copy(Gate B). Never send. - Sequence request: hand the CSV to
sequence-load-skill(confirm-first enrollment). - Lookup only: cards + summary line, no drafts.
Compliance: All data sourced from HubSpot (existing CRM records), Apollo (public professional data), or Clay (verified enrichment). No scraping. Follow CAN-SPAM for outreach.
<self_healing>
partial = cards were produced but a stage degraded: an enrichment tier errored and was skipped,
Clay returned no phone, Gate B/S skipped for a draft (prefix it [brand check skipped] /
[spec unverified]). error = no cards and no zero-with-reason summary (HubSpot search failed
after retry, or input could not be resolved to a person/company). halt = qualify_lead
unavailable → stop, alert Tim, write state. Ladder per
~/.claude/skills/epiphan-house-rules-skill/reference/self-healing.md: retry once → degrade with a
flag → alert Tim → halt. Append one line per run to ~/.claude/skill-runs/contact-hunter-skill.jsonl.
</self_healing>
Emit Outcome Sidecar
As the final step, write to ~/.claude/skill-analytics/last-outcome-contact-hunter-skill.json:
{"ts":"[UTC ISO8601]","skill":"contact-hunter-skill","version":"<frontmatter version>","variant":"default",
"status":"[success|partial|error]","runtime_ms":[estimated ms from start],
"metrics":{"contacts_searched":[n],"contacts_found":[n],"phones_found":[n],"emails_found":[n],"excluded_by_qualify":[n],"stale_ae_surfaced":[n],"tiers_errored":[n]},
"error":null,"session_id":"[YYYY-MM-DD]"}
Use status "partial" if some stages failed but results were produced; error must name the stage. Use "error" only if no output was generated.