Skip to content

Voice

create_voice_agent(*, api, settings, payload)

Create a voice agent. REQUIRED: name, config; project_id is filled in automatically.

config REQUIRES instructions and a mode, and the mode decides which stages it carries:

pipeline (default) -> stt, llm and tts, all three, and NO realtime realtime -> realtime alone, and none of stt/llm/tts

Each stage is {"provider", "model", "connection_id", ...}; connection_id is a Nexus connection UUID and the agent cannot run without it. Optional: welcome_message, tools, mcp_servers, transfer, end_call_enabled, recording_enabled, advanced.

Creating does not deploy. Nothing can call the agent until voice deploy.

Source code in dynamiq/cli/commands/voice.py
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
@voice.command("create")
@click.argument("payload")
@with_api_and_settings
def create_voice_agent(*, api: ApiClient, settings: Settings, payload: str):
    """Create a voice agent. REQUIRED: `name`, `config`; `project_id` is filled in automatically.

    `config` REQUIRES `instructions` and a `mode`, and the mode decides which stages it carries:

      pipeline (default) -> `stt`, `llm` and `tts`, all three, and NO `realtime`
      realtime           -> `realtime` alone, and none of stt/llm/tts

    Each stage is {"provider", "model", "connection_id", ...}; `connection_id` is a Nexus
    connection UUID and the agent cannot run without it. Optional: `welcome_message`, `tools`,
    `mcp_servers`, `transfer`, `end_call_enabled`, `recording_enabled`, `advanced`.

    Creating does not deploy. Nothing can call the agent until `voice deploy`.
    """
    body = read_json_arg(payload)
    body.setdefault("project_id", require_project(settings))
    echo_response(api.post(f"{BASE}/agents", json=body))

delete_voice_agent(*, api, settings, agent_id)

Delete the agent permanently.

Source code in dynamiq/cli/commands/voice.py
177
178
179
180
181
182
183
@voice.command("delete")
@click.argument("agent_id")
@click.confirmation_option(prompt="Delete this voice agent?")
@with_api_and_settings
def delete_voice_agent(*, api: ApiClient, settings: Settings, agent_id: str):
    """Delete the agent permanently."""
    echo_response(api.delete(f"{BASE}/agents/{agent_id}"))

deploy_voice_agent(*, api, settings, agent_id)

Deploy the agent so it can take calls.

Returns before it is live. Poll voice get <id> until status settles; a failure lands in deploy_error on the agent, which is the first thing to read when a deploy does not take.

Source code in dynamiq/cli/commands/voice.py
79
80
81
82
83
84
85
86
87
88
@voice.command("deploy")
@click.argument("agent_id")
@with_api_and_settings
def deploy_voice_agent(*, api: ApiClient, settings: Settings, agent_id: str):
    """Deploy the agent so it can take calls.

    Returns before it is live. Poll `voice get <id>` until `status` settles; a failure lands in
    `deploy_error` on the agent, which is the first thing to read when a deploy does not take.
    """
    echo_response(api.post(f"{BASE}/agents/{agent_id}/deploy"))

duplicate_voice_agent(*, api, settings, agent_id)

Copy an agent, config and all - the safe way to try a change on a live one.

Source code in dynamiq/cli/commands/voice.py
169
170
171
172
173
174
@voice.command("duplicate")
@click.argument("agent_id")
@with_api_and_settings
def duplicate_voice_agent(*, api: ApiClient, settings: Settings, agent_id: str):
    """Copy an agent, config and all - the safe way to try a change on a live one."""
    echo_response(api.post(f"{BASE}/agents/{agent_id}/duplicate"))

get_call(*, api, settings, agent_id, call_id)

One call, including its transcript.

Source code in dynamiq/cli/commands/voice.py
116
117
118
119
120
121
122
@voice.command("call")
@click.argument("agent_id")
@click.argument("call_id")
@with_api_and_settings
def get_call(*, api: ApiClient, settings: Settings, agent_id: str, call_id: str):
    """One call, including its transcript."""
    echo_response(api.get(f"{BASE}/agents/{agent_id}/calls/{call_id}"))

get_recording(*, api, settings, agent_id, call_id)

A short-lived URL for the call recording.

Treat it like a connect link: give it to the user, do not open it yourself.

Source code in dynamiq/cli/commands/voice.py
125
126
127
128
129
130
131
132
133
134
@voice.command("recording")
@click.argument("agent_id")
@click.argument("call_id")
@with_api_and_settings
def get_recording(*, api: ApiClient, settings: Settings, agent_id: str, call_id: str):
    """A short-lived URL for the call recording.

    Treat it like a connect link: give it to the user, do not open it yourself.
    """
    echo_response(api.get(f"{BASE}/agents/{agent_id}/calls/{call_id}/recording-url"))

get_telephony(*, api, settings, agent_id)

Phone numbers and SIP trunks attached to this agent.

Source code in dynamiq/cli/commands/voice.py
137
138
139
140
141
142
@voice.command("telephony")
@click.argument("agent_id")
@with_api_and_settings
def get_telephony(*, api: ApiClient, settings: Settings, agent_id: str):
    """Phone numbers and SIP trunks attached to this agent."""
    echo_response(api.get(f"{BASE}/agents/{agent_id}/telephony"))

get_voice_agent(*, api, settings, agent_id)

Fetch one voice agent - status, deploy_error and phone_numbers live here.

