> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.getdial.ai/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.getdial.ai/_mcp/server.

# Place a voice call

> Place an outbound AI voice call with a system prompt, language, and voice.

Place an outbound voice call that an AI voice agent handles in real time. You provide an **outbound instruction** — the system prompt describing how the agent should behave — and a **language** for the conversation. The call is placed from one of your Dial numbers (`fromNumberId`).

```bash title="CLI"
dial call \
  --to +14155550123 \
  --outbound-instruction "You are confirming a dinner reservation for 7pm." \
  --language en-US \
  --from-number-id pn_123
```

```typescript title="Node"
import { DialClient } from "@getdial/sdk";

const dial = new DialClient({ apiKey: process.env.DIAL_API_KEY! });
const call = await dial.makeCall({
  to: "+14155550123",
  fromNumberId: "pn_123",
  outboundInstruction: "You are confirming a dinner reservation for 7pm.",
  language: "en-US",
});
console.log(call.id, call.status);
```

```python title="Python"
from dial_sdk import DialClient, DialConfig

dial = DialClient(DialConfig(api_key="sk_live_..."))
call = await dial.make_call(
    to="+14155550123",
    from_number_id="pn_123",
    outbound_instruction="You are confirming a dinner reservation for 7pm.",
    language="en-US",
)
print(call.id, call.status)
```

```python title="LangChain"
from dial_langchain import MakeCallTool

call = MakeCallTool(api_key="sk_live_...")
await call.ainvoke({
    "to": "+14155550123",
    "from_number_id": "pn_123",
    "outbound_instruction": "You are confirming a dinner reservation for 7pm.",
    "language": "en-US",
})
```

```bash title="cURL"
curl -X POST https://api.getdial.ai/v1/calls \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "to": "+14155550123",
    "fromNumberId": "pn_123",
    "outboundInstruction": "You are confirming a dinner reservation for 7pm.",
    "language": "en-US"
  }'
```

The call returns immediately with a `status` of `initiated`. The conversation runs on its own; once it ends, the call's final `status`, `duration`, and `transcript` are available via `GET /api/v1/calls` or by listening for the `call.ended` [event](/documentation/platform/stream-account-events). However the call ends — completed, failed, or cancelled — it emits exactly one `call.ended` carrying the terminal `status`, so a wait always resolves; a cancelled call additionally carries `canceled: true`.

Placing a call isn't idempotent — there's no idempotency key, so a retry places a **second** call. If a request fails ambiguously, [confirm before retrying](/documentation/reference/errors#retries-and-idempotency) rather than blind-retrying.

## Choosing the voice and prompt

You shape each call **per request** — the same number can run a reservation-confirming call and a survey call by sending a different `outboundInstruction` each time. (The number's `inboundInstruction` is separate — it only governs calls *into* the number.)

* **Outbound instruction** — the system prompt for how the AI should behave on this call. This is the main lever you control.
* **Language** — a language tag such as `en-US`. The CLI defaults to `en-US`.
* **Voice** — each Dial number speaks with a preset voice, applied automatically; you don't set it per call.

```python
# Same number, different behavior per call.
await dial.make_call(
    to="+14155550123",
    from_number_id="pn_123",
    outbound_instruction="You are a friendly assistant running a 3-question survey.",
    language="en-US",
)
```

## Transferring the call to a human

Pass a `transferTo` number (E.164) to have the agent **forward the call to a person** once the conversation reaches the right moment. The agent rides out hold music and automated menus, and cold-transfers the moment a real human is on the line — it won't hand off to a recording or an IVR. The destination must differ from both `to` and the call's from number.

When the hand-off happens, the call's `transferredAt` timestamp is stamped; you can read it on the `Call` object via `GET /api/v1/calls`.

```bash title="CLI"
dial call \
  --to +14155550123 \
  --outbound-instruction "Greet the customer, then connect them to our agent." \
  --transfer-to +14155550100 \
  --from-number-id pn_123
```

```typescript title="Node"
const call = await dial.makeCall({
  to: "+14155550123",
  fromNumberId: "pn_123",
  outboundInstruction: "Greet the customer, then connect them to our agent.",
  transferTo: "+14155550100",
});
```

```python title="Python"
call = await dial.make_call(
    to="+14155550123",
    from_number_id="pn_123",
    outbound_instruction="Greet the customer, then connect them to our agent.",
    transfer_to="+14155550100",
)
```

```python title="LangChain"
await call.ainvoke({
    "to": "+14155550123",
    "from_number_id": "pn_123",
    "outbound_instruction": "Greet the customer, then connect them to our agent.",
    "transfer_to": "+14155550100",
})
```

```bash title="cURL"
curl -X POST https://api.getdial.ai/v1/calls \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "to": "+14155550123",
    "fromNumberId": "pn_123",
    "outboundInstruction": "Greet the customer, then connect them to our agent.",
    "transferTo": "+14155550100"
  }'
```