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

# Start an outbound call

POST https://api.v2.rasen.ai/v1/calls
Content-Type: application/json

Starts a real call through an assigned outbound number. The call runs the agent live published version at admission; the request carries no configuration and no revision. Optional expected_version_id only refuses an unexpected live version; it cannot select a historical version. The admitted call keeps that version even if a later publication or rollback changes the live version. Requires current admission capacity. Same key/body replays the original call. The response always carries the executed publication identity (published_version_id). A paused or archived agent is refused with agent_not_live (details.state).

Reference: https://docs.v2.rasen.ai/api-reference/calls/create

## Authentication

- `Authorization` header (bearer token, required) — Create and manage workspace-owned keys through the existing app session routes, outside this public API. Creator and manager identities are audit actors only. Key validity follows workspace policy, expiry and explicit revocation, independently of creator membership. Do not include a workspace-selection header.

## Request

### Headers

- `Idempotency-Key` (string, required) — Stable key per logical intent, 1–255 printable characters. Persist it before sending. Retry the same key/body after an uncertain result for 24 hours. A changed body conflicts. Beyond retention, reconcile the resource before retrying; this is not permanent deduplication. Current authorization is rechecked on replay. Evaluation order: authenticate and check current authorization; look up intent; replay its stored receipt before checking the current revision; only a new intent evaluates If-Match and executes. Fingerprint includes method, path, body and original If-Match. A changed request returns idempotency_key_reused. The server stores only a keyed (HMAC) fingerprint, and no error or response ever returns a fingerprint or hash of a request. In-flight intent returns 409 idempotency_in_progress with Retry-After; retry the same intent after reconciliation. Receipts describe that attempt, not current resource state. A replay returns the original status code and body, with the response header Idempotent-Replayed: true.

### Body (application/json)

This endpoint expects a CallCreate.

- `agent_id` (string, required) — Opaque agent identifier. Do not parse its contents.
- `to` (string, required)
- `expected_version_id` (string, optional) — Optional precondition: refuse if this is not the live version at admission. Does not select an old version.
- `dynamic_variables` (map from string to string, optional) — Flat object of string values. At most 64 keys; each key matches ^\[a-z]\[a-z0-9\_]\$. Each value is at most 4096 characters and contains no control characters except tab and newline. The whole object is at most 16 KiB as JSON. Nested objects and non-string values are refused, never converted. Overrides literal defaults; undeclared names are allowed. Credentials are forbidden.
- `metadata` (map from string to string, optional) — Bounded customer correlation map: at most 16 keys; each key matches ^\[a-z]\[a-z0-9\_]\$ (the same form the metadata\_key filter accepts); each value at most 512 characters; at most 4 KiB in total. No credential storage. Metadata is not a uniqueness or permanent deduplication guarantee.
- `from_number_id` (string, optional) — Choose an existing number assigned outbound to this agent. Omission selects the sole eligible assignment, or with several the agent's primary outbound number when it can dial (outbound_primary_agent_ids on the number). Zero eligible numbers, or several with no primary or a primary that cannot dial, refuse admission; never choose arbitrarily.

## Response

### 202

Start an outbound call.

- `call_id` (string, required) — Opaque call identifier. Do not parse its contents.
- `published_version_id` (string, required) — Opaque pub identifier. Do not parse its contents.
- `state` (string, required)
- `from_number_id` (string, required, nullable) — Number used for this call; null for non-phone calls.
- `poll_after_seconds` (integer, required) — Seconds to wait before the first GET /calls/\{call\_id}.

## Errors

### 400 Bad Request Error

Invalid input or cursor. Correct the request.

- `error` (ErrorResponseError, required) — Only code and message are required. Rasen sends details, request_id and retry on every error, but clients must parse an error that lacks them, so a typed error is never lost to a parsing failure.

### 401 Unauthorized Error

Invalid, expired or revoked API key. Creator departure or membership changes do not invalidate a workspace key.

- `error` (ErrorResponseError, required) — Only code and message are required. Rasen sends details, request_id and retry on every error, but clients must parse an error that lacks them, so a typed error is never lost to a parsing failure.

### 403 Forbidden Error

Operation not allowed by current credential or workspace access.

