# Voice sessions

## What this covers

One session is one call. Create it to get a single-use WebSocket URL for browser audio, or dial out on your own carrier line. Billing is settled from connected seconds when the session ends.

Every example below runs as written once these are exported:

```bash
export DVAARIK_API_KEY="dvk_..."        # project API key, server side only
export DVAARIK_TOKEN="<account access token>"   # console session, from POST /console/auth/login
export PROJECT_ID="<uuid>"
export SESSION_ID="<uuid>"
```

A `<value>` inside a JSON body is a value you supply. Responses are JSON; every error body is `{ "detail": … }`. Each schema is expanded once per page, where it first appears.

### Machine door errors

Raised by the project-key dependency before any handler on this page runs.

| Status | Cause |
| --- | --- |
| `401` | `Missing API key` or `Invalid API key` — the `X-Api-Key` header is absent, unknown, or revoked. |
| `403` | `API key is not scoped to a project`, `Project unavailable`, or `API key is missing the required scope '<scope>'`. |
| `429` | The project's `api_rpm` ceiling was exceeded. Honour `Retry-After`; `X-RateLimit-*` headers are on every response. |

Scopes used on this page: `voice:read`, `voice:write`. Mint a key with only these.

### Account door errors

| Status | Cause |
| --- | --- |
| `401` | No account session was presented, or it is invalid or expired. Refresh once, then re-authenticate. |
| `403` | The account is suspended or its email is not verified. |

## Operations — project API key

Call these from your own server with a project key. Never from a browser or a mobile bundle.

### GET `/v2/voice/sessions`

List sessions newest first, filtered by status, agent or date range.

| Detail | Value |
| --- | --- |
| Door | Project API key — `X-Api-Key: $DVAARIK_API_KEY` |
| Scope | `voice:read` |
| Operation | `list_voice_sessions_v2_voice_sessions_get` |
| Success | `200` `VoiceSessionInfo[]` |

| Parameter | In | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `limit` | query | integer | no |  |
| `offset` | query | integer | no |  |
| `status` | query | string or null | no |  |
| `agent_id` | query | uuid or null | no |  |
| `from` | query | date-time or null | no |  |
| `to` | query | date-time or null | no |  |
| `has_insights` | query | boolean or null | no | true for sessions whose post-call analysis produced a result; false for every other session. |

#### `VoiceSessionInfo`

| Field | Type | Required | Notes |
| --- | --- | --- | --- |
| `agent_id` | uuid or null | yes |  |
| `agent_version` | integer or null | yes |  |
| `connected_seconds` | integer | yes |  |
| `cost_nano_usd` | string | yes |  |
| `created_at` | date-time | yes |  |
| `currency` | string | yes |  |
| `direction` | string | yes |  |
| `end_reason` | string or null | yes |  |
| `ended_at` | date-time or null | yes |  |
| `hold_nano_usd` | string | yes |  |
| `id` | uuid | yes |  |
| `insights` | CallInsightsInfo or null | no |  |
| `language` | string | yes |  |
| `max_duration_seconds` | integer | yes |  |
| `media_connected_at` | date-time or null | yes |  |
| `media_disconnected_at` | date-time or null | yes |  |
| `metadata` | object or null | yes |  |
| `money_scale` | integer | yes |  |
| `pipeline_mode` | string | yes |  |
| `pipeline_snapshot` | object | yes |  |
| `project_id` | uuid | yes |  |
| `project_revision` | integer | yes |  |
| `rate_nano_usd_per_min` | string | yes |  |
| `rate_version` | string | yes |  |
| `record` | boolean | yes |  |
| `recording_ref` | object or null | no | Where this call's audio is, or null when there is none. The audio lives in your own carrier account and is fetched with your own carrier credentials, not ours — Dvaarik does not store call recordings. The block is {provider, reference_id, url, fetched_from}: `fetched_from` is "carrier" when your carrier holds the recording (a null `reference_id` there means the carrier accepted the request and will name the recording on its own webhook), and "unavailable" with a `detail` sentence when the carrier could not be asked at all. Browser and bring-your-own-socket sessions have no carrier, so they have no recording and this is always null for them — there is no fallback, because a fallback would mean us storing the audio again. |
| `sample_rate_in` | integer | yes |  |
| `sample_rate_out` | integer | yes |  |
| `started_at` | date-time or null | yes |  |
| `status` | string | yes |  |
| `store_transcript` | boolean | yes |  |

