- It walks a list of leads.
- It originates each call over a SIP trunk with an AI qualifying agent attached.
- It obeys a calls-per-second rate and a max-concurrency ceiling.
- It records the outcome of every attempt (answered / no-answer / busy / failed).
- When the agent qualifies a lead, it transfers the hot call to a human
closer. The
sidthat keys your recording and analytics stays the same.
originate +
outcome-poll loop wrapped in two limiters. The agent drives the conversation
server-side. Your process never touches audio.
Architecture at a glance
max_concurrent therefore means simultaneous live
calls, exactly like a predictive dialer. Pacing and the AI leg are
independent. You configure the agent, its providers, and its transfer tool
server-side (see Runtime overview). The
dialer decides only who to call and how fast.
Step 1 — the qualifying agent
The dialer attaches an agent by id. You configure the agent’s brain (speech-to-speech or cascaded ASR→LLM→TTS), its system prompt, and its tools once, in the console or via the control plane. You do not configure them inline inoriginate. Give the agent a telephony transfer tool. The agent can
then route a qualified lead to your human closer queue itself. This is the
recommended “hot lead” path. See
Step 4.
You attach an agent by id.
originate({ agent: "outbound-sales" }) and
agents.attach(sid, "outbound-sales") both take a string. Provider keys,
prompt, turn-detection, and the transfer tool live in the agent’s server-side
config. Build the agent first: BYO speech-to-speech,
tool calling.Step 2 — the paced dialer
The dialer combines two limiters over the plaincalls.originate call:
- a calls-per-second gate, so that you never originate faster than the trunk / carrier allows, and
- a max-concurrency semaphore, so that no more than N calls are live at once.
calls.get until the call reaches a terminal state. Only then does the
dialer release the slot.
- TypeScript
- Python
Step 3 — no-answer, busy, and voicemail
Every attempt lands in exactly one terminalCallStatus. The dialer uses that
status to classify the outcome:
Poll
calls.get as the dialer does above. If you prefer not to poll, consume
call-lifecycle events
(initiated → ring → established/cleared, each with a Q.850 cause). Then drive
the same state machine off the event stream.
Step 4 — transfer hot leads to a human
When the agent qualifies a lead, hand the live call to a human closer. Thesid stays the same across the transfer. The recording, transcript, and
analytics therefore stay on one timeline.
Recommended: let the agent route to a closer queue
The cleanest path is a warm handoff driven by the conversation. Configure the agent with a telephony transfer tool. When the agent decides that the lead is hot, it routes the qualified call into a human skill queue on the ACD. Your dialer process stays out of the audio path entirely.Manual: transfer from your own logic
Your process can decide when to escalate (for example, off a scoring webhook). In that case, usecall.transfer directly. Pass a PSTN number to
blind-transfer the audio to a closer’s phone (a SIP REFER on the wire). Or
pass an agent id to re-attach the call to a different AI agent in place.
- TypeScript
- Python
transfer needs exactly one of to (PSTN, via REFER) or agent
(re-attach). A blind PSTN transfer forwards the audio but carries no lead
context. For a true warm handoff with a screen-pop, prefer the
route-to-skill path above.Scaling to very large lists
The dialer above paces in your process. That is ideal when each lead needs custom logic (CRM lookups, per-lead scripts, retry policy). For fire-and-forget lists in the tens of thousands, the platform also ships a server-side campaign dialer. You hand the control plane the whole number list plus a template (trunk, agent, caller-id, CPS, max-concurrent). The engine then owns the pacing. There is no long-running loop on your side. Reach the campaign dialer from the console’s campaign screen or the control-plane API. Use the SDK pacer in this recipe when you need programmatic control. Use the built-in campaign dialer when you need only throughput.Next steps
Outbound calls
The
originate reference: every param, agent attach, and outcome polling.AI → human handoff
How the warm transfer to a human closer works end to end.
Tool calling
Give the agent a route-to-skill tool. The agent then escalates hot leads
itself.
Turn detection
Idle watchdog and barge-in: your voicemail and dead-air backstop.
Call lifecycle
Consume events instead of polls to drive the outcome state machine.
Support handoff recipe
The sibling recipe: routing a qualified call into a human queue.