- `error` (ErrorResponseError, required) — Only code and message are required. Rasen sends details, request_id and retry on every error, but clients must parse an error that lacks them, so a typed error is never lost to a parsing failure.

### 404 Not Found Error

Resource absent or not visible in this workspace.

- `error` (ErrorResponseError, required) — Only code and message are required. Rasen sends details, request_id and retry on every error, but clients must parse an error that lacks them, so a typed error is never lost to a parsing failure.

### 409 Conflict Error

Conflict. Not retryable: read the current state, fix the request and send a new request. The one exception is idempotency_in_progress, which carries Retry-After: wait, then resend the identical request with the same Idempotency-Key. The operation lists its codes in x-error-codes; branch on error.code and, for conflict, error.details.reason.

- `error` (ErrorResponseError, required) — Only code and message are required. Rasen sends details, request_id and retry on every error, but clients must parse an error that lacks them, so a typed error is never lost to a parsing failure.

### 413 Content Too Large Error

Body or upload exceeds its allowed limit.

- `error` (ErrorResponseError, required) — Only code and message are required. Rasen sends details, request_id and retry on every error, but clients must parse an error that lacks them, so a typed error is never lost to a parsing failure.

### 429 Too Many Requests Error

Rate limited. Respect Retry-After and retry the same intent where permitted.

- `error` (ErrorResponseError, required) — Only code and message are required. Rasen sends details, request_id and retry on every error, but clients must parse an error that lacks them, so a typed error is never lost to a parsing failure.

### 500 Internal Server Error

Internal error. The effect may have happened. Retry the same request with the same Idempotency-Key; read the resource before creating a new intent. For an operation that takes an Idempotency-Key, a client retry with the same key is safe: the server replays the stored outcome if the first attempt took effect; if the first attempt had no effect, the retry performs the operation once. Never retry with a new key.

- `error` (ErrorResponseError, required) — Only code and message are required. Rasen sends details, request_id and retry on every error, but clients must parse an error that lacks them, so a typed error is never lost to a parsing failure.

### 502 Bad Gateway Error

Upstream failure. An effect may already have happened; reconcile and use the original intent key. For an operation that takes an Idempotency-Key, a client retry with the same key is safe: the server replays the stored outcome if the first attempt took effect; if the first attempt had no effect, the retry performs the operation once. Never retry with a new key.

- `error` (ErrorResponseError, required) — Only code and message are required. Rasen sends details, request_id and retry on every error, but clients must parse an error that lacks them, so a typed error is never lost to a parsing failure.

### 503 Service Unavailable Error

Service unavailable. Respect Retry-After where present. For an operation that takes an Idempotency-Key, a client retry with the same key is safe: the server replays the stored outcome if the first attempt took effect; if the first attempt had no effect, the retry performs the operation once. Never retry with a new key.

- `error` (ErrorResponseError, required) — Only code and message are required. Rasen sends details, request_id and retry on every error, but clients must parse an error that lacks them, so a typed error is never lost to a parsing failure.

## Types

### ErrorResponseError

Only code and message are required. Rasen sends details, request_id and retry on every error, but clients must parse an error that lacks them, so a typed error is never lost to a parsing failure.

- `code` (string, required) — Stable code. Branch on code (and details.reason for conflict), never on message. Each code has one HTTP status; see components.x-error-catalog.
- `message` (string, required) — Safe description; no provider payload or submitted secrets.
- `details` (map from string to any, optional) — Safe code-specific details. The public fields for each code are listed in components.x-error-catalog. Details never contain a request fingerprint, a hash of a request, secret material or another tenant's data. forbidden may include required_capabilities (a list).
- `request_id` (string, optional) — Same value as the X-Request-Id header. Quote it to support.
- `retry` (string, optional) — Retry class of this code: no (fix the request or state first), yes (retry the same request and the same Idempotency-Key), retry_after (wait for Retry-After, then retry the same request). Open response vocabulary. Preserve unknown values; never assume they mean success.

## Examples

**Request**

```json
{
  "agent_id": "agent_01j00000000000000000000000",
  "to": "+12025550123",
  "dynamic_variables": {
    "customer_name": "Alex"
  },
  "metadata": {
    "customer_reference": "appointment-001"
  }
}
```

**Response**