| Error | When |
| --- | --- |
| `422` | The request body or query is not valid. `detail` is an array of `{loc, msg, type}` and `loc` names the offending field. |

Plus the machine-door errors above. Full list on the [errors page](/docs/errors).

```bash
curl -sS -X GET "https://api.developers.dvaarik.com/v2/voice/sessions?limit=20" \
  -H "X-Api-Key: $DVAARIK_API_KEY"
```

### POST `/v2/voice/sessions`

Admit a media session and return a single-use `ws_url` valid for `expires_in_seconds`. Hand only that URL to the client that streams audio.

| Detail | Value |
| --- | --- |
| Door | Project API key — `X-Api-Key: $DVAARIK_API_KEY` |
| Scope | `voice:write` |
| Operation | `create_voice_session_v2_voice_sessions_post` |
| Success | `201` `CreateVoiceSessionResponse` |

Request body: `CreateVoiceSessionRequest`.

#### `CreateVoiceSessionRequest`

| Field | Type | Required | Notes |
| --- | --- | --- | --- |
| `agent_id` | uuid | yes |  |
| `max_duration_seconds` | integer or null | no |  |
| `metadata` | object or null | no |  |
| `record` | boolean or null | no |  |
| `sample_rate_in` | integer | no | default 16000 |
| `sample_rate_out` | integer | no | default 24000 |
| `store_transcript` | boolean or null | no |  |
| `variables` | object<string> | no |  |

#### `CreateVoiceSessionResponse`

| Field | Type | Required | Notes |
| --- | --- | --- | --- |
| `expires_in_seconds` | integer | yes |  |
| `session` | VoiceSessionInfo | yes |  |
| `ws_url` | string | yes |  |

| Error | When |
| --- | --- |
| `422` | The request body or query is not valid. `detail` is an array of `{loc, msg, type}` and `loc` names the offending field. |

Plus the machine-door errors above. Full list on the [errors page](/docs/errors).

```bash
curl -sS -X POST "https://api.developers.dvaarik.com/v2/voice/sessions" \
  -H "X-Api-Key: $DVAARIK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "agent_id": "<agent uuid>",
    "sample_rate_in": 16000,
    "sample_rate_out": 24000,
    "variables": {
      "company": "Northline Dental"
    }
  }'
```

### GET `/v2/voice/sessions/{session_id}`

Read one session with its frozen pipeline snapshot, connected seconds and settled cost.

| Detail | Value |
| --- | --- |
| Door | Project API key — `X-Api-Key: $DVAARIK_API_KEY` |
| Scope | `voice:read` |
| Operation | `get_voice_session_v2_voice_sessions__session_id__get` |
| Success | `200` `VoiceSessionInfo` |

| Parameter | In | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `session_id` | path | uuid | yes |  |

| Error | When |
| --- | --- |
| `422` | The request body or query is not valid. `detail` is an array of `{loc, msg, type}` and `loc` names the offending field. |

Plus the machine-door errors above. Full list on the [errors page](/docs/errors).

```bash
curl -sS -X GET "https://api.developers.dvaarik.com/v2/voice/sessions/$SESSION_ID" \
  -H "X-Api-Key: $DVAARIK_API_KEY"
```

### POST `/v2/voice/sessions/outbound`

Queue one outbound call from a number on your own verified carrier connection. Send an `Idempotency-Key`; a repeat returns the first call with `duplicate: true`.

| Detail | Value |
| --- | --- |
| Door | Project API key — `X-Api-Key: $DVAARIK_API_KEY` |
| Scope | `voice:write` |
| Operation | `create_outbound_voice_session_v2_voice_sessions_outbound_post` |
| Success | `202` `CreateOutboundVoiceSessionResponse` |

