Skip to main content

Overview

Use answer with assistant.id when an AI assistant should handle an inbound call from pickup. Telnyx attempts to prepare the assistant before answering, reducing the work left to do after pickup. Use dial with assistant.id (or TeXML POST /v2/texml/ai_calls/{connection_id}) to place an outbound call that an assistant handles from pickup, warming up the assistant before the call is placed. Use ai_assistant_start to attach an assistant to a call that is already answered, such as after an IVR or human interaction. This is different from Gather using AI, which is purpose-built for collecting structured data. ai_assistant_start is for open-ended, conversational AI experiences.

Prerequisites

Once you have an assistant, note its id (format: assistant-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx).

Answer an inbound call with an assistant

For an inbound call that is still unanswered, send POST /v2/calls/{call_control_id}/actions/answer with the assistant configuration in the request body:
Only assistant.id is required within the assistant object; the call uses the stored assistant’s configuration. To change settings for this call only, send per-call overrides alongside the ID — for example:
Omitted fields use the stored assistant’s values. Telnyx Ultra voice IDs are UUIDs — copy the exact ID from the voice library. See the Answer call reference for the complete request schema. The call follows this sequence:
  1. Telnyx accepts the command and attempts to warm up the assistant configuration and dependencies while the call remains unanswered.
  2. Telnyx answers the call and emits call.answered. The HTTP success response can arrive before this webhook.
  3. The assistant starts automatically on the answered call. Do not send a second ai_assistant_start command from the call.answered handler.
If the assistant cannot be started after the call is answered — for example, assistant.id does not match an existing assistant — Telnyx sends call.conversation.start_failed instead of call.conversation.created. The call stays answered, so handle this webhook to continue the call, for example by transferring it or hanging up. If the application first rings a business’s phones, send this combined answer request when the no-answer timeout expires and the assistant should take over. The caller’s inbound leg must still be unanswered. Answering that leg earlier and starting the assistant later cannot move preparation back before pickup.

Get the conversation ID

The answer HTTP response does not include conversation_id. Handle the call.conversation.created webhook and store data.payload.conversation_id alongside data.payload.call_control_id. Telnyx sends this event to the configured Call Control webhook URL during assistant startup; no send_message_history_updates setting is required. Example payload fields:
The event identifies the created conversation. It does not confirm that the assistant is ready to speak or that the greeting has started.

Reuse one assistant with per-call settings

A single stored assistant can supply defaults for many businesses or call-by-call experiments. Send each call’s greeting, instructions, tools, voice, and transcription overrides inside assistant; a separate stored assistant per voice or business is not required. When migrating from ai_assistant_start, use these locations in the answer request: The two commands use different voice-settings schemas. Select the supported assistant voice options from the Answer call reference rather than copying the start command’s entire voice_settings object unchanged. Omitted assistant fields retain stored values. A supplied voice_settings or transcription object replaces that entire stored object: include all settings that the call needs, including provider credentials where applicable. dynamic_variables merge with stored variables, with request values taking precedence. The top-level answer.transcription boolean controls standalone call transcription; it does not configure assistant speech recognition.

Understand and measure the remaining delay

Warm-up moves preparation before answer; it does not guarantee a shorter total time from the incoming call to the greeting. It does not wait for greeting audio to be synthesized or guarantee zero silence after pickup. If warm-up fails, Telnyx answers and starts the assistant without the warmed configuration. Compare the same voice and assistant configuration using the time from actual answer to the first audible greeting. Also track the total time from the incoming call to the greeting so that a later answer is visible in the results. Use call.answered for the answer event, rather than the HTTP response time, and verify the greeting onset from call audio. A playback-start event alone does not establish that speech was audible; the playback may contain silence.

Place an outbound call with an assistant

For an outbound call that an AI assistant should handle from the moment the called party picks up, send POST /v2/calls with the destination, your Call Control application’s connection_id, and the assistant configuration in the request body:
Only assistant.id is required within the assistant object; the call uses the stored assistant’s configuration. To change settings for this call only, send per-call overrides alongside the ID, with the same merge rules as the inbound answer request — for example a different greeting and voice:
See the Dial call reference for the complete request schema. The call follows this sequence:
  1. Telnyx accepts the command and attempts to warm up the assistant configuration and dependencies before placing the call.
  2. The called party answers and Telnyx emits call.answered.
  3. The assistant starts automatically on the answered call. Do not send ai_assistant_start from the call.answered handler — the call already has the assistant attached.
Obtain the conversation_id from the call.conversation.created webhook, as with inbound calls; the dial response does not include it. If the assistant cannot be started after the called party answers, Telnyx sends call.conversation.start_failed instead, and the call stays connected. If warm-up fails, the call is still placed and the assistant starts after answer without the warmed configuration.

Outbound with TeXML

