Skip to content

client.calls

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). 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 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.

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

Name Type Required Description
id str yes A call ID (call_…).
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.

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" —
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)

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.

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

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.

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 —
from morevoice import MoreVoice
client = MoreVoice() # MOREVOICE_API_KEY from the environment
call_control = client.calls.consult("call_7Hk2Lm9Qp", "complete", {})
print(call_control)

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.

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

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.

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).
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)

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.

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

Name Type Required Description
id str yes A call ID (call_…).
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).

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.
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)

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.

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

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

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 —
from morevoice import MoreVoice
client = MoreVoice() # MOREVOICE_API_KEY from the environment
call_summary = client.calls.create_summary("call_7Hk2Lm9Qp", {})
print(call_summary)

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.

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

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

CallDeleted:

Field Type Description
id string A call ID (prefix call_).
object "call" Always call.
deleted true —
from morevoice import MoreVoice
client = MoreVoice() # MOREVOICE_API_KEY from the environment
call_deleted = client.calls.delete("call_7Hk2Lm9Qp")
print(call_deleted)

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.

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

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

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.
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 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.

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

client.calls.download_recording() · await async_client.calls.download_recording() · GET /recordings/{token} · API reference

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

Nothing, on success.

from morevoice import MoreVoice
client = MoreVoice() # MOREVOICE_API_KEY from the environment
data = client.calls.download_recording("token_123")
print(len(data), "bytes")

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.

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

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

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).
from morevoice import MoreVoice
client = MoreVoice() # MOREVOICE_API_KEY from the environment
call = client.calls.hangup("call_7Hk2Lm9Qp", {})
print(call)

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.

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

Name Type Required Description
id str yes A call ID (call_…).
Field Type Required Description
on boolean no true: put the customer on hold; false: take them off hold.

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 —
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 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]…).

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

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.

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

from morevoice import MoreVoice
client = MoreVoice() # MOREVOICE_API_KEY from the environment
for call in client.calls.list():
print(call)

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.

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

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).

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

from morevoice import MoreVoice
client = MoreVoice() # MOREVOICE_API_KEY from the environment
for call in client.calls.list_copilot_events("call_7Hk2Lm9Qp"):
print(call)

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.

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

Name Type Required Description
id str yes A call ID (call_…).
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).

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).
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 a participant. Drops a participant from the call. The customer (client) cannot be removed: hang up instead.

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

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

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 —
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 a call. A call, live or ended. While it runs, status and answered_at come from the live call.

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

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

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).
from morevoice import MoreVoice
client = MoreVoice() # MOREVOICE_API_KEY from the environment
call = client.calls.retrieve("call_7Hk2Lm9Qp")
print(call)

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

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

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

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 —
from morevoice import MoreVoice
client = MoreVoice() # MOREVOICE_API_KEY from the environment
call_control = client.calls.retrieve_control("call_7Hk2Lm9Qp")
print(call_control)

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.

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

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

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.
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 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.

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

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

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).
from morevoice import MoreVoice
client = MoreVoice() # MOREVOICE_API_KEY from the environment
call_pci = client.calls.retrieve_pci("call_7Hk2Lm9Qp")
print(call_pci)

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.

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

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.

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.
from morevoice import MoreVoice
client = MoreVoice() # MOREVOICE_API_KEY from the environment
recording_link = client.calls.retrieve_recording("call_7Hk2Lm9Qp")
print(recording_link)

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.

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

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

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 —
from morevoice import MoreVoice
client = MoreVoice() # MOREVOICE_API_KEY from the environment
call_summary = client.calls.retrieve_summary("call_7Hk2Lm9Qp")
print(call_summary)

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.

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

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

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[] —
from morevoice import MoreVoice
client = MoreVoice() # MOREVOICE_API_KEY from the environment
transcript = client.calls.retrieve_transcript("call_7Hk2Lm9Qp")
print(transcript)

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

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

Name Type Required Description
id str yes A call ID (call_…).
Field Type Required Description
digits string yes The keys to send to the far end; , pauses.

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 —
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 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

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).

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

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())

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

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).

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

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 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.

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

Name Type Required Description
id str yes A call ID (call_…).
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.

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 —
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)

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.

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

Name Type Required Description
id str yes A call ID (call_…).
participant_id str yes A participant_id from the call’s control state.
Field Type Required Description
held boolean no —
muted boolean no —
role "moderator" | "member" no —

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 —
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)