Agent Playbook, build tutorial

How to Build an AI Agent to Capture and Qualify Every Lead

A walkthrough of how to build an agent that catches every lead the moment it arrives, from a missed call, WhatsApp, a web form, or an ad, acknowledges in seconds, has a real conversation to qualify it, scores it against your rules, and routes a hot lead to a human while it lands. Every code step is shown in three frameworks, Google ADK, the Claude SDK, and LangGraph, so you can build it on the stack your team already knows. The code here is the agent's core logic; standing it up in production also means adding the setup, integrations, and hardening around it.

Why manual lead handling leaks money

You pay to generate leads, then lose them in the gap before anyone replies. A Harvard Business Review audit of 2,241 US companies found the average first response to an online lead was about 42 hours, and the MIT Sloan Lead Response Management study found that contacting a lead within five minutes rather than thirty makes you roughly 21 times more likely to qualify it and 100 times more likely to reach them at all. Interest does not wait. The lead who filled your form is filling a competitor's too.

A plain autoresponder sends one canned message and stops. An agent is different: it acknowledges instantly, then actually talks, understands Hindi or English, figures out what the person needs and how ready they are, and gets the good ones in front of a human while they are still paying attention. The rest it nurtures instead of dropping.

Pick a framework: ADK, Claude SDK, or LangGraph

You do not build the agent loop from scratch. A framework gives you the moving parts: a step is a model plus an instruction plus a few typed tools, and a tool is just a Python function. The code tabs on the build steps below show the same capture, qualify, score, and route flow three ways, so you can choose by what your team already runs.

Google ADK composes the tools under an agent and runs it with a Runner. The Claude SDK drives a tool-use loop directly. LangGraph builds an explicit graph that branches to route or nurture on the score. The deterministic scoring, the schema, the WhatsApp calls, and the CRM integration are the same code in all three.

The architecture

Every lead flows through one loop: captured, acknowledged, qualified, scored, then either routed to a human or sent to nurture. A reply during nurture pulls the lead back to a human, and guardrails for consent, deduplication, and audit wrap the whole thing.

1 to 2. Capture and acknowledge all channels, instant WhatsApp 3. Qualify conversational, fill the slots 4. Enrich and score dedupe, weight, band 5. Hot lead? meets your bar? 6. Route to a human CRM deal, ping rep in seconds Nurture not ready, drip 7. Learn and monitor speed, qualify rate, hot rate Guardrails consent, DND dedupe audit log yes, hot no reply re-engages feedback loop

Before you build: prerequisites

Channels

A WhatsApp Business API number through a BSP, a telephony provider for missed calls (Exotel, Knowlarity, MyOperator), your web forms, and any Meta lead ads.

Systems to connect

A model API key (Gemini, Claude, or both), your CRM (Zoho, HubSpot, Salesforce), the rep notification path (Slack, call, WhatsApp), and a store for conversations and scores.

Governance

Opt-in capture and records, DPDP Act 2023 consent, TRAI DND handling for SMS, your scoring rules, and an audit log.

Setup: install, keys, and WhatsApp

Install the packages and set your Gemini key. That is enough to run the qualify, score, and route agent against a sample message, which is what the runnable file further down does. The WhatsApp Cloud API setup is only needed once you want to receive and reply to real leads.

Shell (install and keys)
# Python 3.10 or newer
python -m venv venv && source venv/bin/activate

# The runnable path below uses Google ADK + Gemini:
pip install google-adk google-genai pydantic requests

# Your Gemini key from aistudio.google.com/apikey:
export GEMINI_API_KEY="your-key-here"

# For live WhatsApp (production only; not needed to run the demo below):
export WHATSAPP_TOKEN="your-meta-access-token"
export PHONE_NUMBER_ID="your-whatsapp-phone-number-id"

For live messaging, the WhatsApp Cloud API client below sends the acknowledgement template and receives replies through the webhook you register in Meta's dashboard.

Python (WhatsApp Cloud API)
import os, requests

