> ## Documentation Index
> Fetch the complete documentation index at: https://docs.lehar.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Make an outbound phone call

> Have an agent dial a single recipient over the phone.

An outbound call has the agent dial a phone number over SIP instead of a browser joining a room. This guide places one call from your backend; to dial many recipients, run a [voice campaign](/guides/first-campaign) instead.

## Prerequisites

* A **published** voice agent.
* A workspace **API key** with `sessions:write`.
* An outbound SIP trunk configured on the deployment. Without it, the dial step fails.
* The recipient's number in **E.164** format, e.g. `+919876543210`.

## Place the call

`POST /calls` fires one outbound call with an API key alone — no human, no login:

```bash theme={null}
curl -X POST "$LEHAR_BASE_URL/calls" \
  -H "X-API-KEY: $LEHAR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "agent_id": "agent_sales_hi",
    "to_number": "+919876543210",
    "callee_name": "Jane",
    "custom_variables": { "company": "Acme" },
    "idempotency_key": "crm-lead-8821"
  }'
```

You get back a call record with `status: "queued"`. Accepting the call is asynchronous — it may be waiting for a calling window or a free concurrency slot before it dials. Add `call_config` to control retries, the calling window and idle timeouts; see [Calls](/concepts/calls) for the full contract.

<Tip>
  Always send an `idempotency_key`. If you never see the response, retrying with the same key returns `409` instead of dialling the person twice.
</Tip>

## Get the result

Poll the call until it reaches a terminal status, then read the session behind it:

```bash theme={null}
curl "$LEHAR_BASE_URL/calls/$CALL_ID" -H "X-API-KEY: $LEHAR_API_KEY"
```

`status` progresses `queued` → `dialing` → `in_progress` → `completed` / `failed` / `cancelled`, and `attempts` lists one session per dial — transcript and usage hang off those. In production, register an [outbound webhook](/guides/webhooks#outbound-webhooks) and receive `call_completed` instead of polling.

## Calling from the dashboard instead

`POST /sessions` with `call_type: "outbound_phone"` also dials a number, but it requires a **bearer login session** and a human user — it's what the dashboard uses. Prefer `POST /calls` for anything server-to-server; it is the only path with retries, calling windows and idempotency.

<Note>
  On a phone session you **don't** connect the returned `participant_token` — the human is on the phone leg, so the token is subscribe-only.
</Note>

## Notes & gotchas

* **E.164 always** — include the country code on every number.
* **Stored numbers are masked** in session metadata and logs (first char + `***` + last 4).
* **Dialing outcomes** (busy, no-answer, declined) are classified from the SIP result — the same classification [campaigns](/guides/campaign-lifecycle) use to decide retries.
* **Refused before it dials** — a workspace with a negative balance gets `402 insufficient_credits`, and where the deployment restricts destinations, a number outside the allowed prefixes gets `400 invalid_request`. Neither creates a call record. See the [Calls error table](/concepts/calls#errors).

<CardGroup cols={2}>
  <Card title="Calls" icon="phone-arrow-up-right" href="/concepts/calls">The full `POST /calls` contract.</Card>
  <Card title="Run your first campaign" icon="users" href="/guides/first-campaign">Dial many recipients with retries.</Card>
</CardGroup>
