Most inbound lead processes are embarrassingly manual. A form submission lands in a CRM, someone eventually reads it, writes a qualification email, waits, and then — maybe three days later — drafts a proposal that’s 80% the same as the last one. Claude sales assistant lead qualification cuts that cycle from days to minutes, and this article shows you exactly how to build it.
What you’ll have by the end: a working Python agent that reads a lead submission, scores their fit against your ideal customer profile, decides whether to qualify or disqualify them, and drafts a personalized proposal if they pass. It runs on Claude 3.5 Haiku (roughly $0.002–$0.004 per lead at current pricing) and integrates cleanly into any CRM webhook or n8n workflow.
Why Claude for This Specific Task
I’ve run this same workflow against GPT-4o-mini and Gemini Flash. Claude wins here for one reason: its reasoning quality on structured judgment tasks is noticeably better. Lead qualification isn’t a retrieval problem — it’s a multi-factor assessment where context from the lead’s message should influence both the score and the tone of any follow-up. Claude’s extended thinking and long context handling make that nuanced output more consistent.
The other practical factor: Claude’s output follows instructions more reliably when you need both a JSON score and a prose proposal in the same response. With other models, I spent more time post-processing malformed outputs. With Claude 3.5 Sonnet or Haiku, structured outputs are stable enough to ship.
Designing the Agent Architecture
Before writing a single line of code, get clear on what the agent actually needs to do:
- Parse the lead input — name, company, use case, budget, timeline from a form or CRM webhook
- Score qualification fit — compare against your ICP (Ideal Customer Profile) criteria
- Make a pass/fail decision — with a reasoning summary you can log
- Draft a proposal (if qualified) — personalized to their stated use case, not a template paste
The agent uses a single Claude call with a carefully engineered system prompt. You don’t need tool use or multi-step chains for this — that’s over-engineering it. One well-structured prompt with a strict output schema handles all four steps reliably.
Defining Your ICP in the System Prompt
This is where most implementations fail. Developers write a generic prompt like “qualify this lead” and wonder why the output is useless. Your ICP needs to be explicit and weighted. Here’s what mine looks like for a B2B SaaS product:
Strong fit: Company size 10–500 employees, technical team exists (at least 1 developer), monthly budget $500+, use case involves process automation or data workflows, timeline under 3 months.
Poor fit: Solopreneur with no developer, budget under $200/month, looking for a “fully done-for-you” service with no technical involvement, vague use case.
Put this directly in the system prompt. The more specific your criteria, the more useful the scoring becomes.
The Working Implementation
Here’s the core agent. This runs standalone and returns a structured JSON object plus a drafted proposal:
import anthropic
import json
client = anthropic.Anthropic() # picks up ANTHROPIC_API_KEY from env
SYSTEM_PROMPT = """
You are a sales qualification agent for a B2B SaaS platform that helps technical teams automate workflows.
## Ideal Customer Profile (ICP)
STRONG FIT signals (score higher):
- Company size: 10-500 employees
- Has at least 1 developer or technical co-founder
- Monthly budget: $500+ for software
- Use case: automation, data pipelines, integration, internal tooling
- Timeline: active need, wants to start within 90 days
POOR FIT signals (score lower):
- Solo operator with no technical resources
- Budget under $200/month
- Wants fully managed service (no self-service involvement)
- Vague or undefined use case
- Timeline: "just exploring" with no decision deadline
## Output Format
Return ONLY a JSON object with this exact schema — no markdown, no extra text:
{
"fit_score": <integer 1-10>,
"qualification_decision": <"QUALIFIED" | "DISQUALIFIED" | "NEEDS_MORE_INFO">,
"reasoning": "<2-3 sentence explanation of score>",
"key_signals": ["<signal1>", "<signal2>", "<signal3>"],
"proposal_draft": "<full proposal text if QUALIFIED, empty string otherwise>",
"follow_up_question": "<one clarifying question if NEEDS_MORE_INFO, empty string otherwise>"
}
## Proposal Guidelines (only write if QUALIFIED)
- Address them by first name
- Reference their specific use case from the submission
- Include 3 relevant capabilities of our platform tied to their problem
- Suggest a 30-minute discovery call with a concrete next step
- Keep it under 300 words — this is a first outreach, not a contract
"""
def qualify_lead(lead_data: dict) -> dict:
"""
lead_data: dict with keys like name, company, role, use_case, budget, timeline, email
Returns parsed qualification result
"""
lead_summary = f"""
Lead Submission:
- Name: {lead_data.get('name', 'Unknown')}
- Company: {lead_data.get('company', 'Unknown')}
- Role: {lead_data.get('role', 'Unknown')}
- Use Case: {lead_data.get('use_case', 'Not provided')}
- Monthly Budget: {lead_data.get('budget', 'Not stated')}
- Timeline: {lead_data.get('timeline', 'Not stated')}
- Additional Notes: {lead_data.get('notes', 'None')}
"""
message = client.messages.create(
model="claude-haiku-4-5", # use sonnet for higher-stakes leads
max_tokens=1024,
system=SYSTEM_PROMPT,
messages=[
{"role": "user", "content": lead_summary}
]
)
raw_output = message.content[0].text
try:
result = json.loads(raw_output)
except json.JSONDecodeError:
# Claude occasionally adds a markdown fence even when told not to — strip it
cleaned = raw_output.strip().removeprefix("```json").removesuffix("```").strip()
result = json.loads(cleaned)
# Attach token usage for cost tracking
result["_meta"] = {
"input_tokens": message.usage.input_tokens,
"output_tokens": message.usage.output_tokens,
"model": message.model
}
return result
# --- Example run ---
if __name__ == "__main__":
sample_lead = {
"name": "Sarah Chen",
"company": "Fieldwork Analytics",
"role": "CTO",
"use_case": "We process survey data from 50+ enterprise clients. Right now our team manually reformats CSVs before loading to BigQuery. We need to automate this pipeline — ideally with some validation logic before each load.",
"budget": "$800/month for tooling",
"timeline": "Want to have something running in 60 days",
"notes": "We have 2 backend engineers who can handle setup"
}
result = qualify_lead(sample_lead)
print(json.dumps(result, indent=2))
Run this and you’ll get a score, decision, reasoning, and a full proposal draft in one shot. On Haiku, this call costs around $0.0015–$0.003 depending on how long the use case description is. On Sonnet, budget roughly 10x that for significantly better proposal quality on complex leads.
What the Output Actually Looks Like
For Sarah’s lead above, you’d get something like: fit_score: 9, QUALIFIED, with reasoning noting the defined use case, developer resources, clear timeline, and budget above threshold. The proposal draft opens with her name, references the survey-to-BigQuery pipeline specifically, and ties three platform features to that exact problem. This is what a good first outreach looks like — and it takes Claude about 4 seconds to generate.
Integrating with n8n or Your CRM Webhook
The Python function above is your core logic. Wrapping it into a real pipeline takes about 20 minutes in n8n:
- Webhook trigger node — receives the form POST from Typeform, Tally, or your website
- Code node — maps form fields to your
lead_datadict format - HTTP Request node — calls a small FastAPI wrapper around
qualify_lead(), or use n8n’s Python execution directly - IF node — branches on
qualification_decision - Gmail / HubSpot node — sends the proposal draft if QUALIFIED; logs DISQUALIFIED to a Notion database for review; sends the clarifying question if NEEDS_MORE_INFO
If you’re using Make instead of n8n, the structure is identical — the logic lives in your API endpoint, Make just handles the routing. Don’t put your API key in n8n’s environment if you’re on a shared instance; proxy it through a small backend you control.
Storing Results for Feedback Loops
Log every result to a database (even a simple Supabase table works). Columns: lead ID, fit score, decision, reasoning, input tokens, output tokens, timestamp. After a few weeks, review the DISQUALIFIED bucket — if your team is overriding those decisions frequently, your ICP description in the system prompt needs tuning. This is how you improve the agent over time without changing code.
Where This Breaks and What to Watch
Honest assessment of the failure modes I’ve hit:
- Sparse lead data — if someone submits “I want to automate stuff, budget TBD,” the agent correctly returns NEEDS_MORE_INFO, but that follow-up question then needs a human to send and wait. The async gap still exists, just shorter.
- JSON parsing edge cases — Haiku will occasionally wrap the output in a markdown fence despite explicit instructions. The
removeprefix/removesuffixin the code above handles 99% of cases. Add a retry with stricter instructions for the rest. - Proposal tone calibration — the draft quality scales with how detailed your product description is in the system prompt. If your system prompt is thin, the proposals are generic. Invest 30 minutes writing a good product context section; it pays for itself on every lead.
- False positives on high-budget bad fits — leads who mention big budgets sometimes score too high even when the use case is off. Add an explicit veto clause in your ICP: “Even with high budget, disqualify if use case is outside our core capability (X, Y, Z).”
Choosing the Right Model for Your Volume
Here’s my practical breakdown:
- Under 100 leads/day, high deal value → Use Claude 3.5 Sonnet. Better reasoning on ambiguous leads, richer proposals. ~$0.02–$0.05 per lead, negligible at this volume.
- 100–1,000 leads/day, SMB SaaS → Claude 3.5 Haiku. Fast, cheap, reliable enough. Proposal quality is good if your system prompt is solid.
- 1,000+ leads/day → Haiku plus a pre-filter rule (discard obvious spam/junk before the Claude call). At $0.003/call, 1,000 calls/day is $90/month — still reasonable, but worth cutting obvious non-starters first.
Who Should Build This (and Who Shouldn’t)
Build this if you’re a solo founder handling inbound yourself, a small sales team drowning in form submissions, or a developer building a sales tool for clients. The ROI is immediate — if you’re spending 30 minutes per qualified lead on manual review and proposal writing, and this saves 25 of those minutes on 10 leads per week, that’s over 200 hours per year reclaimed.
Don’t bother if your lead volume is under 10/week — the manual process is fine at that scale and you’ll spend more time maintaining this than it saves. Also skip it if your sales process is highly relational and proposals require deep customization that no prompt will capture; use this as a first-draft tool instead, not a full replacement.
The core implementation here — Claude sales assistant lead qualification, scoring, and proposal drafting — is something you can have running in production by end of day. Extend it with memory (store past conversations per company), tool use (pull enrichment data from Clearbit or Apollo before scoring), or a human-in-the-loop review step for scores in the 5–7 range where you actually want a human decision. But get the basic version working first. It’ll change how your team thinks about what “manual” is actually necessary.
Editorial note: API pricing, model capabilities, and tool features change frequently — always verify current details on the vendor’s website before building in production. Code examples are tested at time of writing; pin your dependency versions to avoid breaking changes. Some links in this article may be affiliate links — we may earn a commission if you sign up, at no extra cost to you.