# One-time: in Meta's WhatsApp Cloud API dashboard, add a number, get a permanent
# access token and the phone-number id, and register a webhook for inbound messages.
TOKEN = os.environ["WHATSAPP_TOKEN"]
PHONE_ID = os.environ["PHONE_NUMBER_ID"]
GRAPH = f"https://graph.facebook.com/v20.0/{PHONE_ID}/messages"

# Send a pre-approved template (the instant acknowledgement in step 2).
def send_template(to: str, template: str, variables: dict):
    requests.post(GRAPH, headers={"Authorization": f"Bearer {TOKEN}"}, json={
        "messaging_product": "whatsapp", "to": to, "type": "template",
        "template": {"name": template, "language": {"code": "en"},
            "components": [{"type": "body", "parameters":
                [{"type": "text", "text": v} for v in variables.values()]}]},
    }).raise_for_status()

# Inbound replies hit the webhook you registered; point it at an HTTPS route
# (Flask or FastAPI) that parses the payload and calls your handler.

The stack, and why each piece

Layer Pick Why
Capture Telephony webhook (Exotel, Knowlarity, MyOperator), web form, WhatsApp Cloud API, Meta Lead Ads Catch every channel a lead arrives on. In India, missed calls and WhatsApp outpull email, so they cannot be the channel you handle last.
Messaging WhatsApp Cloud API via a BSP (Gupshup, Interakt, AiSensy) WhatsApp is where Indian buyers actually reply. The API lets you acknowledge within seconds and hold a two-way conversation.
Model Gemini 2.5 Flash, Claude, or any model behind LangChain Holds a natural back-and-forth and fills your qualification fields from free text, in Hindi or English. The code tabs show the same call in all three.
Agent framework Google ADK, the Claude SDK, or LangGraph Drives each lead through capture, qualify, score, route, and the nurture branch. Pick the one your team already runs; the logic is identical.
Data + CRM Your CRM API (Zoho, HubSpot, Salesforce) + Postgres Dedupe against existing contacts, create the deal, and store every conversation, score, and decision.
Routing + notify Slack or WhatsApp ping plus a call to the rep, with CRM assignment A hot lead has to reach a human in seconds. The whole advantage is speed, so queuing it defeats the point.

Build it, step by step

The model and orchestration steps show the same logic in Google ADK, the Claude SDK, and LangGraph. Use the tabs to switch. The channel and deterministic steps are plain Python and identical everywhere. These are the building blocks, and they call your own systems (the CRM, the WhatsApp client, the queue) by name; the complete file you can run today, with those stubbed, is in "Put it together" below.

1

Capture every channel into one lead

Leads arrive as missed calls, web forms, WhatsApp messages, and Meta lead ads, and each one is a separate webhook. Point a telephony provider (Exotel, Knowlarity, or MyOperator) at a missed-call webhook, the web form at a form webhook, and the WhatsApp Cloud API and Meta Lead Ads at theirs. Every handler normalises to the same Lead object and drops it on a queue. The phone number in E.164 form is the join key that later stitches the same person together across channels. This is the same in every framework, so there is one version.

Python (webhooks)
from pydantic import BaseModel
from datetime import datetime

class Lead(BaseModel):
    name: str | None = None
    phone: str                     # E.164, the join key across channels
    email: str | None = None
    source: str                    # missed_call | web_form | whatsapp | meta_ad
    message: str | None = None
    created_at: datetime

@app.post("/webhook/missed-call")           # Exotel / Knowlarity / MyOperator
def missed_call(evt: dict):
    lead = Lead(phone=evt["from"], source="missed_call", created_at=now())
    jobs.enqueue("handle_lead", lead.dict())  # return fast; work in the queue
2

Acknowledge in seconds

This is the step that wins the deal. The instant a lead lands, fire a pre-approved WhatsApp template so the person hears back in seconds, not the industry-average 42 hours. Templates are approved by Meta in advance and only go to numbers that have opted in, which keeps the account healthy. Contacting a lead within five minutes rather than thirty makes you about 21 times more likely to qualify it. This call to the WhatsApp Cloud API is the same whichever agent framework you pick.

