The Problem Nobody Was Solving
Most businesses send emails and SMS to leads. Nobody picks up. Response rates are under 3%. But when a human calls? Conversion jumps to 30%+.
The problem: hiring a call center for 1000+ leads daily is expensive and doesn't scale.
That's exactly the gap I was asked to fill. Build an AI-powered voice campaign platform that could call leads automatically, have real conversations, and report back in real-time — all without a single human agent.
What I Built
The platform — called Calling Agent — is an enterprise SaaS where businesses:
- Upload bulk leads (CSVs with 1000+ contacts)
- Configure an AI voice agent with a custom script
- Launch campaigns and watch calls happen live
Here's the full architecture I designed:
Lead Upload → Redis Job Queue → AI Agent Engine → Twilio Voice API
↓
WebSocket Real-Time Dashboard
↓
Analytics: Call Duration, Outcome, Lead Score
The Agentic AI Core
The most interesting part was building the AI agent itself — not just a script reader, but a genuinely conversational agent that could:
- Understand context — If a lead says "Call me tomorrow," the agent reschedules instead of continuing
- Handle objections — Using prompt chaining with GPT-4 for dynamic responses
- Qualify leads — Score them 1–10 based on conversation signals
- Transfer to human — If the lead is hot, seamlessly transfer the call
The agent used a multi-step prompt orchestration pattern:
const agentResponse = await openai.chat.completions.create({
model: "gpt-4-turbo",
messages: [
{ role: "system", content: systemPrompt }, // Persona + rules
{ role: "system", content: contextPrompt }, // Lead info + history
...conversationHistory, // Full call transcript
{ role: "user", content: currentTranscript }, // Latest speech-to-text
],
temperature: 0.3, // Lower = more consistent agent behavior
});
Real-Time Monitoring with WebSockets
Clients needed to watch campaigns live. I built a WebSocket pipeline that pushed events every 2 seconds:
// Server: emit call status updates
io.to(campaignRoom).emit("call:update", {
leadId,
status: "in-progress",
duration: callDuration,
sentiment: analyzeSentiment(transcript),
leadScore: calculateScore(transcript),
});
The React dashboard consumed these events and updated charts, maps, and tables without any page refresh.
The Hardest Engineering Challenge
Queue management. When 1000 leads are uploaded and campaigns start, you can't fire 1000 API calls simultaneously. Twilio rate-limits you. OpenAI rate-limits you.
Solution: Redis Bull queue with concurrency control:
const campaignQueue = new Bull("voice-campaigns", { redis: redisConfig });
campaignQueue.process(10, async (job) => {
// Max 10 concurrent calls
const { leadId, campaignId } = job.data;
await initiateAICall(leadId, campaignId);
});
This handled bursts gracefully and gave us retry logic for failed calls automatically.
Results
- 1000+ leads handled per campaign launch
- ~35% answer rate on AI calls (industry avg: 20%)
- Real-time dashboards with sub-2-second latency
- Multi-tenant — 10+ business clients running simultaneous campaigns
What I'd Do Differently
Looking back, I'd add streaming responses from the LLM instead of waiting for a full completion — it would cut perceived latency by 400ms+ per turn and make conversations feel more natural.
Tech Stack
Next.js · Node.js · TypeScript · Redis (Bull) · Socket.io · Twilio · OpenAI GPT-4 · MongoDB · Tailwind CSS
Connect with me: buildbysandeep.dev | LinkedIn