You will build a complete outbound sales flow with a paced dialer. The dialer does five things:
  1. It walks a list of leads.
  2. It originates each call over a SIP trunk with an AI qualifying agent attached.
  3. It obeys a calls-per-second rate and a max-concurrency ceiling.
  4. It records the outcome of every attempt (answered / no-answer / busy / failed).
  5. When the agent qualifies a lead, it transfers the hot call to a human closer. The sid that keys your recording and analytics stays the same.
Everything below runs on the Voice SDK. The control plane is request/response. The dialer is therefore an originate + outcome-poll loop wrapped in two limiters. The agent drives the conversation server-side. Your process never touches audio.

Architecture at a glance

The dialer holds a concurrency slot for the whole life of a call (dialing → answered → cleared). 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.
Outbound sales dialing is regulated. You are responsible for calling-hours windows, caller-ID accuracy, and consent. You must also scrub every number against your suppression / Do-Not-Call list before the number reaches the dialer. The platform does not do this for you. This recipe assumes that the numbers you enqueue below are already consented and DNC-clean.

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 in originate. 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 plain calls.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.
The dialer originates each lead with the agent attached. It then polls the call with calls.get until the call reaches a terminal state. Only then does the dialer release the slot.
The server clamps ringTimeoutSec to 5..120 (default 30). A short ring window (20–25 s) improves list throughput. It also cuts down on calls that roll to voicemail. See Step 3.

Step 3 — no-answer, busy, and voicemail

Every attempt lands in exactly one terminal CallStatus. 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.
There is no built-in answering-machine detection (AMD). A call that voicemail picks up reports in_progress, the same as a human answer. Handle voicemail in the agent, not the dialer:
  • Give the agent a short idle watchdog. Dead air or a long unbroken greeting then triggers a soft prompt and then a hang-up (see Turn detection).
  • Prompt the agent to recognise a voicemail greeting. The agent then drops the call, or leaves a single scripted message and ends the call.
  • Keep ringTimeoutSec low. Fewer calls then reach voicemail in the first place.

Step 4 — transfer hot leads to a human

When the agent qualifies a lead, hand the live call to a human closer. The sid stays the same across the transfer. The recording, transcript, and analytics therefore stay on one timeline. 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.
This path uses the agent’s tool-calling surface and the ACD picker. Set them up in Tool calling and PBX / ACD routing. AI → human handoff and the Support handoff recipe cover the full warm-handoff mechanics.

Manual: transfer from your own logic

Your process can decide when to escalate (for example, off a scoring webhook). In that case, use call.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.
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.