Python (WhatsApp Cloud API)
# send_template is the WhatsApp Cloud API helper from the Setup section above.
def acknowledge(lead: Lead):
    send_template(
        to=lead.phone,
        template="lead_ack",               # pre-approved by Meta, opt-in only
        variables={"name": lead.name or "there"})
    crm.touch(lead.phone, status="acknowledged")  # crm = your CRM client
3

Qualify conversationally into a strict schema

Now the agent has a short, helpful conversation instead of an interrogation. The model runs the back-and-forth over WhatsApp, asks one question at a time, understands Hindi and English, and maps the free-text answers into a typed Qualification schema. Forcing structured output is what makes the result usable downstream: you get clean fields, not a transcript. The schema is defined once (in the Gemini tab); the Claude and LangGraph tabs show the same call through their own SDK.

from google import genai
from google.genai import types
from pydantic import BaseModel

client = genai.Client()

class Qualification(BaseModel):
    intent: str
    budget: int | None = None          # rupees, if stated
    timeline: str | None = None        # now | this_month | later
    location: str | None = None
    ready_to_talk: bool = False

def qualify(history: list[str]) -> Qualification:
    resp = client.models.generate_content(
        model="gemini-2.5-flash",
        contents=[QUALIFY_PROMPT, *history],   # ask one question at a time
        config=types.GenerateContentConfig(
            response_mime_type="application/json", response_schema=Qualification))
    return resp.parsed
from anthropic import Anthropic

client = Anthropic()
# Qualification is the same Pydantic schema as the Gemini tab.

def qualify(history: list[dict]) -> Qualification:
    msg = client.messages.parse(
        model="claude-opus-4-8", max_tokens=512,
        system=QUALIFY_PROMPT,                 # ask one question at a time
        messages=history,                      # [{role, content}, ...]
        output_config={"format": Qualification})
    return msg.parsed_output
from langchain_google_genai import ChatGoogleGenerativeAI
from langchain_core.messages import SystemMessage, HumanMessage

# Qualification is the same Pydantic schema as the Gemini tab.
qualifier = ChatGoogleGenerativeAI(
    model="gemini-2.5-flash").with_structured_output(Qualification)

def qualify(history: list[str]) -> Qualification:
    msgs = [SystemMessage(QUALIFY_PROMPT)] + [HumanMessage(t) for t in history]
    return qualifier.invoke(msgs)
4

Enrich and dedupe against your CRM

Before scoring, check whether you already know this person. Look them up in the CRM by phone and email, and if they exist, merge rather than create, so one human does not become three records that two reps both call. Where you have an email, a domain lookup can add the company and rough size for B2B routing. This is plain, testable code talking to your CRM API, not a model decision.

Python (CRM API)
def enrich(lead: Lead) -> Lead:
    existing = crm.find_by_phone(lead.phone) or crm.find_by_email(lead.email)
    if existing:
        crm.merge(existing.id, lead)       # one lead, not three across channels
    if lead.email:
        lead = lead.copy(update=domain_lookup(lead.email))  # company, size
    return lead
5

Score with an explicit, owned policy

Turn your definition of a good lead into points you control, not a vibe the model invents. Readiness to talk, budget over your floor, a near-term timeline, and a high-intent channel each add weight, and the total maps to a band: hot, warm, or cold. Keeping the weights in config means sales can retune them without a deploy, and every score is logged with the inputs that produced it.

Python (deterministic)
def score(lead: Lead, q: Qualification) -> tuple[int, str]:
    pts = 0
    pts += 30 if q.ready_to_talk else 0
    pts += 25 if q.budget and q.budget >= policy.min_budget else 0
    pts += 20 if q.timeline in ("now", "this_month") else 0
    pts += 15 if lead.source in ("missed_call", "whatsapp") else 5
    band = "hot" if pts >= 60 else "warm" if pts >= 35 else "cold"
    return pts, band                       # thresholds live in config, not code
