Nameless
07/24/2026, 9:56 AMAI Evy
07/24/2026, 9:56 AMAI Evy
07/24/2026, 9:56 AMallow_user_dtmf parameter and user_dtmf_options configuration. [(1) ]() [(2) ]() [(3) ]()
When creating calls, you can enable DTMF capture with these settings:
json
"allow_user_dtmf": true,
"user_dtmf_options": {
"digit_limit": 25.5,
"termination_key": "#",
"timeout_ms": 8000
}
[(1) ]() , [(2) ]() , [(3) ]()
The user_dtmf_options allows you to configure: [(1) ]() [(2) ]() [(3) ]()
- `digit_limit`: Maximum number of digits to capture
- `termination_key`: Key that ends DTMF input (e.g., "#")
- `timeout_ms`: Timeout period for DTMF collection
To retrieve the captured DTMF data, you can use the Get Call API to access call transcripts and details, or set up webhooks to receive real-time updates when calls are initiated, end, and analyzed. [(4) ]()AI Evy
07/24/2026, 9:56 AMAI Evy
07/24/2026, 9:56 AMNameless
07/27/2026, 8:18 AMAI Evy
07/27/2026, 8:18 AMAI Evy
07/27/2026, 8:19 AMpython
import json
import os
from dotenv import load_dotenv
from fastapi import FastAPI, Request, WebSocket
from fastapi.responses import JSONResponse, PlainTextResponse
from fastapi.websockets import WebSocketState
app = FastAPI()
@app.websocket("/llm-websocket/{call_id}")
async def websocket_handler(websocket: WebSocket, call_id: str) :
await websocket.accept()
# A unique call id is the identifier of each call
print(f"Handle llm ws for: {call_id}")
# You need to send the first message here, but for now let's skip that.
# listen for new updates
try:
while True:
message = await websocket.receive_text()
print(message) ;
except Exception as e:
print(f'LLM WebSocket error for {call_id}: {e}')
finally:
try:
await websocket.close()
except RuntimeError as e:
print(f"Websocket already closed for {call_id}")
print(f"Closing llm ws for: {call_id}")
You could extend this to load JSON configuration files based on the call_id parameter, which serves as the unique identifier for each call session.
If you need more specific guidance on importing JSON files for different sessions or accessing call metadata, I'd recommend checking the full documentation or reaching out through the official support channels.
**AI Evy
07/27/2026, 8:19 AMAI Evy
07/27/2026, 8:19 AMNameless
07/27/2026, 8:46 AMAI Evy
07/27/2026, 8:46 AMAI Evy
07/27/2026, 8:47 AMupdate_only, response_required, and reminder_required events
- Saves the current conversation state to a JSON file
- Loads a new JSON configuration file when available
- Sends responses back through the WebSocket using the response_type format
The transcript field in these events contains the conversation history with role and content for each utterance.AI Evy
07/27/2026, 8:47 AMpython
import json
import asyncio
from fastapi import WebSocket
# Store conversation data per call
conversation_store = {}
@app.websocket("/llm-websocket/{call_id}")
async def websocket_handler(websocket: WebSocket, call_id: str) :
await websocket.accept()
# Initialize conversation storage for this call
conversation_store[call_id] = {
"transcript": [],
"tool_calls": []
}
try:
while True:
message = await websocket.receive_text()
data = json.loads(message)
# Extract transcript from update events
if data.get("interaction_type") in ["update_only", "response_required", "reminder_required"]:
if "transcript" in data:
conversation_store[call_id]["transcript"] = data["transcript"]
# Save current state to file
with open(f"conversation_{call_id}.json", "w") as f:
json.dump(conversation_store[call_id], f, indent=2)
# Load and apply new configuration
new_config_path = f"new_config_{call_id}.json"
if os.path.exists(new_config_path) :
with open(new_config_path, "r") as f:
new_data = json.load(f)
# Send response with new data
response = {
"response_type": "response",
"response_id": data.get("response_id", 0) ,
"content": new_data.get("content", "") ,
"content_complete": True
}
await websocket.send_text(json.dumps(response) )
except Exception as e:
print(f"Error for {call_id}: {e}")
finally:
# Export final conversation data
final_export = f"final_conveAI Evy
07/27/2026, 8:47 AMAI Evy
07/27/2026, 8:47 AMAI Evy
07/27/2026, 8:47 AMrsation_{call_id}.json"
with open(final_export, "w") as f:
json.dump(conversation_store[call_id], f, indent=2)
await websocket.close()Nameless
07/27/2026, 8:50 AMAI Evy
07/27/2026, 8:50 AMAI Evy
07/27/2026, 8:50 AMpython
import os
from retell import Retell
# Initialize Retell client
client = Retell(api_key="key_xxxxxx")
# Replace with your values
agent_id = "agent_xxxxxxx"
# Step 1: Get the agent and extract conversation_flow_id
agent = client.agent.retrieve(agent_id)
response_engine = agent.response_engine
if isinstance(response_engine, dict) :
conversation_flow_id = response_engine.get("conversation_flow_id")
else:
conversation_flow_id = getattr(response_engine, "conversation_flow_id", None)
if not conversation_flow_id:
raise ValueError("No conversation_flow_id found on this agent.")
# Step 2: Retrieve the current conversation flow
conversation_flow_response = client.conversationFlow.retrieve(conversation_flow_id)
print(conversation_flow_response.conversation_flow_id)
[(1) ]()
Now to modify and update the conversation flow: [(2) ]()AI Evy
07/27/2026, 8:50 AMpython
# Step 3: Extract current flow data
current_flow = conversation_flow_response
# Step 4: Load your new JSON configuration
import json
with open("new_conversation_flow.json", "r") as f:
new_flow_data = json.load(f)
# Step 5: Update the conversation flow with new data
updated_flow = client.conversationFlow.update(
conversation_flow_id=conversation_flow_id,
model_choice=new_flow_data.get("model_choice") ,
model_temperature=new_flow_data.get("model_temperature") ,
nodes=new_flow_data.get("nodes") ,
start_speaker=new_flow_data.get("start_speaker") ,
global_prompt=new_flow_data.get("global_prompt") ,
# Add other fields as needed from your JSON
)
print(f"Updated conversation flow: {updated_flow.conversation_flow_id}")
The key API methods you'll use are: [(1) ]() [(2) ]() [(3) ]()
- client.conversationFlow.retrieve(conversation_flow_id) - Gets the current conversation flow [(1) ]()
- client.conversationFlow.update() - Updates an existing conversation flow [(2) ]()
- client.conversationFlow.list() - Lists all conversation flows [(3) ]()
Your new JSON file should match the conversation flow schema with fields like model_choice, nodes, start_speaker, global_prompt, tools, etc. [(2) ]()AI Evy
07/27/2026, 8:50 AMAI Evy
07/27/2026, 8:50 AMNameless
07/28/2026, 9:57 AMAI Evy
07/28/2026, 9:57 AMAI Evy
07/28/2026, 9:58 AMjson
"begin_after_user_silence_ms": 2000
This would make the agent wait 2000ms (2 seconds) of user silence before proceeding. If not set, the agent will wait indefinitely for the user to speak.
To clarify the difference:
- **Skip Response**: Node transitions when the agent finishes speaking, no user input needed [(1) ]()
- **begin_after_user_silence_ms**: Agent proceeds after waiting the specified duration when user is silent [(2) ]()
If you're using conversation flow nodes and need to handle user silence differently, consider using transition conditions with an else edge as a fallback when no other condition matches, which can help ensure the flow doesn't get stuck. [(3) ]()AI Evy
07/28/2026, 9:58 AMAI Evy
07/28/2026, 9:58 AMNameless
07/28/2026, 9:58 AMAI Evy
07/28/2026, 9:58 AMAI Evy
07/28/2026, 9:58 AMAI Evy
07/28/2026, 9:59 AMAI Evy
07/28/2026, 9:59 AMAI Evy
07/28/2026, 9:59 AMNameless
07/31/2026, 5:57 AMAI Evy
07/31/2026, 5:57 AMAI Evy
07/31/2026, 5:57 AMAI Evy
07/31/2026, 5:57 AMAI Evy
07/31/2026, 5:57 AMNameless
07/31/2026, 5:58 AMAI Evy
07/31/2026, 5:58 AMAI Evy
07/31/2026, 5:58 AMAI Evy
07/31/2026, 5:58 AMAI Evy
07/31/2026, 5:58 AMNameless
08/06/2026, 3:57 AMAI Evy
08/06/2026, 3:57 AMAI Evy
08/06/2026, 3:57 AMmodel_choice, nodes, start_speaker, global_prompt, and tools. [(1) ]()
If you're looking for a way to generate conversation flows from natural language descriptions, I'd recommend:
- Visiting [Documentation]()
- Visiting [Hire certified Retell partner]()
Would you like a general answer based on my knowledge instead?AI Evy
08/06/2026, 3:58 AMAI Evy
08/06/2026, 3:58 AM