This is the guide that ties the whole voice stack together. By the end you’ll have a production voice agent that dials out (or answers), reasons over the caller with a model, calls your tools mid-conversation, hands off to a human when it needs to, and records both legs for review. Every other page in this section goes deep on one piece — turn detection, tool calling, handoffs. This page walks the assembly in order, links out at each step, and shows the one control-plane call that starts it all.
You’ll use one identifier throughout: the sid (call_sid). It’s minted once — at the inbound INVITE or when you call originate() — and it addresses everything after: audio, transfer, hangup, recording, telemetry. You never mint a second one.

What you’ll assemble

A call

Originate outbound over a SIP trunk, or answer an inbound one routed by your dialplan.

An agent

A server-side runtime that transcribes, reasons, speaks, and stops when interrupted — attached to the call by id.

A safety net

Tools, a warm human handoff, and both-leg recording — the things a production agent can’t ship without.

Before you start

1

Provision a workspace

Managed cloud or self-hosted — either gives you a tenant with an API key and a per-tenant SIP subdomain. See managed cloud or on-prem.
2

Configure a SIP trunk

A trunk carries calls to and from a carrier. Note its trunkId. See SIP trunking.
3

Define an agent

An agent is a runtime config — a pipeline plus turn detection and tools. Author it in the agent console or by config; note its agent id. The shape is covered in runtime overview.
4

Initialize the Voice client

import { Voice } from "@telequick/sdk/voice";

const v = new Voice({
  baseUrl: "https://engine.telequick.dev",
  apiKey:  process.env.TELEQUICK_CREDENTIALS!,
  orgId:   "org_abc",
});
import os
from telequick.voice import Voice

v = Voice(
    base_url="https://engine.telequick.dev",
    api_key=os.environ["TELEQUICK_CREDENTIALS"],
    org_id="org_abc",
)

Step 1 — Get a call

You either start a call (outbound) or answer one that arrives (inbound). Both end with a live sid you can attach an agent to.
One control-plane call. Pass the agent id and the engine attaches the agent automatically the moment the callee answers — you can skip Step 2 entirely.
const call = await v.calls.originate({
  to:      "+15551234567",   // E.164 destination
  from:    "+15558675309",   // caller-id you're allowed to present
  trunkId: "trunk_main",
  agent:   "agent_sales",    // attach on answer
});

console.log(call.sid, call.status); // "call_9f2c…" "dialing"
call = v.calls.originate(
    to="+15551234567",
    from_="+15558675309",
    trunk_id="trunk_main",
    agent="agent_sales",
)
print(call.sid, call.status)  # "call_9f2c…" "dialing"
originate is control-plane: it returns at dialing, not when the callee answers. Read the outcome with calls.get(sid) or by consuming call-lifecycle events. Full walkthrough in outbound calls.

Step 2 — Attach the agent

If you didn’t pass agent to originate (or you’re answering inbound from code and want to bind an agent yourself), attach one to the live sid. The engine wires the audio bridge end to end — caller audio into the agent, agent audio back to the caller — so you don’t open a bridge yourself.
await v.agents.attach(call.sid, "agent_support");
v.agents.attach(call.sid, "agent_support")
Attaching binds a server-side agent that runs inside the voice gateway, on the core that owns the call’s media — turn detection and barge-in are wired straight into the media path. If instead you want to run your own runtime and drive raw audio frames yourself, open an AudioBridge and feed it, or bring an external runtime via keep your existing runtime.

Step 3 — Configure turn detection

Turn detection is what makes the call feel human: the agent stops the instant the caller talks (barge-in) and replies the instant they finish. It lives in the agent config, not in your call code — set it once per agent. Pick a mode. Cascaded pipelines use on-device VAD; realtime models let the provider own turns.
# agent config — turn detection block
entry_node: ASR
turn_detection:
  type: silero            # on-device VAD for cascaded pipelines (default)
  silence_threshold_ms: 500   # trailing silence before end-of-utterance fires
  min_speech_ms: 300          # discard clicks / line noise shorter than this
  prefix_padding_ms: 200      # audio kept before the speech-start marker
nodes:
  ASR: { provider: deepgram }
  LLM: { provider: anthropic }
  TTS: { provider: elevenlabs }
type
"silero" | "server_vad"
default:"\"silero\""
silero runs on-device VAD in the gateway — the default for cascaded ASR → LLM → TTS. server_vad lets a realtime provider own turn signalling. A REALTIME entry node auto-promotes to server_vad unless you set type explicitly.
silence_threshold_ms
number
default:"500"
Trailing silence before end-of-utterance fires. Lower (~300) for snappier replies; raise (~800) for callers who pause to think.
min_speech_ms
number
default:"300"
Minimum speech duration to count as a real utterance. Raise if line noise causes false triggers.
Backchannels (“mhm”, “yeah”) shouldn’t cut the agent off. The runtime uses hold-and-confirm barge-in for exactly this — a short interjection never cancels the agent. It’s on by default; the full model (and the PSTN-vs-browser echo-gating knobs) is in turn detection.