6

Route hot leads, nurture the rest

A hot lead is worthless sitting in a queue. The agent assigns it round-robin to an available rep, creates the deal in the CRM with the conversation and score attached, and pings that rep on Slack and by call right away. Warm and cold leads are not binned, they enter a nurture sequence so they are worked later. Both routes are plain functions exposed to the agent as tools.

Python (CRM + notify)
def route_lead(lead: Lead, band: str) -> dict:
    """Hot leads reach a human now; warm/cold leads enter nurture."""
    if band == "hot":
        rep = assign.round_robin(team="sales")
        crm.create_deal(lead, owner=rep, priority="high")
        notify(rep, channel="slack+call",  # ping now, do not queue
               text=f"HOT lead {lead.name} {lead.phone}")
        return {"routed": "rep", "owner": rep}
    nurture.enroll(lead, sequence=band)    # warm/cold drip sequence
    return {"routed": "nurture"}
7

Re-engage on reply, with consent and DND guardrails

Most leads are not ready today, and that is fine. A nurture sequence sends spaced, useful WhatsApp messages, paced and capped so it never feels like spam. The important part is the loop back: when a nurtured lead replies, the WhatsApp Cloud API posts the inbound message to your webhook, you pull them out of the drip and treat them as hot again, because intent just spiked. Around all of this sit the guardrails: WhatsApp and the DPDP Act 2023 both require opt-in, SMS fallback must respect TRAI DND, and every contact is rate-limited and logged.

Python (webhook + guardrails)
# WhatsApp Cloud API posts inbound messages to your webhook.
@app.post("/webhook/whatsapp")
def on_whatsapp(payload: dict):
    msg = parse_whatsapp(payload)              # sender + text
    lead = crm.find_by_phone(msg.sender)
    if nurture.is_enrolled(lead):
        nurture.pause(lead)
        route_lead(lead, band="hot")           # a reply is fresh intent

def may_message(phone: str, channel: str) -> bool:
    if not consent.has_optin(phone):           # WhatsApp + DPDP need opt-in
        consent.request_optin(phone); return False
    if channel == "sms" and dnd.is_registered(phone):
        return False                           # respect TRAI DND
    return not rate_limit.exceeded(phone)      # never spam a contact
8

Assemble and run the agent

Now wire qualify, score, and route into one runnable agent. ADK composes the tools under an LlmAgent and runs it with a Runner, the Claude SDK drives a tool-use loop, and LangGraph builds an explicit graph that branches to route or nurture on the score. The tools and the schema are the same in all three; only the orchestration differs.

from google.adk.agents import LlmAgent
from google.adk.runners import Runner
from google.adk.sessions import InMemorySessionService

lead_agent = LlmAgent(
    name="lead_agent", model="gemini-2.5-flash",
    instruction="Chat briefly to qualify the lead, then call score, then "
                "route_lead for hot leads or enroll_nurture otherwise.",
    tools=[qualify, score, route_lead, nurture.enroll])

runner = Runner(agent=lead_agent, app_name="leads",
                session_service=InMemorySessionService())
from anthropic import Anthropic

client = Anthropic()
TOOLS = [qualify_tool, score_tool, route_tool, nurture_tool]   # JSON tool schemas

def run_lead(messages: list):
    while True:
        resp = client.messages.create(model="claude-opus-4-8", max_tokens=1024,
                                       tools=TOOLS, messages=messages)
        if resp.stop_reason != "tool_use":
            return resp
        messages.append({"role": "assistant", "content": resp.content})
        for b in resp.content:
            if b.type == "tool_use":
                out = dispatch(b.name, b.input)        # your handlers
                messages.append({"role": "user", "content": [
                    {"type": "tool_result", "tool_use_id": b.id, "content": str(out)}]})
from langgraph.graph import StateGraph, START, END