```json
{
  "call_id": "call_01j00000000000000000000000",
  "published_version_id": "pub_01j00000000000000000000000",
  "state": "admitted",
  "from_number_id": "number_01j00000000000000000000000",
  "poll_after_seconds": 5
}
```

**SDK Code**

```python calls_create_example
import requests

url = "https://api.v2.rasen.ai/v1/calls"

payload = {
    "agent_id": "agent_01j00000000000000000000000",
    "to": "+12025550123",
    "dynamic_variables": { "customer_name": "Alex" },
    "metadata": { "customer_reference": "appointment-001" }
}
headers = {
    "Idempotency-Key": "intent-appointment-001",
    "Authorization": "Bearer <api_key>",
    "Content-Type": "application/json"
}

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

print(response.json())
```

```javascript calls_create_example
const url = 'https://api.v2.rasen.ai/v1/calls';
const options = {
  method: 'POST',
  headers: {
    'Idempotency-Key': 'intent-appointment-001',
    Authorization: 'Bearer <api_key>',
    'Content-Type': 'application/json'
  },
  body: '{"agent_id":"agent_01j00000000000000000000000","to":"+12025550123","dynamic_variables":{"customer_name":"Alex"},"metadata":{"customer_reference":"appointment-001"}}'
};

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

```ruby calls_create_example
require 'uri'
require 'net/http'

url = URI("https://api.v2.rasen.ai/v1/calls")

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

request = Net::HTTP::Post.new(url)
request["Idempotency-Key"] = 'intent-appointment-001'
request["Authorization"] = 'Bearer <api_key>'
request["Content-Type"] = 'application/json'
request.body = "{\n  \"agent_id\": \"agent_01j00000000000000000000000\",\n  \"to\": \"+12025550123\",\n  \"dynamic_variables\": {\n    \"customer_name\": \"Alex\"\n  },\n  \"metadata\": {\n    \"customer_reference\": \"appointment-001\"\n  }\n}"

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

```java calls_create_example
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://api.v2.rasen.ai/v1/calls")
  .header("Idempotency-Key", "intent-appointment-001")
  .header("Authorization", "Bearer <api_key>")
  .header("Content-Type", "application/json")
  .body("{\n  \"agent_id\": \"agent_01j00000000000000000000000\",\n  \"to\": \"+12025550123\",\n  \"dynamic_variables\": {\n    \"customer_name\": \"Alex\"\n  },\n  \"metadata\": {\n    \"customer_reference\": \"appointment-001\"\n  }\n}")
  .asString();
```

```php calls_create_example
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://api.v2.rasen.ai/v1/calls', [
  'body' => '{
  "agent_id": "agent_01j00000000000000000000000",
  "to": "+12025550123",
  "dynamic_variables": {
    "customer_name": "Alex"
  },
  "metadata": {
    "customer_reference": "appointment-001"
  }
}',
  'headers' => [
    'Authorization' => 'Bearer <api_key>',
    'Content-Type' => 'application/json',
    'Idempotency-Key' => 'intent-appointment-001',
  ],
]);

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

```csharp calls_create_example
using RestSharp;

var client = new RestClient("https://api.v2.rasen.ai/v1/calls");
var request = new RestRequest(Method.POST);
request.AddHeader("Idempotency-Key", "intent-appointment-001");
request.AddHeader("Authorization", "Bearer <api_key>");
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{\n  \"agent_id\": \"agent_01j00000000000000000000000\",\n  \"to\": \"+12025550123\",\n  \"dynamic_variables\": {\n    \"customer_name\": \"Alex\"\n  },\n  \"metadata\": {\n    \"customer_reference\": \"appointment-001\"\n  }\n}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

```swift calls_create_example
import Foundation

let headers = [
  "Idempotency-Key": "intent-appointment-001",
  "Authorization": "Bearer <api_key>",
  "Content-Type": "application/json"
]
let parameters = [
  "agent_id": "agent_01j00000000000000000000000",
  "to": "+12025550123",
  "dynamic_variables": ["customer_name": "Alex"],
  "metadata": ["customer_reference": "appointment-001"]
] as [String : Any]

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

let request = NSMutableURLRequest(url: NSURL(string: "https://api.v2.rasen.ai/v1/calls")! 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()
```