Source code in dynamiq/cli/commands/voice.py
57
58
59
60
61
62
@voice.command("get")
@click.argument("agent_id")
@with_api_and_settings
def get_voice_agent(*, api: ApiClient, settings: Settings, agent_id: str):
    """Fetch one voice agent - `status`, `deploy_error` and `phone_numbers` live here."""
    echo_response(api.get(f"{BASE}/agents/{agent_id}"))

list_calls(*, api, settings, agent_id, page, page_size, fetch_all, compact)

Calls this agent has handled - the proof it is actually working.

Source code in dynamiq/cli/commands/voice.py
107
108
109
110
111
112
113
@voice.command("calls")
@click.argument("agent_id")
@pagination_options
@with_api_and_settings
def list_calls(*, api: ApiClient, settings: Settings, agent_id: str, page, page_size, fetch_all, compact):
    """Calls this agent has handled - the proof it is actually working."""
    echo_list(api, f"{BASE}/agents/{agent_id}/calls", None, page, page_size, fetch_all, compact)

list_simulations(*, api, settings, agent_id, page, page_size, fetch_all, compact)

Simulation runs and their results.

Source code in dynamiq/cli/commands/voice.py
160
161
162
163
164
165
166
@voice.command("simulations")
@click.argument("agent_id")
@pagination_options
@with_api_and_settings
def list_simulations(*, api: ApiClient, settings: Settings, agent_id: str, page, page_size, fetch_all, compact):
    """Simulation runs and their results."""
    echo_list(api, f"{BASE}/agents/{agent_id}/simulations", None, page, page_size, fetch_all, compact)

list_voice_agents(*, api, settings, page, page_size, fetch_all, compact)

List voice agents in the current project.

Source code in dynamiq/cli/commands/voice.py
27
28
29
30
31
32
@voice.command("list")
@pagination_options
@with_api_and_settings
def list_voice_agents(*, api: ApiClient, settings: Settings, page, page_size, fetch_all, compact):
    """List voice agents in the current project."""
    echo_list(api, f"{BASE}/agents", {"project_id": require_project(settings)}, page, page_size, fetch_all, compact)

simulate(*, api, settings, agent_id, payload)

Run a simulated call against the agent - a test that needs no phone.

Use this as the "does it work" gate before telling anyone the agent is ready.

Source code in dynamiq/cli/commands/voice.py
145
146
147
148
149
150
151
152
153
154
155
156
157
@voice.command("simulate")
@click.argument("agent_id")
@click.argument("payload")
@with_api_and_settings
def simulate(*, api: ApiClient, settings: Settings, agent_id: str, payload: str):
    """Run a simulated call against the agent - a test that needs no phone.

    Use this as the "does it work" gate before telling anyone the agent is ready.
    """
    # Each attempt creates a simulation. A retry after a timed-out response leaves a second
    # record behind, and there is no idempotency key to tell them apart.
    echo_response(api.post(f"{BASE}/agents/{agent_id}/simulations", json=read_json_arg(payload),
                           timeout=EXECUTION_TIMEOUT))

undeploy_voice_agent(*, api, settings, agent_id)

Take the agent offline. Calls to it stop being answered.

Source code in dynamiq/cli/commands/voice.py
91
92
93
94
95
96
@voice.command("undeploy")
@click.argument("agent_id")
@with_api_and_settings
def undeploy_voice_agent(*, api: ApiClient, settings: Settings, agent_id: str):
    """Take the agent offline. Calls to it stop being answered."""
    echo_response(api.post(f"{BASE}/agents/{agent_id}/undeploy"))

update_voice_agent(*, api, settings, agent_id, payload)

Partially update a voice agent: send only the fields to change.

Accepted fields: name, description, config. config is replaced as a whole, so send the complete config (take it from voice get), not just the keys that change. A deployed agent needs redeploying to pick up the change.

Source code in dynamiq/cli/commands/voice.py
65
66
67
68
69
70
71
72
73
74
75
76
@voice.command("update")
@click.argument("agent_id")
@click.argument("payload")
@with_api_and_settings
def update_voice_agent(*, api: ApiClient, settings: Settings, agent_id: str, payload: str):
    """Partially update a voice agent: send only the fields to change.

    Accepted fields: `name`, `description`, `config`. `config` is replaced as a whole, so send
    the complete config (take it from `voice get`), not just the keys that change. A deployed
    agent needs redeploying to pick up the change.
    """
    echo_response(api.patch(f"{BASE}/agents/{agent_id}", json=read_json_arg(payload)))

voice_catalog(*, api, settings)

Providers, models and voices this platform can serve.

Run this BEFORE writing a config: every provider/model/voice in a voice agent comes from here, and a name that is merely plausible fails at deploy time rather than at create.

Source code in dynamiq/cli/commands/voice.py
16
17
18
19
20
21
22
23
24
@voice.command("catalog")
@with_api_and_settings
def voice_catalog(*, api: ApiClient, settings: Settings):
    """Providers, models and voices this platform can serve.

    Run this BEFORE writing a config: every `provider`/`model`/`voice` in a voice agent comes
    from here, and a name that is merely plausible fails at deploy time rather than at create.
    """
    echo_response(api.get(f"{BASE}/catalog"))

voice_deployments(*, api, settings, agent_id)

Deployment history for a voice agent.

Source code in dynamiq/cli/commands/voice.py
 99
100
101
102
103
104
@voice.command("deployments")
@click.argument("agent_id")
@with_api_and_settings
def voice_deployments(*, api: ApiClient, settings: Settings, agent_id: str):
    """Deployment history for a voice agent."""
    echo_response(api.get(f"{BASE}/agents/{agent_id}/deployments"))