g = StateGraph(dict)
g.add_node("qualify", lambda s: {"q": qualify(s["history"])})
g.add_node("score",   lambda s: {"band": score(s["lead"], s["q"])[1]})
g.add_node("route",   lambda s: route_lead(s["lead"], s["band"]))
g.add_node("nurture", lambda s: {"enrolled": nurture.enroll(s["lead"], s["band"])})
g.add_edge(START, "qualify"); g.add_edge("qualify", "score")
g.add_conditional_edges("score", lambda s: "route" if s["band"] == "hot" else "nurture")
g.add_edge("route", END); g.add_edge("nurture", END)
lead_agent = g.compile()

Put it together: run it end to end

Here is the whole thing as one runnable file. It qualifies a single inbound message, scores it, and routes it, then prints the result. Replace the stubs (the budget floor, assign_rep, notify, and enroll_nurture) with your CRM and notifier. Save it as main.py and run python main.py "Need 50 units this week, budget about 2 lakh".

Python (full runnable file, ADK)
# main.py  --  run:  python main.py "Need 50 units this week, budget about 2 lakh"
# A runnable reference: qualifies one inbound message, scores it, and routes it.
# Replace the stubs (MIN_BUDGET, assign_rep, notify, enroll_nurture) with your CRM.
import sys, asyncio
from pydantic import BaseModel
from google import genai
from google.genai import types
from google.adk.agents import LlmAgent
from google.adk.runners import InMemoryRunner

client = genai.Client()                       # reads GEMINI_API_KEY

class Qualification(BaseModel):
    intent: str
    budget: int | None = None                 # rupees, if stated
    timeline: str | None = None               # now | this_month | later
    ready_to_talk: bool = False

# ---- stubs: replace with your CRM and notifier ----
MIN_BUDGET = 50000
def assign_rep(): return "rep-1"
def notify(rep, text): print(f"[notify {rep}] {text}")
def enroll_nurture(phone, band): print(f"[nurture] {phone} -> {band}")
# ---------------------------------------------------

def qualify(message: str) -> dict:
    """Pull the qualification fields from a lead's message."""
    return client.models.generate_content(
        model="gemini-2.5-flash",
        contents=["Extract the lead's qualification from this message.", message],
        config=types.GenerateContentConfig(
            response_mime_type="application/json", response_schema=Qualification),
    ).parsed.model_dump()

def score_and_route(qualification: dict, phone: str = "+910000000000") -> dict:
    """Score the lead; route a hot one to a human, otherwise nurture it."""
    q = qualification
    pts = (30 if q["ready_to_talk"] else 0) \
        + (25 if q.get("budget") and q["budget"] >= MIN_BUDGET else 0) \
        + (20 if q.get("timeline") in ("now", "this_month") else 0)
    band = "hot" if pts >= 50 else "warm" if pts >= 25 else "cold"
    if band == "hot":
        rep = assign_rep()
        notify(rep, f"HOT lead {phone}: {q['intent']}")
        return {"band": band, "score": pts, "routed_to": rep}
    enroll_nurture(phone, band)
    return {"band": band, "score": pts, "routed_to": "nurture"}

agent = LlmAgent(
    name="lead_agent", model="gemini-2.5-flash",
    instruction=("Call qualify on the user's message, then call score_and_route "
                 "with the result, then report the band and where it was routed."),
    tools=[qualify, score_and_route])

async def main(message: str):
    runner = InMemoryRunner(agent=agent, app_name="leads")
    session = await runner.session_service.create_session(app_name="leads", user_id="demo")
    msg = types.Content(role="user", parts=[types.Part(text=message)])
    async for event in runner.run_async(user_id="demo", session_id=session.id, new_message=msg):
        if event.is_final_response():
            print(event.content.parts[0].text)

if __name__ == "__main__":
    if len(sys.argv) < 2:
        sys.exit('usage: python main.py "<a lead message>"')
    asyncio.run(main(sys.argv[1]))