The POST /v2/texml/ai_calls/{connection_id} endpoint places the same assistant-backed outbound call from a TeXML application, warming up the assistant before the call is placed:
AIAssistantDynamicVariables passes per-call variables and AIAssistantVersion pins a specific assistant version. If the assistant’s voicemail detection is configured, include the MachineDetection parameters as shown in the Initiate an outbound AI call reference. The <AIAssistant> TeXML verb attaches an assistant to a call leg that is already on a TeXML call. When a still-ringing leg’s instructions begin with <Connect><AIAssistant/></Connect>, TeXML answers the leg and starts the assistant in one step, with the assistant warmed up while the leg is still ringing. See the <AIAssistant> verb documentation.

Reduce the silence before the greeting

Warm-up moves preparation before answer; the greeting still only starts once media is connected after answer. To keep the called party’s wait short:
  • Prefer dial with assistant.id (or POST /v2/texml/ai_calls/{connection_id}) over dial-then-ai_assistant_start-on-call.answered: the two-step flow performs all startup after the called party answered, and the webhook handler’s round trip adds to the silence the called party hears.
  • Use a static greeting rather than <assistant-speaks-first-with-model-generated-message>, which puts the LLM’s first token in the critical path before any audio.
  • Keep the stored assistant’s dynamic-variables webhook fast if one is configured: it runs during warm-up on every call, and a slow endpoint delays every greeting.
  • Measure from call.answered (or the TeXML answered status callback) to the first audible greeting in the call audio, not from your API request, and keep total time from request to greeting visible so a longer ringing phase is not hidden.

Start an AI Assistant on an answered call

Send a POST request to ai_assistant_start with the call_control_id of the active call:
That’s it. The assistant is now live on the call.

Webhooks

The assistant emits the following lifecycle webhooks:
When the AI conversation ends — the assistant stops itself at its time_limit_secs or user_idle_timeout_secs, or you call ai_assistant_stop — the call is not hung up by the assistant stop itself. What happens to the call depends on how the assistant is running on it:
  • Call Control (ai_assistant_start): the call leg stays connected and continues to be billed until you hang it up with the Hangup command, the far end hangs up, or the call reaches the platform’s maximum call duration. Handle call.conversation.ended and decide what happens next: hang up the leg, transfer the caller, or keep the call open for your own Call Control flow.
  • TeXML (<Connect><AIAssistant>): the assistant is the TeXML instruction currently executing, and stopping it ends that instruction. In the default TeXML setup for assistants there is nothing after the assistant command — the served document is just <Connect><AIAssistant id="…"/></Connect> — so with no further instructions to execute, the call is hung up when the assistant stops. If you need the call to continue, either put TeXML instructions after </Connect> — the following instructions execute when the service stops — or set the action attribute on <Connect> to a URL that returns the next TeXML instructions (for example a <Dial> to a human agent); the call is hung up only when neither continues the flow.

Stream Message History Updates

By default you only learn what was said once the conversation ends. Set send_message_history_updates to true on ai_assistant_start to receive a call.ai_gather.message_history_updated webhook every time the conversation history changes — useful for live transcripts, agent-assist screens, or supervisor dashboards.
Each webhook carries the full message history so far, not just the new message:

Control It From the Assistant Instead

The same setting exists on the assistant itself, as telephony_settings.send_message_history_updates. When it is set, it overrides whatever the start command asked for, so an assistant can turn the webhooks on for every call it takes — or opt out of them entirely:
Messages exchanged privately with a transfer destination during warm transfer acceptance are never included in these webhooks unless the transfer tool is configured with end_user_target_context_mode: "shared".

Include the Full History in call.conversation.ended

By default, the messages array of the call.conversation.ended webhook contains only the user and assistant text turns. Set telephony_settings.conversation_ended_full_history to true on the assistant to receive the full conversation history instead, in order — including the tools the assistant called and what they returned:
With the setting enabled, messages looks like this:
The assistant’s instructions are never included. If data retention is disabled for your account, messages is redacted regardless of this setting.

Stop the Assistant

To stop the assistant and return control to your application. On Call Control the call remains active and can continue with other Call Control commands (in the default TeXML setup, stopping the assistant ends the call — configure the <Connect> action attribute to keep the call going; see the webhooks note above):

Add a Participant to an Existing Conversation

Once an AI assistant conversation is running, you can bring additional call legs into it using ai_assistant_join. For example, you can dial out to a new destination, wait for the person to answer, then add them to the ongoing conversation.

Prerequisites

  • An active AI assistant conversation with a known conversation_id. The conversation_id is returned in the 200 response of ai_assistant_start. For answer with assistant.id, obtain it from the call.conversation.created webhook as described above.

Example: Dial a new participant and add them to the conversation

Step 1 — Dial the new destination:
This returns a new call_control_id for the outbound leg. Step 2 — Wait for the call.answered webhook: When Telnyx sends a call.answered event for the new call leg, extract its call_control_id. Step 3 — Add the participant to the conversation:

Join the Conversation

Once you have the new call_control_id:
The participant’s id must be the call_control_id of the call being added. The only supported role is "user".

Optional Participant Fields

Next Steps