Goose-skills inbound-lead-qualification
git clone https://github.com/gooseworks-ai/goose-skills
T=$(mktemp -d) && git clone --depth=1 https://github.com/gooseworks-ai/goose-skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/composites/inbound-lead-qualification" ~/.claude/skills/gooseworks-ai-goose-skills-inbound-lead-qualification && rm -rf "$T"
skills/composites/inbound-lead-qualification/SKILL.mdInbound Lead Qualification
Takes a set of inbound leads and validates each against your full ICP criteria. Not a fast-pass triage (that's
inbound-lead-triage) — this is the thorough qualification step that determines whether a lead is genuinely worth pursuing, and produces a scored CSV for the team.
When to Auto-Load
Load this composite when:
- User says "qualify these inbound leads", "check if these leads are ICP", "score my inbound"
- An upstream triage has been completed and leads need deeper qualification
- User has a batch of leads and wants a qualified/disqualified verdict on each
Architecture
[Inbound Leads] → Step 1: Load ICP & Config → Step 2: CRM/Pipeline Check → Step 3: Company Qualification → Step 4: Person Qualification → Step 5: Use Case Fit → Step 6: Score & Verdict → Step 7: Output CSV
Step 0: Configuration (Once Per Client)
On first run, establish the ICP definition and CRM access. Save to the current working directory or wherever the user prefers (e.g.,
config/lead-qualification.json).
{ "icp_definition": { "company_size": { "min_employees": null, "max_employees": null, "sweet_spot": "", "notes": "" }, "industry": { "target_industries": [], "excluded_industries": [], "notes": "" }, "use_case": { "primary_use_cases": [], "secondary_use_cases": [], "anti_use_cases": [], "notes": "" }, "company_stage": { "target_stages": [], "excluded_stages": [], "notes": "" }, "geography": { "target_regions": [], "excluded_regions": [], "notes": "" } }, "buyer_personas": [ { "name": "", "titles": [], "seniority_levels": [], "departments": [], "is_economic_buyer": false, "is_champion": false, "is_user": false } ], "hard_disqualifiers": [], "hard_qualifiers": [], "crm_access": { "tool": "HubSpot | Salesforce | CSV export | none", "access_method": "", "tables_or_objects": [] }, "existing_customer_source": { "tool": "HubSpot | Salesforce | CSV | none", "access_method": "" }, "qualification_prompt_path": "path/to/lead-qualification/prompt.md or null" }
If
capability already has a saved qualification prompt: Reference it directly — don't rebuild ICP criteria from scratch.lead-qualification
On subsequent runs: Load config silently.
Step 1: Load ICP Criteria & Parse Leads
Process
- Load the client's ICP config (or qualification prompt from
capability)lead-qualification - Parse the inbound lead list — accept any format:
- Output from
(already normalized)inbound-lead-triage - Raw CSV with any column structure
- Pasted list of names/emails/companies
- CRM export
- Output from
- Identify what data is available vs. missing per lead:
- Have: Fields present in the input
- Need: Fields required for qualification but missing
- Gap report: "X leads have company name, Y have title, Z have nothing but email"
Output
- Parsed lead list with available/missing field inventory
- Gap report for the user
Human Checkpoint
If >50% of leads are missing critical fields (company name or person title), recommend running
inbound-lead-enrichment first. Ask: "Many leads are missing company/title data. Want me to enrich them first, or qualify with what's available?"
Step 2: CRM & Pipeline Check
Process
For each lead, check against existing data sources to identify overlaps:
Check 1 — Existing customer?
- Search customer database by company domain/name
- If match found: Flag as
with customer details (plan, account owner, contract status)existing_customer - This is NOT a disqualification — it's a routing flag (upsell vs. new business)
Check 2 — Already in pipeline?
- Search your CRM (HubSpot, Salesforce, CSV) for the company in active deals
- If match found: Flag as
with deal details (stage, owner, last activity)in_pipeline - Critical: Sales rep should know before reaching out that a colleague already has this account
Check 3 — Previous engagement?
- Search outreach logs for the email/company
- If match found: Flag as
with history summary (when, what channel, outcome)previously_contacted
Check 4 — Known from signal composites?
- Search your CRM or signal tracking system for the company
- If match found: Flag as
with signal type and datesignal_flagged
Output
Each lead tagged with:
:pipeline_status
|new
|existing_customer
|in_pipelinepreviously_contacted
: One sentence explaining the overlap (or null)pipeline_detail
: Any signal composite matchessignal_flags
Handling Overlaps
- Existing customer: Don't disqualify. Mark separately. Might be expansion/upsell.
- In pipeline: Don't disqualify. Flag for sales rep coordination. Note the existing deal owner.
- Previously contacted but no response: Still qualify. The inbound signal means they're now warmer.
- Previously contacted and rejected: Still qualify the inbound. People change their minds. Note the prior context.
Step 3: Company Qualification
Process
For each lead's company, evaluate against every ICP company dimension:
Dimension 1 — Company Size
- Check employee count against ICP range
- Sources: enrichment data, LinkedIn company page, web search
- Score:
|match
|borderline
|mismatchunknown - Note: If the lead is from a subsidiary or division, evaluate the relevant unit, not the parent company
Dimension 2 — Industry
- Check against target and excluded industry lists
- Be smart about classification: "AI-powered HR platform" matches both "AI/ML" and "HR Tech"
- Score:
|match
(related but not core target) |adjacent
|mismatchunknown
Dimension 3 — Company Stage
- Seed, Series A, Series B+, Growth, Public, Bootstrapped
- Sources: SixtyFour or Orthogonal, news, enrichment data
- Score:
|match
|borderline
|mismatchunknown
Dimension 4 — Geography
- Check HQ location and/or the specific person's location
- For remote-first companies, check where the majority of the team is
- Score:
|match
|borderline
|mismatchunknown
Dimension 5 — Use Case Fit
- Based on what the company does, could they plausibly use the product?
- This is the most nuanced dimension — requires understanding both the product and the company's operations
- Sources: company website, product description, job postings (hint at internal tools/processes)
- Score:
|strong_fit
|moderate_fit
|weak_fit
|no_fitunknown
Output
Each lead gets a
company_qualification block:
{ "company_size": { "score": "", "value": "", "reasoning": "" }, "industry": { "score": "", "value": "", "reasoning": "" }, "stage": { "score": "", "value": "", "reasoning": "" }, "geography": { "score": "", "value": "", "reasoning": "" }, "use_case": { "score": "", "value": "", "reasoning": "" }, "company_verdict": "qualified | borderline | disqualified | insufficient_data" }
Step 4: Person Qualification
Process
For each lead's contact person, evaluate against buyer persona criteria:
Dimension 1 — Title/Role Match
- Check title against buyer persona title lists
- Handle variations: "VP of Marketing" = "Vice President, Marketing" = "VP Marketing"
- Be smart about title inflation at small companies (a "Director" at a 10-person startup ≠ "Director" at a 10,000-person enterprise)
- Score:
|exact_match
|close_match
|adjacent
|mismatchunknown
Dimension 2 — Seniority Level
- Map to: Individual Contributor, Manager, Director, VP, C-Level, Founder
- Check against ICP seniority requirements
- Score:
|match
|too_junior
|too_seniorunknown
Dimension 3 — Department
- Engineering, Product, Marketing, Sales, Operations, Finance, HR, etc.
- Check against ICP department targets
- Score:
|match
|adjacent
|mismatchunknown
Dimension 4 — Authority Type
- Based on title + seniority, classify:
— Can sign the checkeconomic_buyer
— Wants it, can influence the decisionchampion
— Would use it daily, can validate needuser
— Tasked with research, limited decision powerevaluator
— Can block but not approvegatekeeperunknown
Dimension 5 — Right Person, Wrong Company (or Vice Versa)
- If company qualifies but person doesn't: Flag as
— this is a referral opportunityright_company_wrong_person - If person qualifies but company doesn't: Flag as
— rare for inbound, but possible with job changersright_person_wrong_company
Output
Each lead gets a
person_qualification block:
{ "title_match": { "score": "", "value": "", "reasoning": "" }, "seniority": { "score": "", "value": "", "reasoning": "" }, "department": { "score": "", "value": "", "reasoning": "" }, "authority_type": "", "person_verdict": "qualified | borderline | disqualified | insufficient_data", "mismatch_type": "null | right_company_wrong_person | right_person_wrong_company" }
Step 5: Use Case Fit Assessment
Process
This step connects the company's likely needs to your product's actual capabilities. It goes deeper than Step 3's company-level use case check.
-
Infer the lead's intent from their inbound action:
- Demo request message → What did they say they need?
- Content downloaded → What topic were they researching?
- Webinar attended → What problem were they trying to solve?
- Free trial signup → What feature did they try first?
- Chatbot conversation → What questions did they ask?
-
Map intent to product capabilities:
- Does the product actually solve what they seem to need?
- Is this a primary use case or a stretch?
- Are there known limitations that would disappoint them?
-
Assess implementation feasibility:
- Based on company size and stage, can they realistically implement?
- Do they likely have the technical resources / team to adopt?
- Any known blockers for companies like this? (e.g., "banks need SOC2 and we don't have it yet")
Output
{ "inferred_intent": "", "intent_source": "", "product_fit": "strong | moderate | weak | unknown", "product_fit_reasoning": "", "implementation_feasibility": "easy | moderate | complex | unlikely", "known_blockers": [] }
Step 6: Score & Verdict
Scoring Logic
Combine all dimensions into a final qualification verdict.
Composite Score Calculation:
| Dimension | Weight | Possible Values |
|---|---|---|
| Company Size | 15% | match=100, borderline=50, mismatch=0, unknown=30 |
| Industry | 20% | match=100, adjacent=60, mismatch=0, unknown=30 |
| Company Stage | 10% | match=100, borderline=50, mismatch=0, unknown=30 |
| Geography | 10% | match=100, borderline=50, mismatch=0, unknown=30 |
| Use Case Fit | 25% | strong=100, moderate=60, weak=20, no_fit=0, unknown=30 |
| Person Title/Role | 15% | exact=100, close=75, adjacent=40, mismatch=0, unknown=30 |
| Person Seniority | 5% | match=100, too_junior=20, too_senior=60, unknown=30 |
Hard overrides (bypass the score):
- Any hard disqualifier present →
regardless of scoredisqualified - Any hard qualifier present →
regardless of score (but still show the full breakdown)qualified - Existing customer → Route separately, don't score as new lead
Verdict thresholds:
- Score ≥ 75:
— Pursue activelyqualified - Score 50-74:
— Qualified with caveats, may need manual reviewborderline - Score 30-49:
— Not qualified now, but close enough to consider (referral or nurture)near_miss - Score < 30:
— Does not fit ICPdisqualified
Sub-verdicts for routing:
— Score ≥ 75 AND Tier 1/2 urgency from triagequalified_hot
— Score ≥ 75 AND Tier 3/4 urgencyqualified_warm
— Score 50-74, needs human judgment callborderline_review
— Score 30-49 AND right_company_wrong_person (referral opportunity)near_miss_referral
— Score 30-49, might fit in the futurenear_miss_nurture
— Score < 30, needs polite declinedisqualified_polite
— Competitor employeedisqualified_competitor
— Existing customer with expansion signalexisting_customer_upsell
Output
Each lead gets:
{ "composite_score": 0-100, "verdict": "", "sub_verdict": "", "top_qualification_reasons": [], "top_disqualification_reasons": [], "summary": "One sentence: why this lead is/isn't a fit" }
Step 7: Output CSV
CSV Structure
Produce a CSV with ALL input fields preserved plus qualification columns appended:
Core qualification columns:
— qualified | borderline | near_miss | disqualifiedqualification_verdict
— qualified_hot | qualified_warm | borderline_review | near_miss_referral | near_miss_nurture | disqualified_polite | disqualified_competitor | existing_customer_upsellqualification_sub_verdict
— 0-100composite_score
— One sentence qualification reasoningsummary
Pipeline check columns:
— new | existing_customer | in_pipeline | previously_contactedpipeline_status
— One sentence on the overlappipeline_detail
— Any signal composite matchessignal_flags
Company qualification columns:
— match | borderline | mismatch | unknowncompany_size_score
— match | adjacent | mismatch | unknownindustry_score
— match | borderline | mismatch | unknownstage_score
— match | borderline | mismatch | unknowngeography_score
— strong | moderate | weak | no_fit | unknownuse_case_score
Person qualification columns:
— exact_match | close_match | adjacent | mismatch | unknowntitle_match_score
— match | too_junior | too_senior | unknownseniority_score
— economic_buyer | champion | user | evaluator | gatekeeper | unknownauthority_type
— null | right_company_wrong_person | right_person_wrong_companymismatch_type
Use case columns:
— What they seem to needinferred_intent
— strong | moderate | weak | unknownproduct_fit
— easy | moderate | complex | unlikelyimplementation_feasibility
Save Location
The current working directory or wherever the user prefers (e.g.,
leads/inbound-qualified-[date].csv).
Summary Report
After producing the CSV, present a summary:
## Inbound Lead Qualification: [Period] **Total leads processed:** X **Qualified:** X (Y%) — X hot, X warm **Borderline (manual review):** X (Y%) **Near miss:** X (Y%) — X referral opportunities, X nurture **Disqualified:** X (Y%) **Pipeline overlaps:** - Existing customers: X (route to CS) - Already in pipeline: X (coordinate with deal owner) - Previously contacted: X (now warmer — re-engage) **Top qualification reasons:** 1. [reason] — X leads 2. [reason] — X leads **Top disqualification reasons:** 1. [reason] — X leads 2. [reason] — X leads **Data quality:** - Leads with full data: X - Leads with partial data (some dimensions scored as 'unknown'): X - Leads needing enrichment: X **CSV saved to:** [path]
Handling Edge Cases
Lead with only an email (no name, no company):
- Extract company domain from email
- If corporate domain: look up the company, proceed with company qualification (person qualification will be mostly "unknown")
- If personal email (gmail, yahoo): Score as
, recommend enrichment or manual reviewinsufficient_data
Same company, multiple leads:
- Qualify the company once, apply to all leads from that company
- Person qualification runs individually for each
- Flag the multi-contact opportunity: "3 people from [Company] came inbound — potential committee buy"
Contradictory signals:
- Company is strong fit but person is completely wrong (e.g., intern at a perfect company)
- Score honestly. The sub-verdict
routes this to referral handling inright_company_wrong_persondisqualification-handling
Borderline calls:
- When the score is 50-74 and could go either way, lean toward qualifying for inbound leads
- Rationale: they came to YOU. The intent signal tips borderline cases toward "worth a conversation"
- Note this lean in the reasoning: "Borderline on [dimension], but inbound intent suggests pursuing"
Scoring with missing data:
- Unknown dimensions score at 30 (not 0, not 50) — absence of data is mildly negative but not disqualifying
- If >3 dimensions are "unknown", the lead is
regardless of score — recommend enrichment firstinsufficient_data
Tools Required
- CRM access — to check pipeline, existing customers, outreach history
- Web search — for company research when enrichment data is sparse
- Enrichment tools — SixtyFour or Orthogonal, LinkedIn scraper, or similar (optional, enhances accuracy)
- Read/Write — for CSV I/O and config management