You should see the agent report the band, the score, and where it went, for example routed to rep-1 for a hot lead or to nurture for a warm or cold one. The per-step tabs above are the same logic split into stages across the three frameworks; one agent with two tools, as here, is the simplest version that runs. A real deployment runs qualification as a multi-turn WhatsApp conversation rather than a single message.

The sales view

The rep sees a ranked inbox: who is hot, the conversation so far, and the qualification already filled in, with one action to take.

Lead inbox NEW (3) Rohan S.HOT, score 82 Meena T.WARM, score 48 Arjun P.COLD, score 20 CONVERSATION Need 50 units this week What is your budget? Around 2 lakh Connecting you to sales QUALIFICATION IntentBulk order BudgetRs 2,00,000 TimelineThis week ChannelMissed call Score 82, route now Call now Assign

Build, buy, or no-code

Approach Best for Effort Cost Control
Custom agent (build in-house) You want your own scoring, tight CRM fit, and control of the data High Build cost + model and WhatsApp usage Full
Conversational platform (Verloop, Yellow.ai, Interakt flows) Standard WhatsApp bots, fast start, less engineering Low Per-conversation or seat subscription Medium
No-code (n8n, Make, Zapier) Low volume, a quick pilot, simple rules Low Cheap, but brittle once logic grows Low

Compliance and sender health (India)

Outbound messaging in India has rules, and ignoring them gets your number banned. Send WhatsApp only to numbers that have opted in, use Meta-approved templates for the first touch, and stay inside the 24-hour session window for free-form replies. For any SMS fallback, respect the TRAI DND registry and the DLT registration your provider enforces.

Under the DPDP Act 2023, the contact details a lead gives you are personal data, so capture consent, store it, and honour a request to stop. Build these in as guardrails the agent checks before every message, keep an audit log of consent and contact, and your sender reputation and your legal position both stay clean.

The numbers

Benchmarks vary by source and should be treated as ranges, not promises. These are from neutral research, not vendor marketing.

Metric Manual Agent
Average first response ~42 hours across B2B (HBR 2011 audit of 2,241 firms) Seconds: an instant WhatsApp acknowledgement on every lead
Odds of qualifying the lead Falls fast with delay Replying in 5 minutes vs 30 makes you ~21x more likely to qualify (MIT Sloan / Lead Response Management)
Leads contacted within 5 minutes Roughly a quarter of companies manage it (industry studies) Every lead, because acknowledgement is automatic
Leads that get no response at all A large share slip through on busy days None: every channel feeds one queue that cannot be missed

Sources: Harvard Business Review, "The Short Life of Online Sales Leads" (2011); MIT Sloan / Lead Response Management study (Oldroyd). Figures are industry ranges.

What makes it fail

  • !Acknowledging slowly, which throws away the entire speed-to-lead advantage.
  • !Messaging without opt-in, which risks a WhatsApp ban and a DPDP breach.
  • !A bot that interrogates instead of helping, so leads drop mid-chat.
  • !No dedupe, so one lead becomes three and two reps collide on it.
  • !Hot leads queued instead of pinging a human in seconds.

A safe rollout

  1. Pilot on one channel with instant WhatsApp acknowledgement only; a human still qualifies.
  2. Turn on conversational qualification for that channel and route hot leads to a human instantly.
  3. Add scoring and auto-routing, then widen to every channel on one engine.
  4. Add nurture for not-ready leads, plus monitoring and alerts on speed and quality.

FAQs

General FAQs