Step 4 — Add tools

A useful agent does things — looks up an order, books a slot, checks a balance. Declare tools in the agent config; the model calls them mid-conversation and the runtime feeds results back while keeping the caller engaged.
{
  "kind": "http",
  "name": "lookup_order",
  "description": "Look up an order by its ID and return its status.",
  "spec": {
    "method": "GET",
    "url": "https://api.example.com/orders/{{order_id}}",
    "auth": { "type": "bearer", "token": "sk_live_..." },
    "parameters": {
      "type": "object",
      "properties": {
        "order_id": { "type": "string", "description": "Order id to look up." }
      },
      "required": ["order_id"]
    },
    "timeout_ms": 10000
  }
}
Three tool kinds ship today: http (call an endpoint you own), mcp (a remote MCP server over Streamable HTTP), and telephony tools like routing to a live human queue. Full parameter reference, MCP setup, and per-node allow-lists are in tool calling.

Step 5 — Hand off to a human

The agent thesis for regulated work: the model qualifies, authenticates, and captures compliance context, then escalates to a person — a warm handoff, not a cold transfer. The caller keeps their sid and hears uninterrupted audio; only the agent leg re-points. There are two shapes, and you pick by target.

Re-attach to another agent

Swap the AI agent for a different one — same call, new brain.
await call.transfer({ agent: "agent_tier2" });

Escalate to a human

Route to a live human queue (browser softphone or SIP deskphone). The telephony tool route_to_skill — or an operator transfer — rings the next available agent and bridges them onto the caller leg.
// forward the whole call to a PSTN number …
await call.transfer("+15550000000");
// … or re-attach a different agent to the same sid
await call.transfer({ agent: "agent_tier2" });
call.transfer("+15550000000")
call.transfer(agent="agent_tier2")
On the wire, escalation pauses the AI leg, mirrors the caller’s audio to the human, and the human’s mic is injected back to the caller — the caller never hears a gap. The queue routing and human-agent ring (browser or deskphone) are ACD-driven. Mechanics live in AI ↔ human handoff; the product-level bot ⇄ human framing (and warm-handoff context) is in handoffs.

Step 6 — Record and review

Recording captures both legs — caller on the left channel, agent on the right — and lands a stereo WAV in your recording store for playback and review. Media QoS (loss, jitter, MOS) is scored per call at the same time.
Recording is a workspace/trunk-level capability, not a per-call SDK flag in the current surface — you enable it on the trunk (or workspace) and every call it carries is captured. There’s no record: true argument on originate() today. To analyze recordings programmatically, pull them and the QoS metrics from the observability plane.
Once recording is on, wire up review:

Record & analyze

Pull both legs and run analysis over the stereo capture.

MOS, jitter & loss

The per-call media-quality score and what drives it.

Call traces

The event-level trace of a single call, keyed on its sid.

Dashboards

Fleet-wide call, quality, and agent reporting.

Step 7 — Tear down

End the call cleanly. Hangup releases the media legs, tears down the agent session, and emits the final lifecycle event and CDR.
await call.hangup();
call.hangup()
You rarely need to poll for the end — the runtime’s idle watchdog re-engages a silent caller (“still there?”) and drops the AI leg if they’ve gone, and there’s a hard max-session ceiling. Tune both in session lifecycle.

Put it together

The whole outbound-to-teardown flow, minus the config that lives in the agent definition:
import { Voice } from "@telequick/sdk/voice";

const v = new Voice({
  baseUrl: "https://engine.telequick.dev",
  apiKey:  process.env.TELEQUICK_CREDENTIALS!,
  orgId:   "org_abc",
});

// 1. originate + attach the agent on answer
const call = await v.calls.originate({
  to: "+15551234567", from: "+15558675309",
  trunkId: "trunk_main", agent: "agent_sales",
});

// 2. …the agent runs the conversation, calls tools, and can escalate…
//    (turn detection + tools live in the agent config; recording on the trunk)
await call.transfer({ agent: "agent_tier2" }); // e.g. mid-call re-attach

// 3. tear down
await call.hangup();
import os
from telequick.voice import Voice

v = Voice(
    base_url="https://engine.telequick.dev",
    api_key=os.environ["TELEQUICK_CREDENTIALS"],
    org_id="org_abc",
)

call = v.calls.originate(
    to="+15551234567", from_="+15558675309",
    trunk_id="trunk_main", agent="agent_sales",
)

call.transfer(agent="agent_tier2")
call.hangup()

Next steps

Inbound call recipe

The answer-and-route path, end to end.

Outbound call recipe

Originate, poll, and read the outcome.

Transfer to a human

A runnable warm-handoff recipe.

Bring your own runtime

Use TeleQuick as pure transport with an external agent runtime.