| Parameter | In | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `Idempotency-Key` | header | string or null | no |  |

Request body: `CreateOutboundVoiceSessionRequest`.

#### `CreateOutboundVoiceSessionRequest`

One queued call through a project-owned carrier connection and agent.

| Field | Type | Required | Notes |
| --- | --- | --- | --- |
| `agent_id` | uuid | yes |  |
| `from` | string | yes |  |
| `max_duration_seconds` | integer or null | no |  |
| `metadata` | object or null | no |  |
| `record` | boolean or null | no |  |
| `store_transcript` | boolean or null | no |  |
| `to` | string | yes |  |
| `variables` | object<string> | no |  |

#### `CreateOutboundVoiceSessionResponse`

| Field | Type | Required | Notes |
| --- | --- | --- | --- |
| `duplicate` | boolean | no | default false |
| `session` | VoiceSessionInfo | yes |  |

| Error | When |
| --- | --- |
| `422` | The request body or query is not valid. `detail` is an array of `{loc, msg, type}` and `loc` names the offending field. |

Plus the machine-door errors above. Full list on the [errors page](/docs/errors).

```bash
curl -sS -X POST "https://api.developers.dvaarik.com/v2/voice/sessions/outbound" \
  -H "X-Api-Key: $DVAARIK_API_KEY" \
  -H "Idempotency-Key: <unique per call attempt>" \
  -H "Content-Type: application/json" \
  -d '{
    "agent_id": "<agent uuid>",
    "from": "+15550000001",
    "to": "+15550000002"
  }'
```

## Operations — account session

The same resource through the console door. These are what the web console calls; both doors return the same shapes.

### GET `/console/v2/projects/{project_id}/voice/sessions`

List sessions newest first, filtered by status, agent or date range.

| Detail | Value |
| --- | --- |
| Door | Account session — `Authorization: Bearer $DVAARIK_TOKEN` |
| Operation | `consoleListVoiceSessions` |
| Success | `200` `VoiceSessionInfo[]` |

| Parameter | In | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `project_id` | path | uuid | yes |  |
| `limit` | query | integer | no |  |
| `offset` | query | integer | no |  |
| `status` | query | string or null | no |  |
| `agent_id` | query | uuid or null | no |  |
| `from` | query | date-time or null | no |  |
| `to` | query | date-time or null | no |  |
| `has_insights` | query | boolean or null | no | true for sessions whose post-call analysis produced a result; false for every other session. |

| Error | When |
| --- | --- |
| `401` | No account session was presented, or it is invalid or expired. |
| `403` | The account is suspended or its email is not verified. |
| `404` | The project or the addressed resource does not exist for this owner. A resource owned by another developer is deliberately indistinguishable from one that never existed. |
| `422` | The request body or query is not valid. `detail` is an array of `{loc, msg, type}` and `loc` names the offending field. |

Plus the account-door errors above. Full list on the [errors page](/docs/errors).

```bash
curl -sS -X GET "https://api.developers.dvaarik.com/console/v2/projects/$PROJECT_ID/voice/sessions?limit=20" \
  -H "Authorization: Bearer $DVAARIK_TOKEN"
```

### POST `/console/v2/projects/{project_id}/voice/sessions`

Admit a media session and return a single-use `ws_url` valid for `expires_in_seconds`. Hand only that URL to the client that streams audio.

| Detail | Value |
| --- | --- |
| Door | Account session — `Authorization: Bearer $DVAARIK_TOKEN` |
| Operation | `consoleCreateVoiceSession` |
| Success | `201` `CreateVoiceSessionResponse` |

| Parameter | In | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `project_id` | path | uuid | yes |  |

Request body: `CreateVoiceSessionRequest`.