Everything you need to know about the service and how it works. Can’t find an answer? Mail us at info@galific.com

  • Why does responding in seconds matter so much?
    Because lead interest decays fast. An audit of 2,241 companies in Harvard Business Review found the average first response was about 42 hours, and the MIT Sloan Lead Response Management study found that contacting a lead within five minutes rather than thirty makes you roughly 21 times more likely to qualify it and 100 times more likely to reach them at all. An agent that acknowledges every lead in seconds captures that window automatically, which a human team cannot do around the clock.
  • Which framework should I use: ADK, the Claude SDK, or LangGraph?
    The right framework depends on your team, and the agent logic is the same in all three. The code tabs let you read the identical capture, qualify, score, and route flow in Google ADK, the Anthropic SDK, and LangGraph and pick the one you already know. ADK and LangGraph are model-agnostic, so you can run Gemini or Claude under either.
  • Will sending WhatsApp messages get my business number banned?
    Not if it is built correctly. The agent uses the official WhatsApp Cloud API, sends only Meta-approved templates, messages only opted-in numbers, and rate-limits per contact. Bans happen when businesses blast unsolicited messages; respecting opt-in and the 24-hour session rules keeps the account healthy, and we treat consent as a DPDP Act 2023 requirement.
  • Can it handle Hindi and regional languages?
    Yes. The qualification step uses a model that understands and replies in Hindi, English, and common mixes, and can be extended to other Indian languages. Buyers answer in whatever they are comfortable with, and the agent still extracts clean, structured fields from it, which a rigid keypad or form flow cannot do.
  • How does missed-call capture actually work?
    Indian telephony providers such as Exotel, Knowlarity, and MyOperator call a webhook the instant a call to your number is missed or completed. The agent receives the caller number, creates a lead, and fires a WhatsApp acknowledgement within seconds, so a missed call becomes a started conversation instead of a lost one. You keep your existing number through the provider.
  • Does it integrate with my CRM?
    Yes, through the CRM's API. The agent dedupes against existing contacts, creates or updates the deal, assigns an owner, and attaches the full conversation and score, for Zoho, HubSpot, Salesforce, and similar systems. Where a system has no clean API, it can write through a connector or a validated import instead of re-keying.
  • How does lead scoring work, and can I set my own rules?
    Scoring is explicit and yours. Readiness, budget, timeline, and channel each carry a weight you set, the total maps to a hot, warm, or cold band, and the thresholds live in config so your sales team can retune them without a developer. Every score is logged with the inputs that produced it, so routing is auditable rather than a black box.
  • What happens to leads that are not ready to buy?
    They are not discarded. Warm and cold leads enter a nurture sequence that sends spaced, useful messages over time, capped so it never feels like spam. The instant a nurtured lead replies, the agent pulls them out of the drip and routes them to a human as hot, because a reply is a fresh spike in intent that a one-shot autoresponder would miss.
  • How do you stop it from spamming people?
    Several layers. It messages only opted-in numbers, respects TRAI DND for any SMS fallback, rate-limits per contact, paces nurture sequences, and stops the moment someone asks to stop. This is both good practice and a compliance requirement under the DPDP Act 2023, and it is what keeps your WhatsApp sender reputation intact.
  • When does the bot hand over to a human?
    Whenever it should. Hot leads go to a rep immediately, and at any point the lead can ask for a person, or the agent can escalate if it is unsure or the conversation goes off-script. The goal is not to replace the rep but to make sure a human only ever speaks to leads that are warmed up and qualified, with the context already gathered.
  • Should I build a custom agent or buy an off-the-shelf chatbot platform?
    Buy a platform if your flows are standard and you want speed with little engineering. Build, or have it built, when you need your own scoring, tight CRM and telephony integration, and control of the data and the conversation logic. See the comparison table above; the right answer depends on volume and how custom your qualification really is.
  • What does it cost to run, and how fast is payback?
    Running cost is mostly per-conversation WhatsApp charges plus model usage and hosting, which is small next to the value of leads that used to go cold. Because the gain is conversion of leads you are already paying to generate, payback usually shows within the first full month once one channel is live and acknowledgement is instant.

Want this built and running in your stack?

Galific designs, builds, and runs lead agents like this on ADK, the Claude SDK, or LangGraph, wired into your CRM, your telephony, and WhatsApp. Or explore the ready-made versions, from Missed Call Lead Capture to the WhatsApp Qualification Bot, in our agent suite.