# client.calls

> The calls methods of the morevoice Python SDK: Inbound and outbound calls, their artefacts and live call control.

Inbound and outbound calls, their artefacts and live call control. These methods are on `client.calls`, where `client` is a `MoreVoice` client (see [the Python SDK](https://docs.morevoice.ai/sdk/python/#connect)). On `AsyncMoreVoice` the same methods are awaited. Each one returns the response object and raises an exception when the API answers with an error.

## `add_participant()`

**Add a participant.** Calls handled by a human agent: rings a phone number or an agent into the call (a conference of up to 8), or adds an AI assistant. The participant starts `ringing`; follow it in GET …/control.

```python
# client.calls
def add_participant(self, id: str, body: _m.CallsAddParticipantBody | Mapping[str, Any], *, more_voice_version: str | Unset = UNSET, idempotency_key: str | Unset = UNSET) -> _m.CallParticipantCreated
```

`client.calls.add_participant()` · `await async_client.calls.add_participant()` · `POST /calls/{id}/participants` · [API reference](https://docs.morevoice.ai/api/operations/calls_add_participant/)

### Parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | `str` | yes | A call ID (`call_…`). |

### Request body

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `to` | `object` | yes | Who to add: a phone number, an agent or an AI assistant (queues cannot join a call). |
| `announce` | `string` | no | A line said to them when they join. |

### Returns

`CallParticipantCreated`:

| Field | Type | Description |
| --- | --- | --- |
| `object` | `"call_participant"` | Always `call_participant`. |
| `call_id` | `string` | A call ID (prefix `call_`). |
| `participant_id` | `string` | — |
| `name` | `string` | — |
| `state` | `"ringing" \| "connected"` | — |

### Example

```python
from morevoice import MoreVoice

client = MoreVoice()  # MOREVOICE_API_KEY from the environment

call_participant_created = client.calls.add_participant("call_7Hk2Lm9Qp", {
    "announce": "Joining you to a customer call",
    "to": {
        "number": "+97235551234",
    },
})
print(call_participant_created)
```

## `consult()`

**Complete, merge, swap or cancel a consultation.** After a warm transfer: `complete` hands the customer to the consulted party, `merge` makes a three-way call, `swap` alternates between them, `cancel` returns to the customer.

```python
# client.calls
def consult(self, id: str, action: _m.CallsConsultAction, body: _m.CallsConsultBody | Mapping[str, Any] | Unset = UNSET, *, more_voice_version: str | Unset = UNSET, idempotency_key: str | Unset = UNSET) -> _m.CallControl
```

`client.calls.consult()` · `await async_client.calls.consult()` · `POST /calls/{id}/consult/{action}` · [API reference](https://docs.morevoice.ai/api/operations/calls_consult/)

### Parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | `str` | yes | A call ID (`call_…`). |
| `action` | `str` | yes | complete: hand the customer over; merge: a three-way call; swap: alternate; cancel: back to the customer. |

### Returns

`CallControl`:

| Field | Type | Description |
| --- | --- | --- |
| `object` | `"call_control"` | Always `call_control`. |
| `call_id` | `string` | A call ID (prefix `call_`). |
| `status` | `CallStatus` | `queued` (placed, not ringing yet), `ringing`, `in_progress` (answered, or an inbound call being handled) or `ended`. |
| `type` | `CallType` | Who handles the call: an AI assistant, a human agent, an IVR flow, a conference room, or a voicemail box. |
| `phase` | `"active" \| "held" \| "consulting" \| "conference"` | — |
| `held` | `boolean` | The customer is on hold. |
| `consult` | `object \| null` | A running consultation (warm transfer). |
| `participants` | `CallParticipant[]` | — |
| `recording` | `boolean` | — |

### Example

```python
from morevoice import MoreVoice

client = MoreVoice()  # MOREVOICE_API_KEY from the environment

call_control = client.calls.consult("call_7Hk2Lm9Qp", "complete", {})
print(call_control)
```

## `create()`

**Create an outbound call.** Places an outbound call with an assistant, or with a published voice flow and its assistant. `purpose` is required: it drives the outbound compliance checks (do-not-call list for every call; recorded consent and the national registry for marketing). The Idempotency-Key header is required, so a retried request never calls anyone twice. Test-mode keys place simulated calls that never reach a phone network.

```python
# client.calls
def create(self, body: _m.CallsCreateBody | Mapping[str, Any], *, more_voice_version: str | Unset = UNSET, idempotency_key: str | Unset = UNSET) -> _m.Call
```

`client.calls.create()` · `await async_client.calls.create()` · `POST /calls` · [API reference](https://docs.morevoice.ai/api/operations/calls_create/)

### Request body

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `to` | `string` | yes | The number to call, E.164. |
| `purpose` | `"service" \| "marketing" \| "survey"` | yes | service, marketing or survey. Marketing calls need recorded consent and pass the national do-not-call registry (§30A). |
| `amd` | `object` | no | Answering-machine detection (default: the assistant's setting). |
| `assistant_id` | `string` | no | The assistant that handles the call. Pass this or flow_id. |
| `caller_id` | `string` | no | The number to present; it must be allowed on the connection. |
| `connection_id` | `string` | no | The SIP connection to call out on (default: the organisation's default connection). |
| `flow_id` | `string` | no | A published voice flow; it runs on its linked assistant. Pass this or assistant_id. |
| `from_number_id` | `string` | no | The phone number to call from (`pn_…`, GET /v1/phone_numbers): it sets the connection and the caller ID. Or pass connection_id and caller_id. |
| `max_duration_s` | `integer` | no | End the call after this many seconds (default: the assistant's limit). |
| `metadata` | `MetadataInput` | no | Up to 50 key/value pairs (keys up to 40 characters, values up to 500) you attach to an object. Returned as sent. |
| `variables` | `Record<string, string>` | no | Template variables for the prompt, first message and flow ({{customer_name}}); at most 50. |

### Returns

`Call`:

| Field | Type | Description |
| --- | --- | --- |
| `id` | `string` | A call ID (prefix `call_`). |
| `object` | `"call"` | Always `call`. |
| `type` | `CallType` | Who handles the call: an AI assistant, a human agent, an IVR flow, a conference room, or a voicemail box. |
| `direction` | `CallDirection \| null` | — |
| `transport` | `"sip" \| "webrtc"` | `sip` for phone calls, `webrtc` for browser calls and conference rooms. |
| `status` | `CallStatus` | `queued` (placed, not ringing yet), `ringing`, `in_progress` (answered, or an inbound call being handled) or `ended`. |
| `from` | `string \| null` | The calling number (E.164 when it is a phone number) or name. |
| `to` | `string \| null` | The called number (E.164 when it is a phone number) or destination. |
| `assistant_id` | `string \| null` | `asst_…` ID. |
| `flow_id` | `string \| null` | `flow_…` ID. |
| `flow_version` | `integer \| null` | The flow version that ran (0: an unpublished draft). |
| `queue_id` | `string \| null` | `q_…` ID. |
| `agent_id` | `string \| null` | The human agent who handled the call (`usr_…`). |
| `campaign_id` | `string \| null` | `cmp_…` ID. |
| `contact_id` | `string \| null` | `ctc_…` ID. |
| `connection_id` | `string \| null` | `conn_…` ID. |
| `api_key_id` | `string \| null` | The API key that created the call (`key_…`). |
| `purpose` | `CallPurpose \| null` | — |
| `started_at` | `string` | When the call started: dialled out, or rang in. |
| `answered_at` | `string \| null` | — |
| `ended_at` | `string \| null` | — |
| `duration_ms` | `integer \| null` | — |
| `end_reason` | `string \| null` | Why the call ended, e.g. `customer-ended-call`, `assistant-ended-call`, `customer-busy`, `customer-did-not-answer`. |
| `answered_by` | `CallAnsweredBy \| null` | — |
| `disposition` | `string \| null` | The business outcome recorded on the call (set_outcome tool, or the summary). |
| `has_recording` | `boolean` | — |
| `ai_generated` | `boolean` | Machine-readable marking (EU AI Act Art. 50(2)): true when the call contains speech generated by AI — an AI voice agent, or synthetic-voice IVR prompts. Recordings of such calls carry the same tag in their file metadata. |
| `ai_segments` | `CallAiSegment[]` | When AI-generated speech was played, in ms from the start of the call (empty when `ai_generated` is false). |
| `summary` | `CallSummary \| null` | — |
| `cost` | `CallCost \| null` | Set once the call has ended (never null then); null while it is in progress. |
| `variables` | `Record<string, string>` | The variables the call was created with. |
| `metadata` | `Metadata` | Up to 50 key/value pairs (keys up to 40 characters, values up to 500) you attach to an object. Returned as sent. |
| `livemode` | `boolean` | `true` in live mode, `false` in test mode. |
| `assistant` | `CallAssistant` | The assistant of the call (expand[]=assistant). |

### Example

```python
from morevoice import MoreVoice

client = MoreVoice()  # MOREVOICE_API_KEY from the environment

call = client.calls.create({
    "assistant_id": "asst_4Gk2LmN9pQ4rS6tV8wX0yZ",
    "from_number_id": "pn_4Gk2LmN9pQ4rS6tV8wX0yZ",
    "metadata": {
        "crm_contact_id": "0031x00000AbCdE",
    },
    "purpose": "service",
    "to": "+972501234567",
    "variables": {
        "customer_name": "Dana",
    },
})
print(call)
```

## `create_stream()`

**Stream a call's audio to your WebSocket.** Starts a media stream on a live call in `fork` mode: a one-way copy of the caller (`inbound`), what the caller hears (`outbound`), or both, sent to your `wss://` URL as Twilio Media Streams messages (`connected`, `start`, `media`, `stop`). The call goes on exactly as before: if your server is slow, frames older than 2 s are dropped (`frames_dropped`); if it disconnects, the stream reconnects (3 tries) and otherwise gives up (`failed`) without touching the call. The upgrade request is signed with your signing secret over its path and query (`webhook-id` is the stream's ID). The stream stops when the call ends or with DELETE. At most 2 streams per call.

```python
# client.calls
def create_stream(self, id: str, body: _m.MediaStreamCreateParamsInput | Mapping[str, Any], *, more_voice_version: str | Unset = UNSET, idempotency_key: str | Unset = UNSET) -> _m.MediaStream
```

`client.calls.create_stream()` · `await async_client.calls.create_stream()` · `POST /calls/{id}/streams` · [API reference](https://docs.morevoice.ai/api/operations/calls_create_stream/)

### Parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | `str` | yes | A call ID (`call_…`). |

### Request body

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `url` | `string` | yes | Your WebSocket URL (`wss://`). The upgrade is signed with your signing secret (`webhook-id`, `webhook-timestamp`, `webhook-signature` over the path and query). |
| `custom_parameters` | `Record<string, string>` | no | Up to 20 string key/values the receiver gets in `start.customParameters` (as Twilio's stream parameters). |
| `format` | `"mulaw_8000" \| "l16_16000" \| "l16_8000"` | no | `mulaw_8000` (the default): G.711 μ-law at 8 kHz, Twilio's; `l16_16000` / `l16_8000`: signed 16-bit PCM, little-endian. |
| `mode` | `"fork"` | no | `fork` (the default): a one-way copy; your server's messages are ignored. |
| `tracks` | `"inbound" \| "outbound" \| "both"` | no | `inbound`: the caller; `outbound`: what the caller hears (the assistant, an agent, prompts); `both` (the default). |

### Returns

`MediaStream`:

| Field | Type | Description |
| --- | --- | --- |
| `id` | `string` | The stream's ID (`ms_…`): `streamSid` in the protocol's messages. |
| `object` | `"media_stream"` | Always `media_stream`. |
| `livemode` | `boolean` | `true` in live mode, `false` in test mode. |
| `call_id` | `string` | The call whose audio is streamed. |
| `mode` | `"fork"` | `fork`: a one-way copy of the call's audio; the call goes on as before. |
| `url` | `string` | The receiver's WebSocket URL. |
| `tracks` | `"inbound" \| "outbound" \| "both"` | `inbound`: the caller; `outbound`: what the caller hears (the assistant, an agent, prompts); `both`. |
| `format` | `"mulaw_8000" \| "l16_16000" \| "l16_8000"` | `mulaw_8000`: G.711 μ-law at 8 kHz (Twilio's); `l16_16000` / `l16_8000`: signed 16-bit PCM, little-endian. |
| `custom_parameters` | `Record<string, string>` | — |
| `status` | `"connecting" \| "streaming" \| "reconnecting" \| "stopped" \| "failed"` | `connecting` (and `reconnecting` after the receiver dropped), `streaming`, `stopped`, or `failed` (the receiver could not be reached; the call went on). |
| `frames_sent` | `integer` | 20 ms frames sent so far (all tracks). |
| `frames_dropped` | `integer` | Frames dropped because the receiver did not keep up (at most 2 s of audio waits per track). |
| `ended_reason` | `string \| null` | Why it ended: `call_ended`, `stopped`, `receiver_closed` or `connect_failed`; null while it runs. |
| `created` | `string` | An ISO-8601 timestamp in UTC. |

### Example

```python
from morevoice import MoreVoice

client = MoreVoice()  # MOREVOICE_API_KEY from the environment

media_stream = client.calls.create_stream("call_7Hk2Lm9Qp", {
    "custom_parameters": {
        "crm_id": "42",
    },
    "format": "mulaw_8000",
    "tracks": "both",
    "url": "wss://media.example.com/streams",
})
print(media_stream)
```

## `create_summary()`

**Summarise a call again.** Writes a new AI summary from the transcript and returns it (it replaces the stored one). Send an Idempotency-Key: a retry then returns the same summary instead of writing another.

```python
# client.calls
def create_summary(self, id: str, body: _m.CallsCreateSummaryBody | Mapping[str, Any] | Unset = UNSET, *, more_voice_version: str | Unset = UNSET, idempotency_key: str | Unset = UNSET) -> _m.CallSummary
```

`client.calls.create_summary()` · `await async_client.calls.create_summary()` · `POST /calls/{id}/summary` · [API reference](https://docs.morevoice.ai/api/operations/calls_create_summary/)

### Parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | `str` | yes | A call ID (`call_…`). |

### Returns

`CallSummary`:

| Field | Type | Description |
| --- | --- | --- |
| `object` | `"call_summary"` | Always `call_summary`. |
| `call_id` | `string` | A call ID (prefix `call_`). |
| `text` | `string` | A two-to-four sentence summary, in the language of the call. |
| `intent` | `string` | What the caller wanted. |
| `outcome` | `string` | How the call ended, or what was agreed. |
| `sentiment` | `"positive" \| "neutral" \| "negative" \| "mixed"` | — |
| `key_points` | `string[]` | — |
| `action_items` | `string[]` | Follow-ups for your business; empty when there are none. |
| `generated_at` | `string \| null` | — |

### Example

```python
from morevoice import MoreVoice

client = MoreVoice()  # MOREVOICE_API_KEY from the environment

call_summary = client.calls.create_summary("call_7Hk2Lm9Qp", {})
print(call_summary)
```

## `delete()`

**Delete a call.** Deletes an ended call with its transcript, events and recording. A call in progress answers 409 `call_in_progress`: hang it up first.

```python
# client.calls
def delete(self, id: str, *, more_voice_version: str | Unset = UNSET, idempotency_key: str | Unset = UNSET) -> _m.CallDeleted
```

`client.calls.delete()` · `await async_client.calls.delete()` · `DELETE /calls/{id}` · [API reference](https://docs.morevoice.ai/api/operations/calls_delete/)

### Parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | `str` | yes | A call ID (`call_…`). |

### Returns

`CallDeleted`:

| Field | Type | Description |
| --- | --- | --- |
| `id` | `string` | A call ID (prefix `call_`). |
| `object` | `"call"` | Always `call`. |
| `deleted` | `true` | — |

### Example

```python
from morevoice import MoreVoice

client = MoreVoice()  # MOREVOICE_API_KEY from the environment

call_deleted = client.calls.delete("call_7Hk2Lm9Qp")
print(call_deleted)
```

## `delete_stream()`

**Stop a media stream.** Stops the stream: what is queued is flushed, your server gets `stop`, and the socket closes. The call is not affected. Streams also stop on their own when the call ends.

```python
# client.calls
def delete_stream(self, id: str, stream_id: str, *, more_voice_version: str | Unset = UNSET, idempotency_key: str | Unset = UNSET) -> _m.MediaStream
```

`client.calls.delete_stream()` · `await async_client.calls.delete_stream()` · `DELETE /calls/{id}/streams/{stream_id}` · [API reference](https://docs.morevoice.ai/api/operations/calls_delete_stream/)

### Parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | `str` | yes | A call ID (`call_…`). |
| `stream_id` | `str` | yes | The stream's ID (`ms_…`). |

### Returns

`MediaStream`:

| Field | Type | Description |
| --- | --- | --- |
| `id` | `string` | The stream's ID (`ms_…`): `streamSid` in the protocol's messages. |
| `object` | `"media_stream"` | Always `media_stream`. |
| `livemode` | `boolean` | `true` in live mode, `false` in test mode. |
| `call_id` | `string` | The call whose audio is streamed. |
| `mode` | `"fork"` | `fork`: a one-way copy of the call's audio; the call goes on as before. |
| `url` | `string` | The receiver's WebSocket URL. |
| `tracks` | `"inbound" \| "outbound" \| "both"` | `inbound`: the caller; `outbound`: what the caller hears (the assistant, an agent, prompts); `both`. |
| `format` | `"mulaw_8000" \| "l16_16000" \| "l16_8000"` | `mulaw_8000`: G.711 μ-law at 8 kHz (Twilio's); `l16_16000` / `l16_8000`: signed 16-bit PCM, little-endian. |
| `custom_parameters` | `Record<string, string>` | — |
| `status` | `"connecting" \| "streaming" \| "reconnecting" \| "stopped" \| "failed"` | `connecting` (and `reconnecting` after the receiver dropped), `streaming`, `stopped`, or `failed` (the receiver could not be reached; the call went on). |
| `frames_sent` | `integer` | 20 ms frames sent so far (all tracks). |
| `frames_dropped` | `integer` | Frames dropped because the receiver did not keep up (at most 2 s of audio waits per track). |
| `ended_reason` | `string \| null` | Why it ended: `call_ended`, `stopped`, `receiver_closed` or `connect_failed`; null while it runs. |
| `created` | `string` | An ISO-8601 timestamp in UTC. |

### Example

```python
from morevoice import MoreVoice

client = MoreVoice()  # MOREVOICE_API_KEY from the environment

media_stream = client.calls.delete_stream("call_7Hk2Lm9Qp", "ms_4fG7hJ2kL9mN3pQ6rS8tUv")
print(media_stream)
```

## `download_recording()`

**Download a recording (signed link).** The URL a recording link points at. No API key: the token is the credential, valid for one call for 15 minutes. Supports Range requests.

```python
# client.calls
def download_recording(self, token: str) -> bytes
```

`client.calls.download_recording()` · `await async_client.calls.download_recording()` · `GET /recordings/{token}` · [API reference](https://docs.morevoice.ai/api/operations/calls_download_recording/)

### Parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `token` | `str` | yes | The signed token of a recording link. |

### Returns

Nothing, on success.

### Example

```python
from morevoice import MoreVoice

client = MoreVoice()  # MOREVOICE_API_KEY from the environment

data = client.calls.download_recording("token_123")
print(len(data), "bytes")
```

## `hangup()`

**Hang up a call.** Ends a live call for everyone on it (while it rings, cancels it) and returns the call. An ended call answers 409 call_not_active.

```python
# client.calls
def hangup(self, id: str, body: _m.CallsHangupBody | Mapping[str, Any] | Unset = UNSET, *, more_voice_version: str | Unset = UNSET, idempotency_key: str | Unset = UNSET) -> _m.Call
```

`client.calls.hangup()` · `await async_client.calls.hangup()` · `POST /calls/{id}/hangup` · [API reference](https://docs.morevoice.ai/api/operations/calls_hangup/)

### Parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | `str` | yes | A call ID (`call_…`). |

### Returns

`Call`:

| Field | Type | Description |
| --- | --- | --- |
| `id` | `string` | A call ID (prefix `call_`). |
| `object` | `"call"` | Always `call`. |
| `type` | `CallType` | Who handles the call: an AI assistant, a human agent, an IVR flow, a conference room, or a voicemail box. |
| `direction` | `CallDirection \| null` | — |
| `transport` | `"sip" \| "webrtc"` | `sip` for phone calls, `webrtc` for browser calls and conference rooms. |
| `status` | `CallStatus` | `queued` (placed, not ringing yet), `ringing`, `in_progress` (answered, or an inbound call being handled) or `ended`. |
| `from` | `string \| null` | The calling number (E.164 when it is a phone number) or name. |
| `to` | `string \| null` | The called number (E.164 when it is a phone number) or destination. |
| `assistant_id` | `string \| null` | `asst_…` ID. |
| `flow_id` | `string \| null` | `flow_…` ID. |
| `flow_version` | `integer \| null` | The flow version that ran (0: an unpublished draft). |
| `queue_id` | `string \| null` | `q_…` ID. |
| `agent_id` | `string \| null` | The human agent who handled the call (`usr_…`). |
| `campaign_id` | `string \| null` | `cmp_…` ID. |
| `contact_id` | `string \| null` | `ctc_…` ID. |
| `connection_id` | `string \| null` | `conn_…` ID. |
| `api_key_id` | `string \| null` | The API key that created the call (`key_…`). |
| `purpose` | `CallPurpose \| null` | — |
| `started_at` | `string` | When the call started: dialled out, or rang in. |
| `answered_at` | `string \| null` | — |
| `ended_at` | `string \| null` | — |
| `duration_ms` | `integer \| null` | — |
| `end_reason` | `string \| null` | Why the call ended, e.g. `customer-ended-call`, `assistant-ended-call`, `customer-busy`, `customer-did-not-answer`. |
| `answered_by` | `CallAnsweredBy \| null` | — |
| `disposition` | `string \| null` | The business outcome recorded on the call (set_outcome tool, or the summary). |
| `has_recording` | `boolean` | — |
| `ai_generated` | `boolean` | Machine-readable marking (EU AI Act Art. 50(2)): true when the call contains speech generated by AI — an AI voice agent, or synthetic-voice IVR prompts. Recordings of such calls carry the same tag in their file metadata. |
| `ai_segments` | `CallAiSegment[]` | When AI-generated speech was played, in ms from the start of the call (empty when `ai_generated` is false). |
| `summary` | `CallSummary \| null` | — |
| `cost` | `CallCost \| null` | Set once the call has ended (never null then); null while it is in progress. |
| `variables` | `Record<string, string>` | The variables the call was created with. |
| `metadata` | `Metadata` | Up to 50 key/value pairs (keys up to 40 characters, values up to 500) you attach to an object. Returned as sent. |
| `livemode` | `boolean` | `true` in live mode, `false` in test mode. |
| `assistant` | `CallAssistant` | The assistant of the call (expand[]=assistant). |

### Example

```python
from morevoice import MoreVoice

client = MoreVoice()  # MOREVOICE_API_KEY from the environment

call = client.calls.hangup("call_7Hk2Lm9Qp", {})
print(call)
```

## `hold()`

**Put a call on hold or take it off hold.** Calls handled by a human agent: the customer hears the hold music while `on` is true.

```python
# client.calls
def hold(self, id: str, body: _m.CallsHoldBody | Mapping[str, Any] | Unset = UNSET, *, more_voice_version: str | Unset = UNSET, idempotency_key: str | Unset = UNSET) -> _m.CallControl
```

`client.calls.hold()` · `await async_client.calls.hold()` · `POST /calls/{id}/hold` · [API reference](https://docs.morevoice.ai/api/operations/calls_hold/)

### Parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | `str` | yes | A call ID (`call_…`). |

### Request body

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `on` | `boolean` | no | true: put the customer on hold; false: take them off hold. |

### Returns

`CallControl`:

| Field | Type | Description |
| --- | --- | --- |
| `object` | `"call_control"` | Always `call_control`. |
| `call_id` | `string` | A call ID (prefix `call_`). |
| `status` | `CallStatus` | `queued` (placed, not ringing yet), `ringing`, `in_progress` (answered, or an inbound call being handled) or `ended`. |
| `type` | `CallType` | Who handles the call: an AI assistant, a human agent, an IVR flow, a conference room, or a voicemail box. |
| `phase` | `"active" \| "held" \| "consulting" \| "conference"` | — |
| `held` | `boolean` | The customer is on hold. |
| `consult` | `object \| null` | A running consultation (warm transfer). |
| `participants` | `CallParticipant[]` | — |
| `recording` | `boolean` | — |

### Example

```python
from morevoice import MoreVoice

client = MoreVoice()  # MOREVOICE_API_KEY from the environment

call_control = client.calls.hold("call_7Hk2Lm9Qp", {
    "on": True,
})
print(call_control)
```

## `list()`

**List calls.** The organisation's calls, newest first. Test-mode keys see only test calls. Filter by status, direction, type, assistant, campaign, agent, number or start time (`created[gte]`, `created[lt]`…).

```python
# client.calls
def list(self, *, limit: int | Unset = 20, starting_after: str | Unset = UNSET, ending_before: str | Unset = UNSET, status: _m.CallsListStatus | Unset = UNSET, direction: _m.CallsListDirection | Unset = UNSET, type_: _m.CallsListType | Unset = UNSET, assistant_id: str | Unset = UNSET, campaign_id: str | Unset = UNSET, agent_id: str | Unset = UNSET, from_: str | Unset = UNSET, to: str | Unset = UNSET, created: _m.CallsListCreated | Unset = UNSET, expand: _b.list[str] | Unset = UNSET, more_voice_version: str | Unset = UNSET) -> AsyncPage[_m.Call]
```

`client.calls.list()` · `await async_client.calls.list()` · `GET /calls` · [API reference](https://docs.morevoice.ai/api/operations/calls_list/)

### Parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `limit` | `integer` | no | How many objects to return, 1–100 (default 20). |
| `starting_after` | `string` | no | A cursor (`next_cursor`) or object ID: return the objects after it (older). |
| `ending_before` | `string` | no | A cursor or object ID: return the objects before it (newer). |
| `status` | `"in_progress" \| "ended"` | no | `in_progress`: calls that have not ended (queued, ringing or answered). `ended`: finished calls. |
| `direction` | `"inbound" \| "outbound" \| "browser"` | no |  |
| `type` | `"ai" \| "human" \| "ivr" \| "conference" \| "voicemail"` | no |  |
| `assistant_id` | `string` | no | A assistant ID (`asst_…`). |
| `campaign_id` | `string` | no | A campaign ID (`cmp_…`). |
| `agent_id` | `string` | no | A user ID (`usr_…`). |
| `from` | `string` | no | Calls from this number, exactly as recorded on the call. |
| `to` | `string` | no | Calls to this number, exactly as recorded on the call. |
| `created` | `object` | no | Filter on started_at: created[gte], created[gt], created[lte], created[lt] (ISO-8601). |
| `expand` | `string[]` | no | Fields to expand into objects, e.g. expand[]=assistant. |

### Returns

A page of results (`CallList`): `data`, `has_more` and `next_cursor`.

### Example

```python
from morevoice import MoreVoice

client = MoreVoice()  # MOREVOICE_API_KEY from the environment

for call in client.calls.list():
    print(call)
```

## `list_copilot_events()`

**List a call's copilot events.** What the real-time agent copilot showed on the call and what the agent did with it (cards used or dismissed, searches), newest first.

```python
# client.calls
def list_copilot_events(self, id: str, *, limit: int | Unset = 20, starting_after: str | Unset = UNSET, ending_before: str | Unset = UNSET, more_voice_version: str | Unset = UNSET) -> AsyncPage[_m.CopilotEvent]
```

`client.calls.list_copilot_events()` · `await async_client.calls.list_copilot_events()` · `GET /calls/{id}/copilot_events` · [API reference](https://docs.morevoice.ai/api/operations/calls_list_copilot_events/)

### Parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | `str` | yes | A call ID (`call_…`). |
| `limit` | `integer` | no | How many objects to return, 1–100 (default 20). |
| `starting_after` | `string` | no | A cursor (`next_cursor`) or object ID: return the objects after it (older). |
| `ending_before` | `string` | no | A cursor or object ID: return the objects before it (newer). |

### Returns

A page of results (`CopilotEventList`): `data`, `has_more` and `next_cursor`.

### Example

```python
from morevoice import MoreVoice

client = MoreVoice()  # MOREVOICE_API_KEY from the environment

for call in client.calls.list_copilot_events("call_7Hk2Lm9Qp"):
    print(call)
```

## `pci()`

**Pause or resume recording for a card payment.** PCI DSS scope reduction: while paused the call is neither recorded nor transcribed, live listeners hear silence and keypad digits are logged masked; with DTMF masking on, keypad tones are removed for everyone except the payment line. Pausing a paused call (or resuming a running one) is a 409. Needs the organisation's PCI mode.

```python
# client.calls
def pci(self, id: str, body: _m.CallsPciBody | Mapping[str, Any], *, more_voice_version: str | Unset = UNSET, idempotency_key: str | Unset = UNSET) -> _m.CallPci
```

`client.calls.pci()` · `await async_client.calls.pci()` · `POST /calls/{id}/pci` · [API reference](https://docs.morevoice.ai/api/operations/calls_pci/)

### Parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | `str` | yes | A call ID (`call_…`). |

### Request body

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `action` | `"pause" \| "resume"` | yes | pause: stop recording and transcription for a card payment; resume: start them again. |
| `destination_participant_id` | `string` | no | Human-handled calls: the participant (a payment IVR or keypad-capture line you added) that receives the caller's keys. |
| `max_seconds` | `integer` | no | Resume by itself after this long (capped by the organisation's maximum, which is also the default). |
| `reason` | `string` | no | Shown in the call log (default `payment`). |

### Returns

`CallPci`:

| Field | Type | Description |
| --- | --- | --- |
| `object` | `"call_pci"` | Always `call_pci`. |
| `call_id` | `string` | A call ID (prefix `call_`). |
| `paused` | `boolean` | Recording and transcription are paused for a payment. |
| `reason` | `string \| null` | Why (e.g. `payment`), while paused. |
| `paused_at` | `string \| null` | — |
| `resumes_at` | `string \| null` | When the pause ends by itself unless resumed earlier. |
| `destination_participant_id` | `string \| null` | The participant the pause named as the payment line, if any (otherwise a participant on one of the organisation's payment numbers). |

### Example

```python
from morevoice import MoreVoice

client = MoreVoice()  # MOREVOICE_API_KEY from the environment

call_pci = client.calls.pci("call_7Hk2Lm9Qp", {
    "action": "pause",
    "max_seconds": 180,
    "reason": "payment",
})
print(call_pci)
```

## `remove_participant()`

**Remove a participant.** Drops a participant from the call. The customer (`client`) cannot be removed: hang up instead.

```python
# client.calls
def remove_participant(self, id: str, participant_id: str, *, more_voice_version: str | Unset = UNSET, idempotency_key: str | Unset = UNSET) -> _m.CallControl
```

`client.calls.remove_participant()` · `await async_client.calls.remove_participant()` · `DELETE /calls/{id}/participants/{participant_id}` · [API reference](https://docs.morevoice.ai/api/operations/calls_remove_participant/)

### Parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | `str` | yes | A call ID (`call_…`). |
| `participant_id` | `str` | yes | A participant_id from the call's control state. |

### Returns

`CallControl`:

| Field | Type | Description |
| --- | --- | --- |
| `object` | `"call_control"` | Always `call_control`. |
| `call_id` | `string` | A call ID (prefix `call_`). |
| `status` | `CallStatus` | `queued` (placed, not ringing yet), `ringing`, `in_progress` (answered, or an inbound call being handled) or `ended`. |
| `type` | `CallType` | Who handles the call: an AI assistant, a human agent, an IVR flow, a conference room, or a voicemail box. |
| `phase` | `"active" \| "held" \| "consulting" \| "conference"` | — |
| `held` | `boolean` | The customer is on hold. |
| `consult` | `object \| null` | A running consultation (warm transfer). |
| `participants` | `CallParticipant[]` | — |
| `recording` | `boolean` | — |

### Example

```python
from morevoice import MoreVoice

client = MoreVoice()  # MOREVOICE_API_KEY from the environment

call_control = client.calls.remove_participant("call_7Hk2Lm9Qp", "participant_id_123")
print(call_control)
```

## `retrieve()`

**Retrieve a call.** A call, live or ended. While it runs, `status` and `answered_at` come from the live call.

```python
# client.calls
def retrieve(self, id: str, *, expand: _b.list[str] | Unset = UNSET, more_voice_version: str | Unset = UNSET) -> _m.Call
```

`client.calls.retrieve()` · `await async_client.calls.retrieve()` · `GET /calls/{id}` · [API reference](https://docs.morevoice.ai/api/operations/calls_retrieve/)

### Parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | `str` | yes | A call ID (`call_…`). |
| `expand` | `string[]` | no | Fields to expand into objects, e.g. expand[]=assistant. |

### Returns

`Call`:

| Field | Type | Description |
| --- | --- | --- |
| `id` | `string` | A call ID (prefix `call_`). |
| `object` | `"call"` | Always `call`. |
| `type` | `CallType` | Who handles the call: an AI assistant, a human agent, an IVR flow, a conference room, or a voicemail box. |
| `direction` | `CallDirection \| null` | — |
| `transport` | `"sip" \| "webrtc"` | `sip` for phone calls, `webrtc` for browser calls and conference rooms. |
| `status` | `CallStatus` | `queued` (placed, not ringing yet), `ringing`, `in_progress` (answered, or an inbound call being handled) or `ended`. |
| `from` | `string \| null` | The calling number (E.164 when it is a phone number) or name. |
| `to` | `string \| null` | The called number (E.164 when it is a phone number) or destination. |
| `assistant_id` | `string \| null` | `asst_…` ID. |
| `flow_id` | `string \| null` | `flow_…` ID. |
| `flow_version` | `integer \| null` | The flow version that ran (0: an unpublished draft). |
| `queue_id` | `string \| null` | `q_…` ID. |
| `agent_id` | `string \| null` | The human agent who handled the call (`usr_…`). |
| `campaign_id` | `string \| null` | `cmp_…` ID. |
| `contact_id` | `string \| null` | `ctc_…` ID. |
| `connection_id` | `string \| null` | `conn_…` ID. |
| `api_key_id` | `string \| null` | The API key that created the call (`key_…`). |
| `purpose` | `CallPurpose \| null` | — |
| `started_at` | `string` | When the call started: dialled out, or rang in. |
| `answered_at` | `string \| null` | — |
| `ended_at` | `string \| null` | — |
| `duration_ms` | `integer \| null` | — |
| `end_reason` | `string \| null` | Why the call ended, e.g. `customer-ended-call`, `assistant-ended-call`, `customer-busy`, `customer-did-not-answer`. |
| `answered_by` | `CallAnsweredBy \| null` | — |
| `disposition` | `string \| null` | The business outcome recorded on the call (set_outcome tool, or the summary). |
| `has_recording` | `boolean` | — |
| `ai_generated` | `boolean` | Machine-readable marking (EU AI Act Art. 50(2)): true when the call contains speech generated by AI — an AI voice agent, or synthetic-voice IVR prompts. Recordings of such calls carry the same tag in their file metadata. |
| `ai_segments` | `CallAiSegment[]` | When AI-generated speech was played, in ms from the start of the call (empty when `ai_generated` is false). |
| `summary` | `CallSummary \| null` | — |
| `cost` | `CallCost \| null` | Set once the call has ended (never null then); null while it is in progress. |
| `variables` | `Record<string, string>` | The variables the call was created with. |
| `metadata` | `Metadata` | Up to 50 key/value pairs (keys up to 40 characters, values up to 500) you attach to an object. Returned as sent. |
| `livemode` | `boolean` | `true` in live mode, `false` in test mode. |
| `assistant` | `CallAssistant` | The assistant of the call (expand[]=assistant). |

### Example

```python
from morevoice import MoreVoice

client = MoreVoice()  # MOREVOICE_API_KEY from the environment

call = client.calls.retrieve("call_7Hk2Lm9Qp")
print(call)
```

## `retrieve_control()`

**Retrieve a call's control state.** Who is on the call, hold and consultation state. Ended calls answer status ended with no one connected.

```python
# client.calls
def retrieve_control(self, id: str, *, more_voice_version: str | Unset = UNSET) -> _m.CallControl
```

`client.calls.retrieve_control()` · `await async_client.calls.retrieve_control()` · `GET /calls/{id}/control` · [API reference](https://docs.morevoice.ai/api/operations/calls_retrieve_control/)

### Parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | `str` | yes | A call ID (`call_…`). |

### Returns

`CallControl`:

| Field | Type | Description |
| --- | --- | --- |
| `object` | `"call_control"` | Always `call_control`. |
| `call_id` | `string` | A call ID (prefix `call_`). |
| `status` | `CallStatus` | `queued` (placed, not ringing yet), `ringing`, `in_progress` (answered, or an inbound call being handled) or `ended`. |
| `type` | `CallType` | Who handles the call: an AI assistant, a human agent, an IVR flow, a conference room, or a voicemail box. |
| `phase` | `"active" \| "held" \| "consulting" \| "conference"` | — |
| `held` | `boolean` | The customer is on hold. |
| `consult` | `object \| null` | A running consultation (warm transfer). |
| `participants` | `CallParticipant[]` | — |
| `recording` | `boolean` | — |

### Example

```python
from morevoice import MoreVoice

client = MoreVoice()  # MOREVOICE_API_KEY from the environment

call_control = client.calls.retrieve_control("call_7Hk2Lm9Qp")
print(call_control)
```

## `retrieve_flow_path()`

**Retrieve a call's flow path.** The flow nodes the call went through, in order, with the edge taken into each. Calls without a flow answer `flow_id: null` and no steps.

```python
# client.calls
def retrieve_flow_path(self, id: str, *, more_voice_version: str | Unset = UNSET) -> _m.FlowPath
```

`client.calls.retrieve_flow_path()` · `await async_client.calls.retrieve_flow_path()` · `GET /calls/{id}/flow_path` · [API reference](https://docs.morevoice.ai/api/operations/calls_retrieve_flow_path/)

### Parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | `str` | yes | A call ID (`call_…`). |

### Returns

`FlowPath`:

| Field | Type | Description |
| --- | --- | --- |
| `object` | `"flow_path"` | Always `flow_path`. |
| `call_id` | `string` | A call ID (prefix `call_`). |
| `flow_id` | `string \| null` | null when no flow ran on the call. |
| `flow_version` | `integer \| null` | — |
| `steps` | `FlowPathStep[]` | — |
| `end` | `string \| null` | How the flow ended (an end node, a transfer, a hang-up). |
| `transferred` | `object \| null` | — |
| `live` | `boolean` | The call is still running: more steps may follow. |

### Example

```python
from morevoice import MoreVoice

client = MoreVoice()  # MOREVOICE_API_KEY from the environment

flow_path = client.calls.retrieve_flow_path("call_7Hk2Lm9Qp")
print(flow_path)
```

## `retrieve_pci()`

**Retrieve a call's payment pause.** Returns the `CallPci` object. Answers `404` with the code `resource_missing` when nothing has this ID in this organisation and mode.

```python
# client.calls
def retrieve_pci(self, id: str, *, more_voice_version: str | Unset = UNSET) -> _m.CallPci
```

`client.calls.retrieve_pci()` · `await async_client.calls.retrieve_pci()` · `GET /calls/{id}/pci` · [API reference](https://docs.morevoice.ai/api/operations/calls_retrieve_pci/)

### Parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | `str` | yes | A call ID (`call_…`). |

### Returns

`CallPci`:

| Field | Type | Description |
| --- | --- | --- |
| `object` | `"call_pci"` | Always `call_pci`. |
| `call_id` | `string` | A call ID (prefix `call_`). |
| `paused` | `boolean` | Recording and transcription are paused for a payment. |
| `reason` | `string \| null` | Why (e.g. `payment`), while paused. |
| `paused_at` | `string \| null` | — |
| `resumes_at` | `string \| null` | When the pause ends by itself unless resumed earlier. |
| `destination_participant_id` | `string \| null` | The participant the pause named as the payment line, if any (otherwise a participant on one of the organisation's payment numbers). |

### Example

```python
from morevoice import MoreVoice

client = MoreVoice()  # MOREVOICE_API_KEY from the environment

call_pci = client.calls.retrieve_pci("call_7Hk2Lm9Qp")
print(call_pci)
```

## `retrieve_recording()`

**Retrieve a call's recording.** Redirects (302) to a signed URL that streams the recording for 15 minutes, with no API key. With `format=json`, answers that URL as a `recording_link` object instead. The recording is stereo: the customer on the left channel, the assistant or agent on the right.

```python
# client.calls
def retrieve_recording(self, id: str, *, format_: _m.CallsRetrieveRecordingFormat | Unset = "redirect", more_voice_version: str | Unset = UNSET) -> _m.RecordingLink
```

`client.calls.retrieve_recording()` · `await async_client.calls.retrieve_recording()` · `GET /calls/{id}/recording` · [API reference](https://docs.morevoice.ai/api/operations/calls_retrieve_recording/)

### Parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | `str` | yes | A call ID (`call_…`). |
| `format` | `"redirect" \| "json"` | no | redirect (default): 302 to the signed URL. json: the signed URL as a recording_link object. |

### Returns

`RecordingLink`:

| Field | Type | Description |
| --- | --- | --- |
| `object` | `"recording_link"` | Always `recording_link`. |
| `call_id` | `string` | A call ID (prefix `call_`). |
| `url` | `string` | A signed URL that streams the audio without an API key. It expires (expires_at). |
| `expires_at` | `string` | An ISO-8601 timestamp in UTC. |
| `content_type` | `string` | audio/wav, audio/ogg, audio/webm or audio/mpeg. |
| `duration_ms` | `integer \| null` | — |
| `channels` | `integer` | 2: the customer on the left channel, the assistant or agent on the right. |

### Example

```python
from morevoice import MoreVoice

client = MoreVoice()  # MOREVOICE_API_KEY from the environment

recording_link = client.calls.retrieve_recording("call_7Hk2Lm9Qp")
print(recording_link)
```

## `retrieve_summary()`

**Retrieve a call's summary.** The AI summary written when the call ended (when the assistant's summary artefact is on), or the last one created with POST.

```python
# client.calls
def retrieve_summary(self, id: str, *, more_voice_version: str | Unset = UNSET) -> _m.CallSummary
```

`client.calls.retrieve_summary()` · `await async_client.calls.retrieve_summary()` · `GET /calls/{id}/summary` · [API reference](https://docs.morevoice.ai/api/operations/calls_retrieve_summary/)

### Parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | `str` | yes | A call ID (`call_…`). |

### Returns

`CallSummary`:

| Field | Type | Description |
| --- | --- | --- |
| `object` | `"call_summary"` | Always `call_summary`. |
| `call_id` | `string` | A call ID (prefix `call_`). |
| `text` | `string` | A two-to-four sentence summary, in the language of the call. |
| `intent` | `string` | What the caller wanted. |
| `outcome` | `string` | How the call ended, or what was agreed. |
| `sentiment` | `"positive" \| "neutral" \| "negative" \| "mixed"` | — |
| `key_points` | `string[]` | — |
| `action_items` | `string[]` | Follow-ups for your business; empty when there are none. |
| `generated_at` | `string \| null` | — |

### Example

```python
from morevoice import MoreVoice

client = MoreVoice()  # MOREVOICE_API_KEY from the environment

call_summary = client.calls.retrieve_summary("call_7Hk2Lm9Qp")
print(call_summary)
```

## `retrieve_transcript()`

**Retrieve a call's transcript.** Everything said on the call, in order, with the speaker of each segment. While the call runs, the transcript so far.

```python
# client.calls
def retrieve_transcript(self, id: str, *, more_voice_version: str | Unset = UNSET) -> _m.Transcript
```

`client.calls.retrieve_transcript()` · `await async_client.calls.retrieve_transcript()` · `GET /calls/{id}/transcript` · [API reference](https://docs.morevoice.ai/api/operations/calls_retrieve_transcript/)

### Parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | `str` | yes | A call ID (`call_…`). |

### Returns

`Transcript`:

| Field | Type | Description |
| --- | --- | --- |
| `object` | `"transcript"` | Always `transcript`. |
| `call_id` | `string` | A call ID (prefix `call_`). |
| `language` | `string \| null` | The call's main language (BCP 47), when known. |
| `segments` | `TranscriptSegment[]` | — |

### Example

```python
from morevoice import MoreVoice

client = MoreVoice()  # MOREVOICE_API_KEY from the environment

transcript = client.calls.retrieve_transcript("call_7Hk2Lm9Qp")
print(transcript)
```

## `send_dtmf()`

**Send DTMF tones.** Calls handled by a human agent: plays the keys to the far end (for example to navigate another company's IVR).

```python
# client.calls
def send_dtmf(self, id: str, body: _m.CallsSendDtmfBody | Mapping[str, Any], *, more_voice_version: str | Unset = UNSET, idempotency_key: str | Unset = UNSET) -> _m.CallControl
```

`client.calls.send_dtmf()` · `await async_client.calls.send_dtmf()` · `POST /calls/{id}/dtmf` · [API reference](https://docs.morevoice.ai/api/operations/calls_send_dtmf/)

### Parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | `str` | yes | A call ID (`call_…`). |

### Request body

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `digits` | `string` | yes | The keys to send to the far end; `,` pauses. |

### Returns

`CallControl`:

| Field | Type | Description |
| --- | --- | --- |
| `object` | `"call_control"` | Always `call_control`. |
| `call_id` | `string` | A call ID (prefix `call_`). |
| `status` | `CallStatus` | `queued` (placed, not ringing yet), `ringing`, `in_progress` (answered, or an inbound call being handled) or `ended`. |
| `type` | `CallType` | Who handles the call: an AI assistant, a human agent, an IVR flow, a conference room, or a voicemail box. |
| `phase` | `"active" \| "held" \| "consulting" \| "conference"` | — |
| `held` | `boolean` | The customer is on hold. |
| `consult` | `object \| null` | A running consultation (warm transfer). |
| `participants` | `CallParticipant[]` | — |
| `recording` | `boolean` | — |

### Example

```python
from morevoice import MoreVoice

client = MoreVoice()  # MOREVOICE_API_KEY from the environment

call_control = client.calls.send_dtmf("call_7Hk2Lm9Qp", {
    "digits": "1234#",
})
print(call_control)
```

## `stream()`

**Stream a call's live events.** Server-Sent Events for one call: `call.state_changed`, `transcript.partial`, `transcript.final`, `call.tool_called`, and `call.ended`, after which the stream closes. The stream starts with what already happened on the call. Reconnect with `Last-Event-ID` (EventSource does it for you) to get only what you missed. An ended call's stream sends `call.ended` at once. A call that has not started yet (a web call before the browser connects) is a 404: subscribe once it starts, or follow it on GET /v1/events/stream. Counts against the key's stream limit (`concurrency_limit`, `limit_type: api_streams`).

`client.calls.stream()` · `await async_client.calls.stream()` · `GET /calls/{id}/events` · [API reference](https://docs.morevoice.ai/api/operations/calls_stream/)

### Parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | `str` | yes | A call ID (`call_…`). |
| `types` | `string \| string[]` | no | Only these event types (comma-separated or repeated): `call.state_changed`, `transcript.partial`, `transcript.final`, `call.tool_called`, `call.ended`, `transcript.*`, `call.*` or `*`. Default: all of them. |
| `last_event_id` | `string` | no | Resume after this event, for clients that cannot send the `Last-Event-ID` header (the header wins when both are sent). |

### Returns

A stream of events (Server-Sent Events): iterate it, or `async for` it on `AsyncMoreVoice`.

### Example

```python
import os

import httpx

url = "https://api.morevoice.ai/v1/calls/call_7Hk2Lm9Qp/events"
headers = {"Authorization": f"Bearer {os.environ['MOREVOICE_API_KEY']}", "Accept": "text/event-stream"}
with httpx.stream("GET", url, headers=headers, timeout=None) as response:
    response.raise_for_status()
    for line in response.iter_lines():
        if line.startswith("data:"):
            print(line[5:].strip())
```

## `transcript_stream()`

**Stream a call's live transcript.** Server-Sent Events with what is said on a call as it is said: `transcript.partial` while a line is spoken (its text so far) and `transcript.final` when it is complete. A final supersedes the partials with the same `utterance_id`. `speaker` is `customer` (the caller), `assistant` (the AI), `agent` (a human agent), `supervisor` or `external` (a transfer party); AI and human calls alike. `start_ms` and `end_ms` are when the line began and became final, in milliseconds from the call's start, as observed live (null for lines spoken before the stream connected; the call's transcript has the exact times after the call). The stream starts with the conversation so far and ends with `call.ended`. Reconnect with `Last-Event-ID` to get only what you missed. Send `partials=false` for complete lines only. Counts against the key's stream limit (`concurrency_limit`, `limit_type: api_streams`).

`client.calls.transcript_stream()` · `await async_client.calls.transcript_stream()` · `GET /calls/{id}/transcript/stream` · [API reference](https://docs.morevoice.ai/api/operations/calls_transcript_stream/)

### Parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | `str` | yes | A call ID (`call_…`). |
| `partials` | `"true" \| "false"` | no | `false`: only complete lines (`transcript.final`), no partials. Default `true`. |
| `last_event_id` | `string` | no | Resume after this event, for clients that cannot send the `Last-Event-ID` header (the header wins when both are sent). |

### Returns

A stream of events (Server-Sent Events): iterate it, or `async for` it on `AsyncMoreVoice`.

### Example

```python
import os

import httpx

url = "https://api.morevoice.ai/v1/calls/call_7Hk2Lm9Qp/transcript/stream"
headers = {"Authorization": f"Bearer {os.environ['MOREVOICE_API_KEY']}", "Accept": "text/event-stream"}
with httpx.stream("GET", url, headers=headers, timeout=None) as response:
    response.raise_for_status()
    for line in response.iter_lines():
        if line.startswith("data:"):
            print(line[5:].strip())
```

## `transfer()`

**Transfer a call.** AI and IVR calls: to a queue or a phone number (warm: `note` is whispered to the person who answers). Calls handled by a human agent: to a queue, a number, an AI assistant or another agent, cold or warm (warm: the agent consults the target first; finish with POST …/consult/complete). Returns the call's control state.

```python
# client.calls
def transfer(self, id: str, body: _m.CallsTransferBody | Mapping[str, Any], *, more_voice_version: str | Unset = UNSET, idempotency_key: str | Unset = UNSET) -> _m.CallControl
```

`client.calls.transfer()` · `await async_client.calls.transfer()` · `POST /calls/{id}/transfer` · [API reference](https://docs.morevoice.ai/api/operations/calls_transfer/)

### Parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | `str` | yes | A call ID (`call_…`). |

### Request body

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `to` | `object` | yes | Where to: a queue, a phone number, an AI assistant or a human agent. |
| `mode` | `"cold" \| "warm"` | no | cold: hand the call over. warm: talk to the target first (human calls), or whisper `note` to them (AI calls). |
| `note` | `string` | no | Context for the target: shown with the queued call, whispered on a warm transfer. |

### Returns

`CallControl`:

| Field | Type | Description |
| --- | --- | --- |
| `object` | `"call_control"` | Always `call_control`. |
| `call_id` | `string` | A call ID (prefix `call_`). |
| `status` | `CallStatus` | `queued` (placed, not ringing yet), `ringing`, `in_progress` (answered, or an inbound call being handled) or `ended`. |
| `type` | `CallType` | Who handles the call: an AI assistant, a human agent, an IVR flow, a conference room, or a voicemail box. |
| `phase` | `"active" \| "held" \| "consulting" \| "conference"` | — |
| `held` | `boolean` | The customer is on hold. |
| `consult` | `object \| null` | A running consultation (warm transfer). |
| `participants` | `CallParticipant[]` | — |
| `recording` | `boolean` | — |

### Example

```python
from morevoice import MoreVoice

client = MoreVoice()  # MOREVOICE_API_KEY from the environment

call_control = client.calls.transfer("call_7Hk2Lm9Qp", {
    "mode": "warm",
    "note": "VIP customer, order A-1042",
    "to": {
        "number": "+97235551234",
    },
})
print(call_control)
```

## `update_participant()`

**Mute, hold or change the role of a participant.** Changes one participant of a call handled by a human agent: mute or unmute them (`muted`), put them on hold or take them off (`held`), or change their `role`. Send only what changes. The answer is the call's control state after the change. AI and IVR calls answer `400` with the code `unsupported_for_call_mode`.

```python
# client.calls
def update_participant(self, id: str, participant_id: str, body: _m.CallsUpdateParticipantBody | Mapping[str, Any] | Unset = UNSET, *, more_voice_version: str | Unset = UNSET, idempotency_key: str | Unset = UNSET) -> _m.CallControl
```

`client.calls.update_participant()` · `await async_client.calls.update_participant()` · `PATCH /calls/{id}/participants/{participant_id}` · [API reference](https://docs.morevoice.ai/api/operations/calls_update_participant/)

### Parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | `str` | yes | A call ID (`call_…`). |
| `participant_id` | `str` | yes | A participant_id from the call's control state. |

### Request body

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `held` | `boolean` | no | — |
| `muted` | `boolean` | no | — |
| `role` | `"moderator" \| "member"` | no | — |

### Returns

`CallControl`:

| Field | Type | Description |
| --- | --- | --- |
| `object` | `"call_control"` | Always `call_control`. |
| `call_id` | `string` | A call ID (prefix `call_`). |
| `status` | `CallStatus` | `queued` (placed, not ringing yet), `ringing`, `in_progress` (answered, or an inbound call being handled) or `ended`. |
| `type` | `CallType` | Who handles the call: an AI assistant, a human agent, an IVR flow, a conference room, or a voicemail box. |
| `phase` | `"active" \| "held" \| "consulting" \| "conference"` | — |
| `held` | `boolean` | The customer is on hold. |
| `consult` | `object \| null` | A running consultation (warm transfer). |
| `participants` | `CallParticipant[]` | — |
| `recording` | `boolean` | — |

### Example

```python
from morevoice import MoreVoice

client = MoreVoice()  # MOREVOICE_API_KEY from the environment

call_control = client.calls.update_participant("call_7Hk2Lm9Qp", "participant_id_123", {
    "muted": True,
})
print(call_control)
```