| Error | When |
| --- | --- |
| `401` | No account session was presented, or it is invalid or expired. |
| `402` | Wallet balance cannot cover this session's maximum duration. |
| `403` | The account is suspended or its email is not verified. |
| `404` | The project or the addressed resource does not exist for this owner. A resource owned by another developer is deliberately indistinguishable from one that never existed. |
| `409` | The project or resource is in a state that refuses this change. |
| `422` | The request body or query is not valid. `detail` is an array of `{loc, msg, type}` and `loc` names the offending field. |
| `503` | BYOK provider accounts are unverified/unavailable, or the service is draining. |

Plus the account-door errors above. Full list on the [errors page](/docs/errors).

```bash
curl -sS -X POST "https://api.developers.dvaarik.com/console/v2/projects/$PROJECT_ID/voice/sessions" \
  -H "Authorization: Bearer $DVAARIK_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "agent_id": "<agent uuid>",
    "sample_rate_in": 16000,
    "sample_rate_out": 24000,
    "variables": {
      "company": "Northline Dental"
    }
  }'
```

### GET `/console/v2/projects/{project_id}/voice/sessions/{session_id}`

Read one session with its frozen pipeline snapshot, connected seconds and settled cost.

| Detail | Value |
| --- | --- |
| Door | Account session — `Authorization: Bearer $DVAARIK_TOKEN` |
| Operation | `consoleGetVoiceSession` |
| Success | `200` `VoiceSessionInfo` |

| Parameter | In | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `session_id` | path | uuid | yes |  |
| `project_id` | path | uuid | yes |  |

| Error | When |
| --- | --- |
| `401` | No account session was presented, or it is invalid or expired. |
| `403` | The account is suspended or its email is not verified. |
| `404` | The project or the addressed resource does not exist for this owner. A resource owned by another developer is deliberately indistinguishable from one that never existed. |
| `422` | The request body or query is not valid. `detail` is an array of `{loc, msg, type}` and `loc` names the offending field. |

Plus the account-door errors above. Full list on the [errors page](/docs/errors).

```bash
curl -sS -X GET "https://api.developers.dvaarik.com/console/v2/projects/$PROJECT_ID/voice/sessions/$SESSION_ID" \
  -H "Authorization: Bearer $DVAARIK_TOKEN"
```

### POST `/console/v2/projects/{project_id}/voice/sessions/outbound`

Queue one outbound call from a number on your own verified carrier connection. Send an `Idempotency-Key`; a repeat returns the first call with `duplicate: true`.

| Detail | Value |
| --- | --- |
| Door | Account session — `Authorization: Bearer $DVAARIK_TOKEN` |
| Operation | `consoleCreateOutboundVoiceSession` |
| Success | `202` `CreateOutboundVoiceSessionResponse` |

| Parameter | In | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `project_id` | path | uuid | yes |  |
| `Idempotency-Key` | header | string or null | no |  |

Request body: `CreateOutboundVoiceSessionRequest`.

| Error | When |
| --- | --- |
| `401` | No account session was presented, or it is invalid or expired. |
| `402` | Wallet balance cannot cover this call's maximum duration. |
| `403` | The account is suspended or its email is not verified. |
| `404` | The project or the addressed resource does not exist for this owner. A resource owned by another developer is deliberately indistinguishable from one that never existed. |
| `409` | The project or resource is in a state that refuses this change. |
| `422` | The request body or query is not valid. `detail` is an array of `{loc, msg, type}` and `loc` names the offending field. |
| `503` | The carrier connection or BYOK pipeline is unavailable. |

Plus the account-door errors above. Full list on the [errors page](/docs/errors).

```bash
curl -sS -X POST "https://api.developers.dvaarik.com/console/v2/projects/$PROJECT_ID/voice/sessions/outbound" \
  -H "Authorization: Bearer $DVAARIK_TOKEN" \
  -H "Idempotency-Key: <unique per call attempt>" \
  -H "Content-Type: application/json" \
  -d '{
    "agent_id": "<agent uuid>",
    "from": "+15550000001",
    "to": "+15550000002"
  }'
```

---

Source: https://developers.dvaarik.com/docs/api/voice-sessions · every page as one file: https://developers.dvaarik.com/docs.md
