# Calls

> Control-plane API for the call lifecycle: originate, get, transfer, and hangup. Every operation keys off the call_sid.

A **call** is a single SIP dialog. The engine handles the dialog as a
back-to-back user agent (two legs, caller ↔ agent). Every part of a call keys
off one identifier, the **`call_sid`**: the media session, the agent session,
the audio tracks, the CDR, and the telemetry. This page is the control plane
for the call object. Use it to place a call, look up a call, move a call, and
end a call. To move audio in and out of a live call, see
[the audio bridge](/modalities/voice/api/transport).

> **NOTE:**
> The **Voice SDK** (`ClutchCallClient`) exposes these operations. There is no
> hand-written REST surface. Each SDK call maps to a typed control-plane RPC
> (`voice.calls.*`). The SDK carries your API key and org id. The control-plane
> API relays the operation to the engine over a correlated control channel.
> Address calls through the SDK. There is no separately documented `POST /calls`
> REST endpoint to curl.

## Construct the client

Every calls operation hangs off a `ClutchCallClient` instance with a scope
of one org. Construct the instance once and reuse it.

```ts
import { Voice } from "clutchcall_connect/voice";

const voice = new Voice({
  baseUrl: "https://engine.firstbyte.fast",   // control-plane API host
  apiKey:  process.env.CLUTCHCALL_API_KEY!,
  orgId:   "org_123",
  relayHost: "relay.firstbyte.fast",     // for the audio bridge; optional
});
```

```python
from clutchcall.voice import Voice

voice = Voice(
    base_url="https://engine.firstbyte.fast",
    api_key=os.environ["CLUTCHCALL_API_KEY"],
    org_id="org_123",
)
```

```go
voice, err := voice.New("https://engine.firstbyte.fast", apiKey, "org_123")
```

```rust
let voice = Voice::new("https://engine.firstbyte.fast", &api_key, "org_123")?;
```

## Originate a call

`calls.originate` dials `to` from `from` over the trunk that you name. If you
pass an `agent`, the engine attaches the voice agent automatically when the far
end answers. You get back a `Call` (below) as soon as the engine accepts the
request. Watch `status` (or the
[call-lifecycle events](/modalities/voice/transport-telephony/call-lifecycle))
to follow the call from `dialing` to `in_progress`.

### Request

- **`to`** (`string`, required) — The destination in E.164 (e.g. `+15551234567`).
- **`from`** (`string`, required) — The caller-ID that the outbound leg presents, in E.164. This must be a number that your trunk can present.
- **`trunkId`** (`string`, required) — The trunk to originate on. You provision trunks in the console. See [SIP trunking](/modalities/voice/transport-telephony/sip-trunking).
- **`agent`** (`string`) — Optional agent id. When set, the engine binds this server-side voice agent to the call on answer. The engine also connects the audio bridge end-to-end, so you do not open one yourself. Omit this field to originate a bare call and drive the audio yourself.
- **`ringTimeoutSec`** (`integer`, default `30`) — The number of seconds to ring before the engine stops the attempt. The call then lands in `no_answer`. The control plane clamps this value to `5..120`.

```ts
const call = await voice.calls.originate({
  to:       "+15551234567",
  from:     "+15558675309",
  trunkId:  "trunk_main",
  agent:    "healthcare-assistant",  // optional
  ringTimeoutSec: 30,
});
console.log(call.sid, call.status);   // "cs_…", "dialing"
```

```python
call = voice.calls.originate(
    to="+15551234567",
    from_="+15558675309",       # `from` is reserved in Python → from_
    trunk_id="trunk_main",
    agent="healthcare-assistant",
    ring_timeout_sec=30,
)
print(call.sid, call.status)
```

```go
call, err := voice.Calls().Originate(voice.OriginateArgs{
    To:             "+15551234567",
    From:           "+15558675309",
    TrunkID:        "trunk_main",
    Agent:          "healthcare-assistant",
    RingTimeoutSec: 30,
})
```

```rust
let call = voice.calls().originate(OriginateArgs {
    to:               "+15551234567",
    from:             "+15558675309",
    trunk_id:         "trunk_main",
    agent:            Some("healthcare-assistant"),
    ring_timeout_sec: 30,
})?;
```

### The `Call` object

`originate` and `get` both return a `Call`. These fields are the whole object.
There are no video codecs or track lists here. Audio lives on the bridge.

