By Sagar Shankaran, Founder of CallSphere
Run 1,000+ outbound voice calls with one API call. ElevenLabs batch calling, dynamic variables per recipient, and a follow-up loop that pushes outcomes to your CRM.
Key takeaways
TL;DR — ElevenLabs Batch Calling fans out one agent across thousands of phone numbers in parallel, each with per-recipient variables (name, balance, appointment time). Pair it with a webhook that ingests outcomes into your CRM and you have a full outbound campaign loop in <100 lines.
A Node.js script that pulls 1,000 leads from Postgres, fires a single batch-calling request to ElevenLabs, and ingests post-call webhooks (call_completed, call_failed, transcript) into a campaign_event table. Each call uses dynamic variables so the agent says "Hi {{first_name}}, this is about your {{topic}}".
ELEVENLABS_API_KEY, AGENT_ID, AGENT_PHONE_NUMBER_ID.flowchart LR
DB[(leads)] --> J[Job script]
J -->|POST /batch-calling/submit| EL[ElevenLabs]
EL -->|outbound calls| LEADS[Recipients]
EL -->|webhook| WH[/api/webhook]
WH --> EVT[(campaign_event)]
```ts import { PrismaClient } from "@prisma/client"; const db = new PrismaClient();
const leads = await db.lead.findMany({ where: { status: "qualified", optedOut: false, lastContactedAt: { lt: new Date(Date.now() - 7 * 86400_000) }, }, select: { id: true, phone: true, firstName: true, topic: true }, take: 1000, }); ```
ElevenLabs accepts a list of recipients with per-recipient variables:
```ts const recipients = leads.map(l => ({ phone_number: l.phone, conversation_initiation_client_data: { dynamic_variables: { first_name: l.firstName, topic: l.topic, lead_id: l.id, }, }, }));
Hear it before you finish reading
Talk to a live CallSphere AI voice agent in your browser — 60 seconds, no signup.
const r = await fetch("https://api.elevenlabs.io/v1/convai/batch-calling/submit", { method: "POST", headers: { "xi-api-key": process.env.ELEVENLABS_API_KEY!, "content-type": "application/json", }, body: JSON.stringify({ call_name: `renewal-${new Date().toISOString().slice(0,10)}`, agent_id: process.env.AGENT_ID!, agent_phone_number_id: process.env.AGENT_PHONE_NUMBER_ID!, scheduled_time_unix: Math.floor(Date.now() / 1000), recipients, }), }); const { batch_id } = await r.json(); console.log("submitted batch:", batch_id); ```
In the ElevenLabs agent system prompt:
```text You are calling {{first_name}} about their {{topic}}. Open with: "Hi {{first_name}}, this is CallSphere following up on your {{topic}} — do you have a moment?" If they say no, schedule a callback. If they say yes, run the renewal flow. ```
Configure the webhook URL in your ElevenLabs agent's "post-call webhooks" setting. Then:
```ts import express from "express"; const app = express(); app.use(express.json());
app.post("/webhook/elevenlabs", async (req, res) => { const { event_type, conversation_id, data } = req.body; if (event_type === "post_call_transcription") { await db.campaignEvent.create({ data: { conversationId: conversation_id, leadId: data.metadata?.dynamic_variables?.lead_id, outcome: data.analysis?.call_successful ? "success" : "failed", transcript: data.transcript, durationSec: data.metadata?.call_duration_secs, rawData: data, }, }); if (data.analysis?.call_successful) { await db.lead.update({ where: { id: data.metadata.dynamic_variables.lead_id }, data: { status: "contacted", lastContactedAt: new Date() }, }); } } res.sendStatus(200); }); app.listen(8080); ```
ElevenLabs signs webhooks with HMAC. Verify before trusting the body:
Still reading? Stop comparing — try CallSphere live.
CallSphere ships complete AI voice agents per industry — 14 tools for healthcare, 10 agents for real estate, 4 specialists for salons. See how it actually handles a call before you book a demo.
```ts import crypto from "crypto"; function verify(sig: string, body: string, secret: string) { const h = crypto.createHmac("sha256", secret).update(body).digest("hex"); return crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(h)); } ```
Batch calling has built-in pacing, but for very large lists split into batches of 5,000 and stagger by hour. Retry failed numbers (busy/no-answer) once after 24 hours; never more than 3 attempts per FCC guidance.
lead_id in dynamic variables: webhooks land orphaned.Retry-After.CallSphere's outbound is multi-provider (ElevenLabs + OpenAI Realtime via Twilio) routed by vertical. Salon nudges (rebook reminders) use ElevenLabs batch calling with GB-YYYYMMDD-### booking refs threaded through dynamic variables. Healthcare uses outbound only for confirmed-consent appointment reminders, never cold. Pricing $149/$499/$1499; demo.
TCPA risk? Real. Scrub DNC, honor opt-outs in <24h, log consent timestamps.
Throughput? ElevenLabs handles thousands per batch; Twilio is the bottleneck (typically 1 call/sec/number — buy more numbers for parallelism).
Cost per call? Roughly $0.05–$0.15/min ElevenLabs + Twilio carrier fees.
Can I A/B test prompts? Yes — run two batches with different agent IDs and compare call_successful rates.
Written by
Sagar Shankaran· Founder, CallSphere
Sagar Shankaran is the founder of CallSphere, where he builds production AI voice and chat agents deployed across healthcare, hospitality, real estate, and home services. He writes about agentic AI, LLM engineering, and shipping voice agents that handle real calls in production.
See how AI voice agents work for your industry. Live demo available -- no signup required.
A VoIP telephone number is a phone number that routes calls over the internet instead of copper lines. Learn what a VoIP number is, how to get one, what it costs, and how to pair it with an AI voice agent in 2026.
How we built a fault-tolerant HVAC emergency triage and tech-dispatch platform on Kubernetes — three-tier CQRS, 11 micro-agents on the OpenAI Agents SDK + LangGraph, NATS JetStream, DTMF/SMS/WebSocket acceptance, circuit breakers, and an evaluation pipeline that catches regressions before they wake a tech at 3 AM.
Haystack 2.7's Agent component plus an Ollama-served Llama 3.2 gives you tool-calling RAG with citations. Here's a complete pipeline against your own document store.
Run STT, LLM, and TTS entirely on Cloudflare's edge — no OpenAI, no ElevenLabs. Real working code with Whisper, Llama 3.3 70B, and Deepgram Aura.
11x.ai and Artisan promised to replace BDRs entirely. By 2026 most adopters reverted to hybrid models. Here is the outbound chat pattern that actually works.
Version your prompts in git, run a 50-case eval suite on every PR, block merges below threshold, and ship a new agent prompt with confidence — full GitHub Actions tutorial.
© 2026 CallSphere LLC. All rights reserved.
Watch how CallSphere handles real customer calls, schedules appointments, and processes payments — live.
Try Live DemoBook a DemoCalculate Your ROI