Lead Qualification Agent
An intelligent voice agent that qualifies inbound leads by asking targeted questions, scoring prospects, and routing qualified leads to your sales team.
Configuration
{
"agent": {
"name": "Lead Qualifier",
"language": "en-US",
"llmProvider": "gemini-2.5",
"llmModel": "gemini-2.5-flash-lite",
"llmTemperature": 0.6,
"sttProvider": "deepgram",
"sttModel": "nova-3",
"ttsProvider": "cartesia",
"ttsVoice": "95856005-0332-41b0-935f-352e296aa0df",
"greetingMessage": "Hi! Thanks for your interest in our product. I'd love to learn more about your needs to see how we can help. Do you have a few minutes?",
"prompt": "...",
"allowInterruptions": true
}
}
System Prompt
You are a lead qualification specialist. Your goal is to understand the prospect's needs, qualify them, and schedule a meeting with sales if they're a good fit.
## Qualification Criteria (BANT)
### Budget
- What budget range are they working with?
- Are they decision-makers on budget?
### Authority
- What is their role?
- Who else is involved in decisions?
### Need
- What problem are they trying to solve?
- How urgent is it?
### Timeline
- When are they looking to implement?
- Is there a specific deadline?
## Qualification Questions
Ask these naturally throughout the conversation:
1. "What brings you to [Company] today?"
2. "Tell me about your current situation with [problem area]"
3. "What would solving this problem mean for your business?"
4. "Who else would be involved in evaluating solutions?"
5. "What's your timeline for making a decision?"
6. "Do you have a budget range in mind?"
## Scoring Guidelines
**Hot Lead (Score 80-100)**
- Clear budget authority
- Decision-maker or strong influencer
- Urgent need (within 30 days)
- Specific pain points that match our solution
**Warm Lead (Score 50-79)**
- Budget being discussed
- Part of buying committee
- Timeline within 90 days
- General interest, exploring options
**Cold Lead (Score 0-49)**
- No budget authority
- Just researching
- No timeline
- Needs we can't address
## Response Style
- Be conversational, not interrogative
- Listen actively and respond to what they say
- Don't rapid-fire questions
- Share relevant info about our solution when appropriate
- Be enthusiastic but not pushy
## Handling Objections
"I'm just looking":
→ "Totally understand! What sparked your interest today?"
"We don't have budget":
→ "Makes sense. When do budget discussions typically happen?"
"I need to talk to my team":
→ "Of course! What would be most helpful for me to share with them?"
## Next Steps
For qualified leads:
→ "Based on what you've shared, I think there's a great fit. Would you be open to a 15-minute call with our solutions team to dive deeper?"
For unqualified leads:
→ "Thanks for sharing! It sounds like timing isn't quite right. Can I send you some resources to review?"
Tools
Save Lead Information
{
"type": "function",
"function": {
"name": "save_lead",
"description": "Save lead information to CRM",
"parameters": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "Lead's full name"
},
"company": {
"type": "string",
"description": "Company name"
},
"role": {
"type": "string",
"description": "Job title/role"
},
"email": {
"type": "string",
"description": "Email address"
},
"phone": {
"type": "string",
"description": "Phone number"
},
"needs": {
"type": "string",
"description": "Summary of their needs"
},
"budget": {
"type": "string",
"description": "Budget range if discussed"
},
"timeline": {
"type": "string",
"description": "Timeline for decision"
},
"score": {
"type": "integer",
"description": "Lead score 0-100"
},
"notes": {
"type": "string",
"description": "Additional notes"
}
},
"required": ["name", "needs", "score"]
}
}
}
Schedule Meeting
{
"type": "function",
"function": {
"name": "schedule_meeting",
"description": "Schedule a meeting with sales team",
"parameters": {
"type": "object",
"properties": {
"lead_id": {
"type": "string"
},
"preferred_times": {
"type": "array",
"items": { "type": "string" },
"description": "Preferred meeting times"
},
"meeting_type": {
"type": "string",
"enum": ["discovery", "demo", "technical_review"]
}
},
"required": ["lead_id", "meeting_type"]
}
}
}
Check Availability
{
"type": "function",
"function": {
"name": "check_availability",
"description": "Check sales team availability",
"parameters": {
"type": "object",
"properties": {
"date_range_start": {
"type": "string",
"description": "Start date (YYYY-MM-DD)"
},
"date_range_end": {
"type": "string",
"description": "End date (YYYY-MM-DD)"
}
}
}
}
}
Tool Handlers
type Lead struct {
ID string
Name string
Company string
Role string
Email string
Phone string
Needs string
Budget string
Timeline string
Score int
Notes string
CreatedAt time.Time
}
func handleSaveLead(params map[string]any) (string, error) {
lead := Lead{
ID: generateID(),
Name: params["name"].(string),
Needs: params["needs"].(string),
Score: int(params["score"].(float64)),
CreatedAt: time.Now(),
}
// Optional fields
if company, ok := params["company"].(string); ok {
lead.Company = company
}
if role, ok := params["role"].(string); ok {
lead.Role = role
}
if email, ok := params["email"].(string); ok {
lead.Email = email
}
if budget, ok := params["budget"].(string); ok {
lead.Budget = budget
}
if timeline, ok := params["timeline"].(string); ok {
lead.Timeline = timeline
}
if notes, ok := params["notes"].(string); ok {
lead.Notes = notes
}
// Save to CRM
err := crm.SaveLead(lead)
if err != nil {
return "", err
}
// If hot lead, notify sales
if lead.Score >= 80 {
notifySalesTeam(lead)
}
return fmt.Sprintf("Lead saved with ID %s. Score: %d", lead.ID, lead.Score), nil
}
func handleScheduleMeeting(params map[string]any) (string, error) {
leadID := params["lead_id"].(string)
meetingType := params["meeting_type"].(string)
// Get available slots
slots, _ := calendar.GetAvailableSlots(3) // Next 3 available
if len(slots) == 0 {
return "I don't have any available slots right now. Can I have someone reach out to you?", nil
}
// Format slots for speech
response := "Great! I have a few times available: "
for i, slot := range slots {
response += formatTimeForSpeech(slot)
if i < len(slots)-1 {
response += ", or "
}
}
response += ". Which works best for you?"
return response, nil
}
Lead Scoring Logic
type LeadScore struct {
Budget int // 0-25
Authority int // 0-25
Need int // 0-25
Timeline int // 0-25
}
func calculateScore(lead *Lead) int {
var score LeadScore
// Budget scoring
switch lead.Budget {
case "over_50k":
score.Budget = 25
case "25k_50k":
score.Budget = 20
case "10k_25k":
score.Budget = 15
case "under_10k":
score.Budget = 10
default:
score.Budget = 5
}
// Authority scoring
switch lead.Role {
case "ceo", "cto", "vp":
score.Authority = 25
case "director":
score.Authority = 20
case "manager":
score.Authority = 15
case "individual":
score.Authority = 10
default:
score.Authority = 5
}
// Need scoring
if lead.Needs != "" && strings.Contains(lead.Needs, "urgent") {
score.Need = 25
} else if lead.Needs != "" {
score.Need = 15
} else {
score.Need = 5
}
// Timeline scoring
switch lead.Timeline {
case "immediate", "this_month":
score.Timeline = 25
case "this_quarter":
score.Timeline = 20
case "this_year":
score.Timeline = 10
default:
score.Timeline = 5
}
return score.Budget + score.Authority + score.Need + score.Timeline
}
Conversation Flow
1. Greeting & Permission
↓
2. Understand Situation (Need)
↓
3. Explore Impact (qualify need)
↓
4. Discuss Decision Process (Authority)
↓
5. Timeline Discussion
↓
6. Budget (if appropriate)
↓
7. Score & Route
├─ Hot (80+) → Schedule meeting
├─ Warm (50-79) → Send resources + follow-up
└─ Cold (<50) → Thank + nurture
Outbound Lead Qualification
For outbound campaigns, use variables to personalize:
curl -X POST https://api.edesy.in/v1/calls \
-d '{
"agent_id": "lead_qualifier",
"to": "+1234567890",
"variables": {
"leadName": "Sarah",
"company": "Acme Corp",
"triggerEvent": "downloaded whitepaper",
"salesRep": "John from Sales"
}
}'
Outbound Greeting:
Hi {{leadName}}! This is Alex from [Company]. I noticed you {{triggerEvent}} recently and wanted to reach out. Do you have a quick minute?
Analytics
type QualificationMetrics struct {
TotalCalls int
QualifiedLeads int
MeetingsScheduled int
AverageCallDuration time.Duration
AverageLeadScore float64
QualificationRate float64
ConversionRate float64
}
func trackQualification(call *Call, lead *Lead) {
metrics.Increment("leads.calls.total")
metrics.Histogram("leads.score", float64(lead.Score))
metrics.Histogram("leads.call_duration", call.Duration.Seconds())
if lead.Score >= 50 {
metrics.Increment("leads.qualified")
}
if call.MeetingScheduled {
metrics.Increment("leads.meetings.scheduled")
}
}
Best Practices
1. Natural Conversation Flow
// Don't rapid-fire questions
badFlow := []string{
"What's your budget?",
"What's your timeline?",
"Who makes decisions?",
}
// Build on their responses
goodFlow := `
Lead: "We're struggling with X"
You: "I hear that a lot. Tell me more about how X is affecting your business..."
Lead: [shares more]
You: "That sounds challenging. What would solving this mean for your team?"
`
2. Handle "Not Interested"
notInterestedResponses := map[string]string{
"timing": "Totally understand. Would it be helpful if I sent some resources for when timing is better?",
"competitor": "Got it! Just curious - what drew you to [competitor]?",
"budget": "Makes sense. Budgets are tight everywhere. When do planning cycles typically happen?",
}
3. Capture Information Naturally
// Extract info from conversation, don't ask directly
func extractLeadInfo(transcript string) *Lead {
// Use LLM to extract structured data from natural conversation
prompt := `Extract lead information from this conversation:
- Name
- Company
- Role
- Budget signals
- Timeline
- Pain points
- Decision process`
return llm.Extract(transcript, prompt)
}
Next Steps
- Outbound Sales - Proactive sales calls
- Customer Support - Support agent
- Function Calling - Tool integration