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

# Voice

## Worked sequences

These four flows walk an outbound call from creation to completed-recording
retrieval, including the two error shapes you must handle on the way. All
requests use your API key (`X-API-Key`) against
`https://api.orbit.devotel.io`.

### 1. Place an outbound call

`POST /voice/calls` initiates an outbound call and returns the new call id
and its initial status. `202` is not returned here — a successful initiation
responds `201`.

```bash cURL theme={null}
curl -X POST "https://api.orbit.devotel.io/api/v1/voice/calls" \
  -H "X-API-Key: dv_live_sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "+14155552671",
    "from": "+18005551234",
    "record": true
  }'
```

```json 201 Created theme={null}
{
  "data": {
    "call_id": "call_7b3e9c2d",
    "status": "initiated",
    "direction": "outbound"
  },
  "meta": {
    "request_id": "req_8c3f…",
    "timestamp": "2026-08-27T09:14:03Z"
  }
}
```

`status` starts as `initiated`; the lifecycle progresses through the terminal
state `completed`/`no-answer`/`failed` (see `hangup_reason` on the webhook, or
`GET /voice/calls/{id}` below). Poll the status endpoint or listen for the
webhook — don't assume the call is still ringing.

### 2. Poll the call status

`GET /voice/calls/{id}` returns the call row with the fields updated as the
call progresses.

```bash cURL theme={null}
curl -X GET "https://api.orbit.devotel.io/api/v1/voice/calls/call_7b3e9c2d" \
  -H "X-API-Key: dv_live_sk_your_key_here"
```

```json 200 OK theme={null}
{
  "data": {
    "id": "call_7b3e9c2d",
    "direction": "outbound",
    "from": "+18005551234",
    "to": "+14155552671",
    "status": "completed",
    "duration_seconds": 142,
    "billing_seconds": 142,
    "recording_url": "/recordings/rec_a51e2c94/download",
    "cost": "0.024",
    "currency": "USD",
    "started_at": "2026-08-27T09:14:03Z",
    "answered_at": "2026-08-27T09:14:15Z",
    "ended_at": "2026-08-27T09:17:37Z"
  },
  "meta": {
    "request_id": "req_9d4e…",
    "timestamp": "2026-08-27T09:17:38Z"
  }
}
```

### 3. Webhook — `call.completed`

Register a webhook endpoint (`POST /webhooks`) subscribed to `call.completed`
so your server receives the terminal status the moment it happens, instead of
polling.

```json POST to your endpoint theme={null}
{
  "type": "call.completed",
  "data": {
    "call_id": "call_7b3e9c2d",
    "provider": "jambonz",
    "provider_call_sid": "CA-…",
    "direction": "outbound",
    "from": "+18005551234",
    "to": "+14155552671",
    "status": "completed",
    "duration_seconds": 142,
    "hangup_reason": "normal_clearing",
    "sip_response_code": 200,
    "sip_reason": "OK",
    "timestamp": "2026-08-27T09:17:37Z"
  }
}
```

The full event list and endpoint-management API live at
[Webhook events](/reference/webhook-events). Field-level definitions (the same
payload every webhook carries) are pinned against `call.initiated` and
`call.completed` there.

### 4. Retrieve the recording

Once a call shows a recording, `GET /voice/calls/{id}/recording` returns a
signed, time-limited playback URL plus the duration/format and any chapter
markers. The `url` is pre-signed and expires after the
`expires_at` epoch-ms timestamp.

```bash cURL theme={null}
curl -X GET "https://api.orbit.devotel.io/api/v1/voice/calls/call_7b3e9c2d/recording" \
  -H "X-API-Key: dv_live_sk_your_key_here"
```

```json 200 OK theme={null}
{
  "data": {
    "url": "https://storage.googleapis.com/orbit-rec…/call_7b3e9c2d.wav?X-Signed-…",
    "duration_seconds": 142,
    "format": "wav",
    "expires_at": 1787899680000,
    "recording_id": "rec_a51e2c94",
    "chapters": []
  },
  "meta": {
    "request_id": "req_a4f1…",
    "timestamp": "2026-08-27T09:17:50Z"
  }
}
```

When structured chaptering is enabled, `chapters` carries
`[{ start_ms, end_ms, title, summary }]` markers; when it is off, it is
`[]`. A call that was never recorded returns `404 NOT_FOUND`.

### Error — unverified caller-id

When the caller-id in `from` is neither a platform/tenant number you own nor
a verified external caller-id, the request is rejected before dispatch.

```json 422 Unprocessable Entity theme={null}
{
  "error": {
    "code": "UNVERIFIED_CALLER_ID",
    "message": "Caller-id `from` is not a number owned by this organization and has not been verified as an outbound caller-id. Either pick one of your active platform/tenant numbers, or enrol the number via POST /voice/caller-ids/verify and confirm the OTP first.",
    "status": 422,
    "details": {
      "from": "+14155559999",
      "docs_url": "https://docs.orbit.devotel.io/voice/verified-caller-ids"
    }
  },
  "meta": {
    "request_id": "req_7b2c…",
    "timestamp": "2026-08-27T09:13:58Z"
  }
}
```

Fix it by enrolling the number through the verified-caller-id flow
(`POST /voice/caller-ids/verify` + OTP confirm) and retrying, or by picking a
number already active on your account.
