Place a call to any phone number over one of your SIP trunks with a single control-plane call. You supply the destination, a caller-id, and the trunk to route over; the engine dials the carrier, and you get back a Call handle whose sid addresses everything else — audio, transfer, hangup, telemetry.
Outbound originate is a control-plane operation: it returns as soon as the call is dialing, not when the callee answers. Read the outcome by polling calls.get (below) or by consuming call-lifecycle events.

Before you start

1

A configured SIP trunk

Outbound calls route over a trunk you own. Create one and note its trunkId — see SIP trunking.
2

A caller-id you're allowed to present

The from number must be one the trunk (or your carrier) permits. Numbers you’ve allocated live in number provisioning.
3

An initialized 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",
});

Place a call

Call calls.originate with the destination, caller-id, and trunk. The returned handle carries the sid — the only identifier you need for everything that follows.
const call = await v.calls.originate({
  to:      "+15551234567",   // E.164 destination
  from:    "+15558675309",   // caller-id you're allowed to present
  trunkId: "trunk_main",     // SIP trunk to route over
});

console.log(call.sid, call.status); // e.g. "call_9f2c…" "dialing"
to
string
required
E.164 destination number.
from
string
required
Caller-id, E.164. Must be permitted on the trunk.
trunkId
string
required
The SIP trunk to route the call over.
agent
string
AI agent id to attach automatically on answer (see below).
ringTimeoutSec
number
Seconds to ring before giving up. Server-clamped to 5..120; default 30.

Attach an AI agent

Pass agent and the engine wires the audio bridge for you the moment the callee answers — the agent starts talking with no AudioBridge code on your side. This is the whole outbound-AI path in one call.
const call = await v.calls.originate({
  to:      "+15551234567",
  from:    "+15558675309",
  trunkId: "trunk_main",
  agent:   "appointment-reminder",   // attaches on answer
});
The agent id names a server-side agent you’ve configured — its speech-to-speech or ASR/LLM/TTS pipeline, prompt, and tools. Build one in the agent runtime. To attach an agent to a call that’s already connected, or to hand between a bot and a person, see AI ↔ human handoff.
If you’d rather own the media yourself — feed the caller’s audio to your own recognizer and push your own synthesized audio back — omit agent and attach an audio bridge to the sid instead.

Cap the ring time

ringTimeoutSec bounds how long the callee’s phone rings before the call is abandoned as no_answer. Pass a shorter value for tight retry loops or a longer one for numbers that are slow to pick up. The engine clamps the value to 5..120 seconds — anything below 5 becomes 5, anything above 120 becomes 120 — so you never have to validate it yourself.
const call = await v.calls.originate({
  to:      "+15551234567",
  from:    "+15558675309",
  trunkId: "trunk_main",
  ringTimeoutSec: 15,   // give up after 15s instead of the default 30
});
ringTimeoutSec: 3 is accepted but behaves as 5; ringTimeoutSec: 240 behaves as 120. The clamp is silent — the value you read back on the call may differ from what you sent.

Poll for the outcome

Because originate returns at dialing, you learn whether the call connected by re-fetching it. calls.get({ sid }) returns a fresh Call with the current status.
const c = await v.calls.get({ sid: call.sid });
if (c.status === "in_progress") console.log("connected");
A call moves through these statuses:
StatusMeaning
dialingOriginate accepted; the carrier is being reached.
ringingThe callee’s phone is ringing.
in_progressAnswered and connected — audio is flowing.
completedEnded normally after connecting.
no_answerRang past ringTimeoutSec without an answer.
failedRejected, busy, or unreachable.
Poll until the call leaves the ringing states to get its terminal outcome:
async function waitConnected(sid: string) {
  for (;;) {
    const c = await v.calls.get({ sid });
    if (c.status !== "dialing" && c.status !== "ringing") return c.status;
    await new Promise((r) => setTimeout(r, 500));
  }
}

const outcome = await waitConnected(call.sid); // in_progress | no_answer | failed
Polling is fine for one-off calls. For fleets of calls, subscribe to call-lifecycle events instead of polling each sid — the engine pushes every transition (initiated, ringing, answered, cleared) as it happens.

Placing many calls

calls.originate places one call. For high-volume outbound — pacing thousands of numbers with a rate limit and a concurrency cap — use the control-plane bulk dialer rather than looping originate yourself; it owns the pacing and back-pressure. See the PBX & ACD guide for campaign setup.

SIP trunking

Create and configure the trunks outbound calls route over.

Number provisioning

Allocate the numbers you present as caller-id.

Call lifecycle

The full event stream — a push alternative to polling status.

AI ↔ human handoff

Attach an agent mid-call, or hand between a bot and a person.