Qualifies inbound leads against full ICP criteria — company size, industry, use case fit, role/seniority of the person. Checks CRM and existing customer base for duplicates and existing relationships. Outputs a scored CSV with qualification status, reasoning, and pipeline overlap flags. Tool-agnostic — works with any CRM, enrichment tool, or data source.
Scanned 9/4/2026
Install to Claude Code
npx -y skills add gooseworks-ai/goose-skills --skill inbound-lead-qualification --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Inbound Lead Qualification?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/gooseworks-ai-inbound-lead-qualification-goose-skills)More formats (shields.io, HTML) on the badges page.
---
name: inbound-lead-qualification
version: 1.0.0
description: >
Qualifies inbound leads against full ICP criteria — company size, industry, use case fit,
role/seniority of the person. Checks CRM and existing customer base for duplicates and
existing relationships. Outputs a scored CSV with qualification status, reasoning, and
pipeline overlap flags. Tool-agnostic — works with any CRM, enrichment tool, or data source.
tags: [lead-generation]
---
# Inbound 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`).
```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 `lead-qualification` capability already has a saved qualification prompt:** Reference it directly — don't rebuild ICP criteria from scratch.
**On subsequent runs:** Load config silently.
---
## Step 1: Load ICP Criteria & Parse Leads
### Process
1. Load the client's ICP config (or qualification prompt from `lead-qualification` capability)
2. Parse the inbound lead list — accept any format:
- Output from `inbound-lead-triage` (already normalized)
- Raw CSV with any column structure
- Pasted list of names/emails/companies
- CRM export
3. 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 `existing_customer` with customer details (plan, account owner, contract status)
- 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 `in_pipeline` with deal details (stage, owner, last activity)
- 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 `previously_contacted` with history summary (when, what channel, outcome)
**Check 4 — Known from signal composites?**
- Search your CRM or signal tracking system for the company
- If match found: Flag as `signal_flagged` with signal type and date
### Output
Each lead tagged with:
- `pipeline_status`: `new` | `existing_customer` | `in_pipeline` | `previously_contacted`
- `pipeline_detail`: One sentence explaining the overlap (or null)
- `signal_flags`: Any signal composite matches
### 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` | `mismatch` | `unknown`
- 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` | `adjacent` (related but not core target) | `mismatch` | `unknown`
**Dimension 3 — Company Stage**
- Seed, Series A, Series B+, Growth, Public, Bootstrapped
- Sources: SixtyFour or Orthogonal, news, enrichment data
- Score: `match` | `borderline` | `mismatch` | `unknown`
**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` | `mismatch` | `unknown`
**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_fit` | `unknown`
### 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` | `mismatch` | `unknown`
**Dimension 2 — Seniority Level**
- Map to: Individual Contributor, Manager, Director, VP, C-Level, Founder
- Check against ICP seniority requirements
- Score: `match` | `too_junior` | `too_senior` | `unknown`
**Dimension 3 — Department**
- Engineering, Product, Marketing, Sales, Operations, Finance, HR, etc.
- Check against ICP department targets
- Score: `match` | `adjacent` | `mismatch` | `unknown`
**Dimension 4 — Authority Type**
- Based on title + seniority, classify:
- `economic_buyer` — Can sign the check
- `champion` — Wants it, can influence the decision
- `user` — Would use it daily, can validate need
- `evaluator` — Tasked with research, limited decision power
- `gatekeeper` — Can block but not approve
- `unknown`
**Dimension 5 — Right Person, Wrong Company (or Vice Versa)**
- If company qualifies but person doesn't: Flag as `right_company_wrong_person` — this is a referral opportunity
- If person qualifies but company doesn't: Flag as `right_person_wrong_company` — rare for inbound, but possible with job changers
### 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.
1. **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?
2. **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?
3. **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 → `disqualified` regardless of score
- Any hard qualifier present → `qualified` regardless of score (but still show the full breakdown)
- Existing customer → Route separately, don't score as new lead
**Verdict thresholds:**
- **Score ≥ 75:** `qualified` — Pursue actively
- **Score 50-74:** `borderline` — Qualified with caveats, may need manual review
- **Score 30-49:** `near_miss` — Not qualified now, but close enough to consider (referral or nurture)
- **Score < 30:** `disqualified` — Does not fit ICP
**Sub-verdicts for routing:**
- `qualified_hot` — Score ≥ 75 AND Tier 1/2 urgency from triage
- `qualified_warm` — Score ≥ 75 AND Tier 3/4 urgency
- `borderline_review` — Score 50-74, needs human judgment call
- `near_miss_referral` — Score 30-49 AND right_company_wrong_person (referral opportunity)
- `near_miss_nurture` — Score 30-49, might fit in the future
- `disqualified_polite` — Score < 30, needs polite decline
- `disqualified_competitor` — Competitor employee
- `existing_customer_upsell` — Existing customer with expansion signal
### 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:**
- `qualification_verdict` — qualified | borderline | near_miss | disqualified
- `qualification_sub_verdict` — qualified_hot | qualified_warm | borderline_review | near_miss_referral | near_miss_nurture | disqualified_polite | disqualified_competitor | existing_customer_upsell
- `composite_score` — 0-100
- `summary` — One sentence qualification reasoning
**Pipeline check columns:**
- `pipeline_status` — new | existing_customer | in_pipeline | previously_contacted
- `pipeline_detail` — One sentence on the overlap
- `signal_flags` — Any signal composite matches
**Company qualification columns:**
- `company_size_score` — match | borderline | mismatch | unknown
- `industry_score` — match | adjacent | mismatch | unknown
- `stage_score` — match | borderline | mismatch | unknown
- `geography_score` — match | borderline | mismatch | unknown
- `use_case_score` — strong | moderate | weak | no_fit | unknown
**Person qualification columns:**
- `title_match_score` — exact_match | close_match | adjacent | mismatch | unknown
- `seniority_score` — match | too_junior | too_senior | unknown
- `authority_type` — economic_buyer | champion | user | evaluator | gatekeeper | unknown
- `mismatch_type` — null | right_company_wrong_person | right_person_wrong_company
**Use case columns:**
- `inferred_intent` — What they seem to need
- `product_fit` — strong | moderate | weak | unknown
- `implementation_feasibility` — easy | moderate | complex | unlikely
### 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:
```markdown
## 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 `insufficient_data`, recommend enrichment or manual review
**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 `right_company_wrong_person` routes this to referral handling in `disqualification-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 `insufficient_data` regardless of score — recommend enrichment first
---
## 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
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!