- **`sid`** (`string`) — The **`call_sid`** — the universal key. Use this key to open the audio bridge, attach an agent, transfer, and hang up. Also use this key to join CDRs, recordings, and telemetry.
- **`status`** (`string`) — One of `dialing`, `ringing`, `in_progress`, `completed`, `failed`, `no_answer`.
- **`to`** (`string`) — The destination number (E.164).
- **`from`** (`string`) — The presented caller-ID (E.164). In the Python SDK this attribute is `from_`.
- **`startedAt`** (`string`) — The ISO-8601 timestamp for when the engine created the call.
- **`trunkId`** (`string`) — The trunk that the call is on. This field is absent for calls with no trunk association.
- **`agent`** (`string`) — The bound agent id, if an agent was attached (at originate, or later through [`agents.attach`](/modalities/voice/api/agents)).

## Get a call

`calls.get` fetches the current `Call` snapshot by `sid`. Use it to poll the
status after an originate. Also use it to hydrate a call that you learned about
from a webhook or the lifecycle stream.

- **`sid`** (`string`, required) — The `call_sid` to look up.

```ts
const call = await voice.calls.get({ sid: "cs_abc123" });
if (call.status === "in_progress") { /* … */ }
```

```python
call = voice.calls.get(sid="cs_abc123")
```

```go
call, err := voice.Calls().Get("cs_abc123")
```

```rust
let call = voice.calls().get("cs_abc123")?;
```

> **NOTE:**
> `get` returns a point-in-time snapshot. It does not stream. For live status
> without polling, consume
> [call-lifecycle events](/modalities/voice/transport-telephony/call-lifecycle)
> or set up [webhooks](/modalities/voice/api/webhooks).

## Transfer a call

`call.transfer` moves a connected call. Pass **exactly one** of:

- **`to`** (`string`) — A PSTN number (E.164). The engine forwards the call to this number. This drives a SIP transfer (`REFER`) on the caller leg.
- **`agent`** (`string`) — An agent id. The engine re-attaches the call to this server-side agent instead of a forward off-net.

If you pass both fields, or neither field, the SDK rejects the request before
it sends any RPC.

```ts
// Forward to a PSTN number…
await call.transfer({ to: "+15550001111" });
// …or re-attach to a different agent.
await call.transfer({ agent: "billing-specialist" });
// shorthand: a bare string is treated as `to`
await call.transfer("+15550001111");
```

```python
call.transfer(to="+15550001111")
call.transfer(agent="billing-specialist")
```

```go
call.Transfer(voice.TransferArgs{To: "+15550001111"})
call.Transfer(voice.TransferArgs{Agent: "billing-specialist"})
```

```rust
call.transfer_to("+15550001111")?;
call.transfer_agent("billing-specialist")?;
```

> **WARNING:**
> The **agent re-attach** path and **operator-initiated `to:` transfers** are
> wired end-to-end. The **peer-initiated** `REFER` path is only partially
> implemented: when the far end asks to be transferred, the engine acknowledges
> the request, but the onward outbound leg may not complete. Prefer transfers
> that you start from your side. Warm AI→human handoff is a distinct, shipped
> flow. See
> [AI ↔ human handoff](/modalities/voice/transport-telephony/ai-human-handoff).

## Hang up a call

`call.hangup` stops the dialog and both media legs. It emits the `cleared`
lifecycle event (with a Q.850 cause and duration) and finalizes the CDR.

```ts
await call.hangup();
```

```python
call.hangup()
```

```go
call.Hangup()
```

```rust
call.hangup()?;
```

## End-to-end

1. **Originate**
Dial out. Optionally bind an agent, so the engine connects the audio for you.
2. **Track status**
Poll `calls.get`, or subscribe to lifecycle events, until `status` reaches
`in_progress` (or `no_answer` / `failed`).
3. **Act on the live call**
Move audio with the [audio bridge](/modalities/voice/api/transport),
[transfer](#transfer-a-call), or attach a different
[agent](/modalities/voice/api/agents) mid-call.
4. **Hang up**
Call `hangup` when you are done. This step finalizes the CDR.

## Related

- **[Audio bridge](/modalities/voice/api/transport)** — Move caller and agent audio in and out of a live call over MoQT.
- **[Agents API](/modalities/voice/api/agents)** — Bind or swap a server-side voice agent on a call.
- **[Call lifecycle](/modalities/voice/transport-telephony/call-lifecycle)** — Every state that a call moves through, and the events that you can subscribe to.
- **[Outbound dialing](/modalities/voice/transport-telephony/outbound)** — Single originate, paced campaigns, and the dialer model.
