> For complete lifecycle-event payload schemas and examples, use the canonical overview: Session /api/sessions; Room lifecycle, members, and room playback /api/rooms; Session playback /api/playback; Recording /api/recording; DTMF /api/keypad-input; Messages /api/messages; WebSocket Session /api/web-socket. Use /api/event-delivery for transport behavior. Endpoint pages name relevant events, but these overviews are the canonical references.

# Originate outbound call

POST https://api.telekesher.dev/v1/sessions:dial
Content-Type: application/json

Start an outbound call and return its new `session_uuid` after the
request is admitted. A `202 Accepted` response confirms only admission; it
does not confirm that the destination rang or answered. Call progress and
completion are reported through webhooks.

**Triggered webhooks:**
`session.ringing_started`, `session.answered`, and `session.ended`. The optional
`session.early_media_started` webhook does not indicate an answer.

See [Session lifecycle events](/api/sessions#lifecycle-events) for complete payload schemas and examples.

Reference: https://docs.vorbal.dev/api/sessions/dial

## Authentication

- `Authorization` header (bearer token, required) — Application auth. Send `Authorization: Bearer <app_uuid>:<api_key>` using any active key in the app's collection. See [Authentication](https://voice-platform.docs.buildwithfern.com/api/authentication) for details.

## Request

### Headers

- `Idempotency-Key` (string, optional) — Optional key for safely retrying mutating requests. See [Idempotency](https://voice-platform.docs.buildwithfern.com/idempotency) for details.

### Body (application/json)

- `from` (string, required) — Caller ID to present, in the same E.164 format with leading `+` as `to` (e.g. `+972500000000`). The schema validates canonical syntax only. Runtime country policy must support the number, and it must be on the app's caller-ID allow-list. A supported but non-allow-listed value is rejected with `403 forbidden` (no call is placed, no webhooks fire). Branch on the response `code`, not the human message. The platform does not substitute a default caller ID; an SDK MAY catch `403 forbidden` for this operation and retry with its own configured approved default.
- `max_duration_sec` (integer, required) — Maximum total call duration in seconds. A typical value is 3600.
- `ring_timeout_sec` (integer, required) — Pre-answer ring timeout in seconds, measured from when dialing begins (the moment the request returns 202 Accepted), NOT from when the destination phone starts ringing. Call setup and routing consume part of this budget, so the callee experiences less ring time than the value set here. On expiry the dial ends with `session.ended` (answered=false, hangup_cause="timeout"). Distinct from `max_duration_sec`, the post-answer cap.
- `to` (string, required) — Destination number in international E.164 format with leading `+` (e.g. `+972500000000`). The schema validates canonical syntax only. Runtime country and trunk policies determine whether the destination is currently dialable; an unsupported destination is rejected with `400 invalid_request`.

## Response

### 202

Dial accepted

- `session_uuid` (string, required)
- `status` (enum, required)
  - Allowed values: `dialing`

## Examples

**Request**

```json
{
  "from": "+972740000000",
  "max_duration_sec": 3600,
  "ring_timeout_sec": 30,
  "to": "+972500000000"
}
```

**Response**

```json
{
  "session_uuid": "string",
  "status": "dialing"
}
```

**SDK Code**

```python Sessions & Calls_createDial_example
import requests

url = "https://api.telekesher.dev/v1/sessions:dial"

payload = {
    "from": "+972740000000",
    "max_duration_sec": 3600,
    "ring_timeout_sec": 30,
    "to": "+972500000000"
}
headers = {
    "Authorization": "Bearer <token>",
    "Content-Type": "application/json"
}

response = requests.post(url, json=payload, headers=headers)

print(response.json())
```

```javascript Sessions & Calls_createDial_example
const url = 'https://api.telekesher.dev/v1/sessions:dial';
const options = {
  method: 'POST',
  headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
  body: '{"from":"+972740000000","max_duration_sec":3600,"ring_timeout_sec":30,"to":"+972500000000"}'
};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go Sessions & Calls_createDial_example
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io"
)

func main() {

	url := "https://api.telekesher.dev/v1/sessions:dial"

	payload := strings.NewReader("{\n  \"from\": \"+972740000000\",\n  \"max_duration_sec\": 3600,\n  \"ring_timeout_sec\": 30,\n  \"to\": \"+972500000000\"\n}")

	req, _ := http.NewRequest("POST", url, payload)

	req.Header.Add("Authorization", "Bearer <token>")
	req.Header.Add("Content-Type", "application/json")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby Sessions & Calls_createDial_example
require 'uri'
require 'net/http'

url = URI("https://api.telekesher.dev/v1/sessions:dial")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n  \"from\": \"+972740000000\",\n  \"max_duration_sec\": 3600,\n  \"ring_timeout_sec\": 30,\n  \"to\": \"+972500000000\"\n}"

response = http.request(request)
puts response.read_body
```

```java Sessions & Calls_createDial_example
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://api.telekesher.dev/v1/sessions:dial")
  .header("Authorization", "Bearer <token>")
  .header("Content-Type", "application/json")
  .body("{\n  \"from\": \"+972740000000\",\n  \"max_duration_sec\": 3600,\n  \"ring_timeout_sec\": 30,\n  \"to\": \"+972500000000\"\n}")
  .asString();
```

```php Sessions & Calls_createDial_example
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://api.telekesher.dev/v1/sessions:dial', [
  'body' => '{
  "from": "+972740000000",
  "max_duration_sec": 3600,
  "ring_timeout_sec": 30,
  "to": "+972500000000"
}',
  'headers' => [
    'Authorization' => 'Bearer <token>',
    'Content-Type' => 'application/json',
  ],
]);

echo $response->getBody();
```

```csharp Sessions & Calls_createDial_example
using RestSharp;

var client = new RestClient("https://api.telekesher.dev/v1/sessions:dial");
var request = new RestRequest(Method.POST);
request.AddHeader("Authorization", "Bearer <token>");
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{\n  \"from\": \"+972740000000\",\n  \"max_duration_sec\": 3600,\n  \"ring_timeout_sec\": 30,\n  \"to\": \"+972500000000\"\n}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

```swift Sessions & Calls_createDial_example
import Foundation

let headers = [
  "Authorization": "Bearer <token>",
  "Content-Type": "application/json"
]
let parameters = [
  "from": "+972740000000",
  "max_duration_sec": 3600,
  "ring_timeout_sec": 30,
  "to": "+972500000000"
] as [String : Any]

let postData = JSONSerialization.data(withJSONObject: parameters, options: [])

let request = NSMutableURLRequest(url: NSURL(string: "https://api.telekesher.dev/v1/sessions:dial")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```