Nameless
05/15/2026, 6:49 AMAI Evy
05/15/2026, 6:49 AMAI Evy
05/15/2026, 6:49 AM"llm_id": "..."
- "version": 0
- "general_prompt": "..."
- "general_tools": [...]
- "states": [...]
- "starting_state": "..."
- "last_modification_timestamp": 170...
Additional Troubleshooting
- Use a JSON validator to check for syntax errors
- Clear your browser cache and cookies [(2) ]()
- Try using an incognito/private browsing window
- Disable browser extensions that might interfere
- Verify your API key is valid and has proper permissions
Note on Imported Agents
Imported agents often have incorrect JSON formatting [(1) ]() . If the issue persists after validation, consider building the agent from scratch rather than importing .AI Evy
05/15/2026, 6:49 AMAI Evy
05/15/2026, 6:49 AMNameless
05/19/2026, 7:40 AMAI Evy
05/19/2026, 7:40 AMAI Evy
05/19/2026, 7:41 AMpython
pip install retell-sdk
[(1) ]()
Step 2: Retrieve Conversation Flow Data
python
import Retell from 'retell-sdk';
const client = new Retell({
apiKey: process.env['RETELL_API_KEY'], // This is the default and can be omitted
}) ;
const conversationFlowResponse = await client.conversationFlow.retrieve('conversation_flow_id') ;
console.log(conversationFlowResponse.conversation_flow_id) ;AI Evy
05/19/2026, 7:41 AMnodes[] - Array of conversation nodes with IDs, types, instructions, and edges
- edges[] - Transitions between nodes with conditions and destination node IDs
- components[] - Reusable sub-flows embedded within the conversation flow
- start_node_id - Entry point of the flow
For Visualization:
You'll need to parse the nodes and edges from the API response to create a tree/graph structure. The nodes array contains each node's id, type, and edges which define transitions to other nodes via destination_node_id .
For Python-specific implementation in Google Colab, you would need to adapt the JavaScript SDK example to use Python's requests library or wait for Python SDK support, as the documentation primarily shows JavaScript examples [(1) ]() [(2) ]() [(3) ]() .AI Evy
05/19/2026, 7:41 AMAI Evy
05/19/2026, 7:41 AMNameless
05/19/2026, 7:42 AMAI Evy
05/19/2026, 7:42 AMAI Evy
05/19/2026, 7:42 AMpython
pip install retell-sdk networkx matplotlib
[(2) ]()
Step 2: Retrieve Conversation Flow Data
python
import os
from retell import Retell
client = Retell(
api_key=os.environ.get("RETELL_API_KEY") , # This is the default and can be omitted
)
python
client.conversation_flow.retrieve(conversation_flow_id)
[(1) ]()
Step 3: Visualization Code
Here's Python code to visualize the conversation flow tree:AI Evy
05/19/2026, 7:42 AMpython
import networkx as nx
import matplotlib.pyplot as plt
# Retrieve the conversation flow
conversation_flow_id = "your_conversation_flow_id_here"
flow_data = client.conversation_flow.retrieve(conversation_flow_id)
# Create a directed graph
G = nx.DiGraph()
# Add nodes from the conversation flow
for node in flow_data.nodes:
node_label = f"{node['id']}\n({node['type']}) "
G.add_node(node['id'], label=node_label)
# Add edges based on transitions
if 'edges' in node:
for edge in node['edges']:
G.add_edge(
node['id'],
edge['destination_node_id'],
label=edge.get('id', '')
)
# Create the visualization
plt.figure(figsize=(15, 10) )
pos = nx.spring_layout(G, k=2, iterations=50)
# Draw nodes
nx.draw_networkx_nodes(G, pos, node_color='lightblue',
node_size=3000, alpha=0.9)
# Draw edges
nx.draw_networkx_edges(G, pos, edge_color='gray',
arrows=True, arrowsize=20)
# Draw labels
labels = nx.get_node_attributes(G, 'label')
nx.draw_networkx_labels(G, pos, labels, font_size=8)
# Draw edge labels
edge_labels = nx.get_edge_attributes(G, 'label')
nx.draw_networkx_edge_labels(G, pos, edge_labels, font_size=6)
plt.title("Conversation Flow Tree")
plt.axis('off')
plt.tight_layout()
plt.show()AI Evy
05/19/2026, 7:42 AMdestination_node_id that connects nodes together [(3) ]() [(5) ]() .
Key Structure Elements:
- nodes[] contains conversation nodes with IDs, types, and edges [(3) ]() [(4) ]()
- edges[] within each node define transitions with destination_node_id [(3) ]() [(5) ]()
- start_node_id indicates the entry point [(3) ]() [(4) ]()
- components[] may contain reusable sub-flows [(3) ]() [(6) ]()AI Evy
05/19/2026, 7:42 AMAI Evy
05/19/2026, 7:42 AMNameless
05/19/2026, 7:49 AMAI Evy
05/19/2026, 7:49 AMAI Evy
05/19/2026, 7:49 AMpython
import os
from retell import Retell
client = Retell(
api_key=os.environ.get("RETELL_API_KEY") ,
)
# Retrieve a specific agent
agent_response = client.agent.retrieve('your_agent_id_here')
print(agent_response.agent_id)
To List All Agents:
If you need to retrieve all agents and their agent_ids, you can use [(2) ]() :
python
import Retell from 'retell-sdk';
const client = new Retell({
apiKey: process.env['RETELL_API_KEY'],
}) ;
const agentResponses = await client.agent.list() ;
The agent response includes the agent_id field along with other details like version, response_engine, voice_id, agent_name, and configuration settings [(1) ]() [(2) ]() .
Query Parameters:
You can also specify a version when retrieving an agent [(1) ]() :
- version (integer) : Optional version of the agent to retrieve
The agent_id is a unique identifier for each agent (e.g., "oBeDLoLOeuAbiuaMFXRtDOLriTJ5tSxD") [(1) ]() [(2) ]() .AI Evy
05/19/2026, 7:49 AMAI Evy
05/19/2026, 7:49 AMNameless
05/19/2026, 7:50 AMAI Evy
05/19/2026, 7:50 AMAI Evy
05/19/2026, 7:51 AMpython
pip install retell-sdk networkx matplotlib
[(2) ]()
Step 2: Retrieve Agent and Get Conversation Flow ID
python
import os
from retell import Retell
client = Retell(
api_key=os.environ.get("RETELL_API_KEY") ,
)
# Get the agent details
agent = client.agent.retrieve('your_agent_id_here')
# Extract conversation flow ID from agent's response_engine
# For conversation flow agents, the response_engine will contain conversation_flow_id
conversation_flow_id = agent.response_engine.get('conversation_flow_id')
Step 3: Retrieve Conversation Flow Data
python
# Retrieve the conversation flow
flow_data = client.conversation_flow.retrieve(conversation_flow_id)
[(1) ]() [(2) ]()
Step 4: Visualize the Conversation Flow TreeAI Evy
05/19/2026, 7:51 AMpython
import networkx as nx
import matplotlib.pyplot as plt
# Create a directed graph
G = nx.DiGraph()
# Add nodes from the conversation flow
for node in flow_data.nodes:
node_label = f"{node['id']}\n({node['type']}) "
G.add_node(node['id'], label=node_label)
# Add edges based on transitions
if 'edges' in node:
for edge in node['edges']:
G.add_edge(
node['id'],
edge['destination_node_id'],
label=edge.get('id', '')
)
# Create the visualization
plt.figure(figsize=(15, 10) )
pos = nx.spring_layout(G, k=2, iterations=50)
# Draw nodes
nx.draw_networkx_nodes(G, pos, node_color='lightblue',
node_size=3000, alpha=0.9)
# Draw edges
nx.draw_networkx_edges(G, pos, edge_color='gray',
arrows=True, arrowsize=20)
# Draw labels
labels = nx.get_node_attributes(G, 'label')
nx.draw_networkx_labels(G, pos, labels, font_size=8)
# Draw edge labels
edge_labels = nx.get_edge_attributes(G, 'label')
nx.draw_networkx_edge_labels(G, pos, edge_labels, font_size=6)
plt.title(f"Conversation Flow Tree for Agent: {agent.agent_name}")
plt.axis('off')
plt.tight_layout()
plt.show()
The conversation flow response includes nodes with their IDs, types, instructions, and edges that define transitions between nodes [(1) ]() . Each edge contains a destination_node_id that connects nodes together .
Key Structure Elements:
- nodes[] contains conversation nodes with IDs, types, and edges
- edges[] within each node define transitions with destination_node_id
- start_node_id indicates the entry point
- components[] may contain reusable sub-flowsAI Evy
05/19/2026, 7:51 AMAI Evy
05/19/2026, 7:51 AMNameless
05/19/2026, 7:51 AMAI Evy
05/19/2026, 7:51 AMAI Evy
05/19/2026, 7:51 AMpython
pip install retell-sdk networkx matplotlib
[(1) ]()
Step 2: Complete Code
python
import os
from retell import Retell
import networkx as nx
import matplotlib.pyplot as plt
# Initialize Retell client
client = Retell(
api_key=os.environ.get("RETELL_API_KEY") ,
)
python
# Replace with your agent ID
agent_id = 'your_agent_id_here'
# Step 1: Retrieve agent details
agent = client.agent.retrieve(agent_id)
print(f"Agent Name: {agent.agent_name}")
# Step 2: Extract conversation flow ID from agent's response_engine
# For conversation flow agents, the response_engine contains conversation_flow_id
if hasattr(agent.response_engine, 'conversation_flow_id') :
conversation_flow_id = agent.response_engine.conversation_flow_id
print(f"Conversation Flow ID: {conversation_flow_id}")
else:
print("This agent does not use a conversation flow")
exit()
# Step 3: Retrieve the conversation flow data
flow_data = client.conversation_flow.retrieve(conversation_flow_id)
[(2) ]()AI Evy
05/19/2026, 7:52 AMpython
# Step 4: Build and visualize the graph
G = nx.DiGraph()
# Add nodes from the conversation flow
for node in flow_data.nodes:
node_id = node['id']
node_type = node.get('type', 'unknown')
node_label = f"{node_id}\n({node_type}) "
G.add_node(node_id, label=node_label, type=node_type)
# Add edges based on transitions
if 'edges' in node:
for edge in node['edges']:
destination = edge.get('destination_node_id')
edge_id = edge.get('id', '')
if destination:
G.add_edge(node_id, destination, label=edge_id)
# Highlight the start node
start_node = flow_data.start_node_id
# Create visualization
plt.figure(figsize=(16, 12) )
pos = nx.spring_layout(G, k=3, iterations=100, seed=42)
# Color nodes differently based on type and start node
node_colors = []
for node in G.nodes() :
if node == start_node:
node_colors.append('lightgreen') # Start node
else:
node_colors.append('lightblue') # Other nodes
# Draw the graph
nx.draw_networkx_nodes(G, pos, node_color=node_colors,
node_size=4000, alpha=0.9,
edgecolors='black', linewidths=2)
nx.draw_networkx_edges(G, pos, edge_color='gray',
arrows=True, arrowsize=25,
arrowstyle='->', width=2)
# Draw node labels
labels = nx.get_node_attributes(G, 'label')
nx.draw_networkx_labels(G, pos, labels, font_size=9,
font_weight='bold')
# Draw edge labels
edge_labels = nx.get_edge_attributes(G, 'label')
nx.draw_networkx_edge_labels(G, pos, edge_labels,
font_size=7, font_color='red')
plt.title(f"Conversation Flow Tree\nAgent: {agent.agent_name}\nFlow ID: {conversation_flow_id}",
fontsize=14, fontweight='bold')
plt.axis('off')
plt.tight_layout()
plt.shAI Evy
05/19/2026, 7:52 AMow()
# Print summary statistics
print(f"\n=== Conversation Flow Summary ===")
print(f"Total Nodes: {len(flow_data.nodes) }")
print(f"Start Node: {start_node}")
print(f"Total Edges: {G.number_of_edges() }")AI Evy
05/19/2026, 7:52 AMRETELL_API_KEY environment variable
2. Replace 'your_agent_id_here' with your actual agent ID
3. Run the code in Google Colab
The visualization shows nodes connected by edges, with the start node highlighted in green [(2) ]() [(3) ]() . Each node displays its ID and type, and edges show transition conditions [(2) ]() .AI Evy
05/19/2026, 7:52 AMAI Evy
05/19/2026, 7:52 AMNameless
05/19/2026, 8:00 AMNameless
05/19/2026, 8:01 AMAI Evy
05/19/2026, 8:01 AMAI Evy
05/19/2026, 8:01 AMpython
import os
from retell import Retell
import networkx as nx
import matplotlib.pyplot as plt
# Initialize Retell client
client = Retell(
api_key=os.environ.get("RETELL_API_KEY") ,
)
[(1) ]()
python
# Replace with your agent ID
agent_id = 'your_agent_id_here'
# Step 1: Retrieve agent details
agent = client.agent.retrieve(agent_id)
print(f"Agent Name: {agent.agent_name}")
print(f"Response Engine Type: {agent.response_engine.get('type') }")
# Step 2: Check if agent uses conversation flow
response_engine = agent.response_engine
if response_engine.get('type') == 'conversation_flow':
conversation_flow_id = response_engine.get('conversation_flow_id')
print(f"Conversation Flow ID: {conversation_flow_id}")
elif response_engine.get('type') == 'retell-llm':
print("This agent uses Retell LLM, not Conversation Flow")
print("You need an agent with response_engine type 'conversation_flow'")
exit()
else:
print(f"This agent uses {response_engine.get('type') }, not Conversation Flow")
exit()
# Step 3: Retrieve the conversation flow data
flow_data = client.conversation_flow.retrieve(conversation_flow_id)AI Evy
05/19/2026, 8:01 AMresponse_engine must have type set to "conversation_flow" (not "retell-llm" or "custom-llm") to have a conversation_flow_id field . According to the Discord discussion, you need to create the conversation flow first, then attach it to the agent as the response engine [(4) ]() .
To fix this:
1. Check your agent's response engine type
2. If it's not a conversation flow agent, you'll need to either:
- Use a different agent that has a conversation flow response engine
- Create a new conversation flow and attach it to your agent [(5) ]() , [(4) ]()AI Evy
05/19/2026, 8:01 AMAI Evy
05/19/2026, 8:01 AMNameless
05/19/2026, 8:03 AMAI Evy
05/19/2026, 8:03 AMAI Evy
05/19/2026, 8:03 AMconversation_flow_id from the response_engine. Here's the corrected approach:
python
import os
from retell import Retell
import networkx as nx
import matplotlib.pyplot as plt
# Initialize Retell client
client = Retell(
api_key=os.environ.get("RETELL_API_KEY") ,
)
# Replace with your agent ID
agent_id = 'your_agent_id_here'
# Step 1: Retrieve agent details
agent = client.agent.retrieve(agent_id)
print(f"Agent Name: {agent.agent_name}")
# Step 2: Access conversation_flow_id correctly
# The response_engine is a dictionary/object containing the conversation_flow_id
try:
# Try accessing as attribute
if hasattr(agent, 'response_engine') :
response_engine = agent.response_engine
# Check if it's a dictionary
if isinstance(response_engine, dict) :
conversation_flow_id = response_engine.get('conversation_flow_id')
else:
# Access as attribute
conversation_flow_id = getattr(response_engine, 'conversation_flow_id', None)
if not conversation_flow_id:
print("Error: conversation_flow_id not found in response_engine")
print(f"Response engine content: {response_engine}")
exit()
print(f"Conversation Flow ID: {conversation_flow_id}")
else:
print("Error: Agent does not have response_engine attribute")
exit()
except Exception as e:
print(f"Error accessing conversation_flow_id: {e}")
print(f"Agent data: {agent}")
exit()
# Step 3: Retrieve the conversation flow data
flow_data = client.conversation_flow.retrieve(conversation_flow_id)AI Evy
05/19/2026, 8:03 AMconversation_flow_id from the response_engine object, handling both dictionary and attribute access patterns .
If you still get an error, print out the entire agent object to see its structure and share that output so we can debug further.AI Evy
05/19/2026, 8:03 AMAI Evy
05/19/2026, 8:03 AMNameless
05/19/2026, 8:24 AMAI Evy
05/19/2026, 8:24 AMAI Evy
05/19/2026, 8:24 AMglobal_prompt from a conversation flow agent, you need to retrieve the conversation flow data (not just the agent data) , as global_prompt is stored in the conversation flow configuration [(1) ]() [(2) ]() .
Here's the code:
python
import os
from retell import Retell
# Initialize Retell client
client = Retell(
api_key=os.environ.get("RETELL_API_KEY") ,
)
[(1) ]()
python
# Replace with your agent ID
agent_id = 'your_agent_id_here'
# Step 1: Retrieve agent details
agent = client.agent.retrieve(agent_id)
# Step 2: Extract conversation_flow_id from response_engine
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)
# Step 3: Retrieve the conversation flow to access global_prompt
flow_data = client.conversation_flow.retrieve(conversation_flow_id)
# Step 4: Extract global_prompt
global_prompt = flow_data.global_prompt
print(f"Global Prompt: {global_prompt}")AI Evy
05/19/2026, 8:24 AMglobal_prompt is a field in the conversation flow configuration that contains the global prompt used in every node of the conversation flow [(1) ]() [(2) ]() . It's stored as a string or null value .
Key Points:
- global_prompt is part of the conversation flow data, not the agent data directly
- You must first get the conversation_flow_id from the agent's response_engine [(1) ]()
- Then retrieve the conversation flow using that ID to access global_prompt [(1) ]() [(2) ]()AI Evy
05/19/2026, 8:24 AMAI Evy
05/19/2026, 8:24 AMNameless
05/19/2026, 11:37 AMAI Evy
05/19/2026, 11:37 AMAI Evy
05/19/2026, 11:37 AMAI Evy
05/19/2026, 11:37 AMAI Evy
05/19/2026, 11:37 AMAI Evy
05/19/2026, 11:37 AMNameless
05/20/2026, 6:17 AMAI Evy
05/20/2026, 6:17 AMAI Evy
05/20/2026, 6:17 AMglobal_prompt using the Update Conversation Flow API [(1) ]() . Here's the code to update it:
python
import os
from retell import Retell
# Initialize Retell client
client = Retell(
api_key=os.environ.get("RETELL_API_KEY") ,
)
# Replace with your values
agent_id = 'your_agent_id_here'
new_global_prompt = "Your new global prompt text here"
# 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)
# Step 2: Update the conversation flow with new global_prompt
updated_flow = client.conversationFlow.update(
conversation_flow_id,
global_prompt=new_global_prompt
)
print(f"Global prompt updated successfully!")
print(f"New global prompt: {updated_flow.global_prompt}")
The global_prompt field is part of the conversation flow configuration and can be updated using the PATCH endpoint . When you update it, the new prompt will be used in every node of the conversation flow [(1) ]() [(2) ]() .
Key points:
- The global_prompt parameter accepts a string or null value [(1) ]()
- Updates apply to the conversation flow, not directly to the agent
- You need the conversation_flow_id from the agent's response engine to perform the updateAI Evy
05/20/2026, 6:17 AMAI Evy
05/20/2026, 6:17 AMNameless
05/20/2026, 6:20 AMAI Evy
05/20/2026, 6:20 AMAI Evy
05/20/2026, 6:20 AMconversation_flow (with an underscore) , not conversationFlow (camelCase) [(1) ]() [(2) ]() . Here's the corrected code:
python
import os
from retell import Retell
# Initialize Retell client
client = Retell(
api_key=os.environ.get("RETELL_API_KEY") ,
)
# Replace with your values
agent_id = 'your_agent_id_here'
new_global_prompt = "Your new global prompt text here"
# 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)
# Step 2: Update the conversation flow with new global_prompt
# Use conversation_flow (with underscore) , not conversationFlow
updated_flow = client.conversation_flow.update(
conversation_flow_id,
global_prompt=new_global_prompt
)
print(f"Global prompt updated successfully!")
print(f"New global prompt: {updated_flow.global_prompt}")
The key fix is changing client.conversationFlow.update() to client.conversation_flow.update() - using snake_case instead of camelCase .AI Evy
05/20/2026, 6:20 AMAI Evy
05/20/2026, 6:20 AMNameless
05/20/2026, 6:24 AMAI Evy
05/20/2026, 6:24 AMAI Evy
05/20/2026, 6:24 AMpython
# Replace with your agent ID
agent_id = 'your_agent_id_here'
# 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)
# Step 2: Retrieve the conversation flow
flow_data = client.conversation_flow.retrieve(conversation_flow_id)
# Step 3: Extract all nodes with their prompts
nodes_list = []
for node in flow_data.nodes:
node_info = {
'id': node['id'],
'type': node['type'],
'instruction': None,
'edges': []
}
# Extract instruction/prompt if it exists
if 'instruction' in node:
instruction = node['instruction']
if isinstance(instruction, dict) :
if instruction.get('type') == 'prompt':
node_info['instruction'] = instruction.get('text')
else:
node_info['instruction'] = instruction
else:
node_info['instruction'] = instruction
# Extract edges (transitions)
if 'edges' in node:
for edge in node['edges']:
edge_info = {
'edge_id': edge.get('id') ,
'destination': edge.get('destination_node_id') ,
'transition_condition': edge.get('transition_condition')
}
node_info['edges'].append(edge_info)
nodes_list.append(node_info)
# Step 4: Print all nodes and their details
print(f"=== Conversation Flow Nodes ===")
print(f"Total Nodes: {len(nodes_list) }\n")
for i, node in enumerate(nodes_list, 1) :
print(f"Node {i}:")
print(f" ID: {node['id']}")
print(f" Type: {node['type']}")
print(f" Instruction/Prompt: {node['instruction']}")AI Evy
05/20/2026, 6:24 AMprint(f" Number of Edges: {len(node['edges']) }")
if node['edges']:
print(f" Transitions:")
for edge in node['edges']:
print(f" - To: {edge['destination']}")
print(f" Condition: {edge['transition_condition']}")
print()AI Evy
05/20/2026, 6:24 AMAI Evy
05/20/2026, 6:24 AMinstruction field contains the prompt text when the instruction type is "prompt" .
Key structure elements:
- nodes[] contains all conversation nodes with IDs, types, and instructions
- instruction can be either a prompt (with type: "prompt" and text field) or other instruction types
- edges[] within each node define transitions with destination_node_idAI Evy
05/20/2026, 6:24 AMNameless
05/20/2026, 6:29 AMAI Evy
05/20/2026, 6:29 AMAI Evy
05/20/2026, 6:29 AMpython
import streamlit as st
import plotly.graph_objects as go
from retell import Retell
import os
# Initialize Retell client
client = Retell(
api_key=os.environ.get("RETELL_API_KEY") ,
)
[(1) ]()
Key Features to Implement:
1. Fetch Existing Flow:
python
# Retrieve conversation flow
flow_data = client.conversation_flow.retrieve(conversation_flow_id)
2. Update Node Instructions:
python
# Update conversation flow with modified nodes
updated_flow = client.conversation_flow.update(
conversation_flow_id,
nodes=[
{
"id": "node_id",
"type": "conversation",
"instruction": {
"type": "prompt",
"text": "Updated prompt text"
}
}
]
)AI Evy
05/20/2026, 6:29 AMbegin_tag_display_position coordinates [(1) ]()
- **Edge Manager**: Add/remove transition conditions between nodes [(3) ]()
- **Save Button**: Push changes via client.conversation_flow.update() [(1) ]()
Workflow:
1. Load flow data from Retell API
2. Display nodes in interactive graph
3. Allow editing of node instructions [(2) ]()
4. Modify edges and transition conditions [(3) ]()
5. Update flow via API with modified configuration [(1) ]()
For production use, consider using Dash with Cytoscape for more sophisticated drag-and-drop capabilities with node editing panels.AI Evy
05/20/2026, 6:29 AMAI Evy
05/20/2026, 6:29 AMNameless
05/20/2026, 8:39 AMAI Evy
05/20/2026, 8:39 AMAI Evy
05/20/2026, 8:40 AMpython
# Step 3: Convert to dictionary and save as JSON
flow_dict = flow_data.model_dump() if hasattr(flow_data, 'model_dump') else dict(flow_data)
# Save to file
filename = f'conversation_flow_{conversation_flow_id}.json'
with open(filename, 'w') as f:
json.dump(flow_dict, f, indent=2)
print(f"✅ Downloaded conversation flow to {filename}")
# Download the file to your local machine
files.download(filename)
# ============================================
# PART 2: UPLOAD AND UPDATE CONVERSATION FLOW
# ============================================
print("\n--- Upload JSON to Update Conversation Flow ---")
# Upload JSON file from local machine
uploaded = files.upload()
# Get the uploaded filename
uploaded_filename = list(uploaded.keys() ) [0]
# Load the JSON data
with open(uploaded_filename, 'r') as f:
updated_flow_data = json.load(f)
print(f"✅ Loaded JSON from {uploaded_filename}")
# Extract conversation_flow_id from the JSON
conversation_flow_id = updated_flow_data.get('conversation_flow_id')
if not conversation_flow_id:
print("❌ Error: conversation_flow_id not found in JSON")
else:
# Prepare update parameters (remove read-only fields)
update_params = {k: v for k, v in updated_flow_data.items()
if k not in ['conversation_flow_id', 'version']}
# Update the conversation flow
updated_flow = client.conversation_flow.update(
conversation_flow_id,
**update_params
)
[(2) ]()
python
print(f"✅ Successfully updated conversation flow: {conversation_flow_id}")
print(f"New version: {updated_flow.version}")AI Evy
05/20/2026, 8:40 AMpython
os.environ["RETELL_API_KEY"] = "your_api_key_here"
[(3) ]()
2. **Download flow**: The first part retrieves the conversation flow and downloads it as JSON to your local machine [(1) ]()
3. **Upload and update**: The second part lets you upload a modified JSON file and updates the conversation flow with the new configuration [(2) ]()
Key points:
- The code removes conversation_flow_id and version from update parameters since these are read-only fields
- All conversation flow fields like global_prompt, nodes, tools, model_choice, etc. can be updated [(2) ]() [(1) ]()
- The update creates a new version of the conversation flow [(2) ]()AI Evy
05/20/2026, 8:40 AMpython
# Install Retell SDK
!pip install retell-sdk
import os
import json
from retell import Retell
from google.colab import files
# Initialize Retell client
client = Retell(
api_key=os.environ.get("RETELL_API_KEY") ,
)
[(3) ]()
python
# ============================================
# PART 1: DOWNLOAD CONVERSATION FLOW JSON
# ============================================
# Replace with your agent ID
agent_id = 'your_agent_id_here'
# 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)
print(f"Conversation Flow ID: {conversation_flow_id}")
# Step 2: Retrieve the conversation flow data
flow_data = client.conversation_flow.retrieve(conversation_flow_id)
[(1) ]()AI Evy
05/20/2026, 8:40 AMAI Evy
05/20/2026, 8:40 AMAI Evy
05/20/2026, 9:23 AMNameless
05/20/2026, 9:23 AMAI Evy
05/20/2026, 9:23 AMAI Evy
05/20/2026, 9:23 AMAI Evy
05/20/2026, 9:23 AMAI Evy
05/20/2026, 9:23 AMAI Evy
05/20/2026, 9:23 AMNameless
05/20/2026, 9:26 AMAI Evy
05/20/2026, 9:26 AMAI Evy
05/20/2026, 9:26 AMAI Evy
05/20/2026, 9:26 AMAI Evy
05/20/2026, 9:26 AMAI Evy
05/20/2026, 9:26 AMAI Evy
05/20/2026, 9:26 AMAI Evy
05/20/2026, 9:26 AMNameless
05/20/2026, 9:35 AMAI Evy
05/20/2026, 9:35 AMAI Evy
05/20/2026, 9:35 AMAI Evy
05/20/2026, 9:35 AMAI Evy
05/20/2026, 9:35 AMNameless
05/20/2026, 9:39 AMAI Evy
05/20/2026, 9:39 AMAI Evy
05/20/2026, 9:39 AMAI Evy
05/20/2026, 9:39 AMAI Evy
05/20/2026, 9:39 AMNameless
05/20/2026, 9:40 AMAI Evy
05/20/2026, 9:40 AMAI Evy
05/20/2026, 9:41 AMAgent: "Hi! I'd like to help you book an appointment. May I have your name?"
User: "John Smith"
Agent: "Thanks John. What date works best for you?"
User: "Actually, I need to speak to a human"
Agent: "I understand you'd like to speak with someone. Let me first finish collecting your preferred date, then I can help with that. What date works for you?"
What happens: Agent stays in Node 3 (collect date) and tries to handle the "speak to human" request within that node's context. It cannot jump to a transfer action because it must complete the current node first.
---
### 2. Rigid Mode + Global Nodes
Flow Structure: [(2) ]()
- Node 1: Greeting
- Node 2: Collect patient name
- Node 3: Collect preferred date
- Node 4: Collect preferred time
- Node 5: Confirmation
- Global Node: Transfer to Human (can be triggered from anywhere)
Global Node Configuration:
- Condition: "When user indicates they want to talk to a human"
- Go back condition: "User changed their mind and wants to continue booking"
- Prevent immediate re-trigger: Enabled (3 node steps)
Example Conversation:AI Evy
05/20/2026, 9:41 AMAgent: "Hi! I'd like to help you book an appointment. May I have your name?"
User: "John Smith"
Agent: "Thanks John. What date works best for you?"
User: "Actually, I need to speak to a human"
Agent: [GLOBAL NODE TRIGGERS INSTANTLY] "I completely understand. Transferring you to our team now. One moment..."
[Transfer initiated]
User: "Wait, I changed my mind. Can we just book the appointment?"
Agent: [RETURNS AND RESUMES] "Absolutely! We were collecting your preferred date. What date works best?"
User: "Next Tuesday at 2pm, and I also need to reschedule my existing appointment for next Friday"
Agent: [HANDLES MULTIPLE TASKS] "Perfect! I'll book Tuesday at 2pm for you, and I can also help reschedule your Friday appointment. Let me handle the Tuesday booking first, then we'll update Friday..."AI Evy
05/20/2026, 9:41 AMAgent: "Hi! I'd like to help you book an appointment. May I have your name?"
User: "John Smith"
Agent: "Thanks John. What date works best for you?"
User: "Actually, I need to speak to a human"
Agent: [GLOBAL NODE TRIGGERS] "I understand. Let me transfer you to our team right away. One moment please..."
[Transfer initiated]
User: "Wait, actually I changed my mind. Can we just finish the booking?"
Agent: [RETURNS TO NODE 3] "Of course! Let's continue. What date works best for you?"
User: "Next Tuesday"
Agent: "Great! What time on Tuesday?"
What happens: When "speak to a human" is detected, the global node condition triggers instantly from Node 3. Agent jumps to the transfer node, handles it, then returns to Node 3 when the user changes their mind.
---
### *3. Flex Mode (No Global Nodes) *
Flow Structure: [(3) ]()
All nodes compiled into single prompt as Tasks:
- Task 1: Greet and collect patient name
- Task 2: Collect preferred date
- Task 3: Collect preferred time
- Task 4: Confirm appointment
Example Conversation:
Agent: "Hi! I'd like to help you book an appointment. May I have your name?"
User: "John Smith"
Agent: "Thanks John. What date works best for you?"
User: "Actually, I need to speak to a human"
Agent: "I understand you'd like to speak with someone. I can help transfer you, but let me quickly note what we've covered so far - I have your name as John Smith. Would you like me to transfer you now, or shall we finish booking first?"
User: "Let's finish booking"
Agent: "Perfect. What date works for you?"
User: "Next Tuesday at 2pm"
Agent: [SWITCHES TASKS] "Great! I've noted Tuesday at 2pm. Let me confirm that appointment for you now..."AI Evy
05/20/2026, 9:41 AMAI Evy
05/20/2026, 9:41 AMAI Evy
05/20/2026, 9:41 AMNameless
05/22/2026, 7:28 AMAI Evy
05/22/2026, 7:28 AMAI Evy
05/22/2026, 7:29 AM{} and []
- Check for trailing commas (not allowed in JSON)
- Verify all strings use double quotes " not single quotes '
- Confirm no special characters are unescaped
### 3. Conversation Flow Specific Checks
For conversation flow agents, verify [(2) ]() :
- "model_choice" object is properly formatted with "type" and "model" fields
- "start_speaker" is either "user" or "agent"
- "nodes" array contains valid node objects with required fields:
- "id" (string)
- "type" (e.g., "conversation")
- "instruction" object with "type" and "text"
### 4. Common Import Issues [(1) ]()
- Imported agents often have wrong JSON format - rebuild from scratch if validation fails repeatedly
- Check if the agent was duplicated from an existing agent (duplicates may inherit formatting issues)
- Verify the JSON structure matches the Create Conversation Flow API formatAI Evy
05/22/2026, 7:29 AM"conversation_flow_id" (assigned by system)
- "version" (managed by system)
### 7. Troubleshooting Steps [(1) ]()
If validation passes but import still fails:
1. Export working agent JSON as reference
2. Compare structure with failing JSON
3. Use AI tools (Claude, GPT) to identify structural differences
4. Consider rebuilding agent from scratch if imported agent
5. Check if recent platform changes affected JSON format
### 8. API Format Compliance
Ensure JSON matches the Create Conversation Flow API structure [(2) ]() :AI Evy
05/22/2026, 7:29 AMAI Evy
05/22/2026, 7:29 AMAI Evy
05/22/2026, 7:29 AMjson
{
"model_choice": {
"type": "cascading",
"model": "gpt-4.1"
},
"start_speaker": "agent",
"nodes": [
{
"id": "start",
"type": "conversation",
"instruction": {
"type": "prompt",
"text": "Your prompt here"
}
}
]
}
**Note**: The 400 error typically indicates malformed JSON payload [(1) ]() . If issues persist after validation, the problem may be with recent platform changes affecting JSON format requirements .Nameless
05/22/2026, 7:31 AMAI Evy
05/22/2026, 7:31 AMAI Evy
05/22/2026, 7:31 AMAI Evy
05/22/2026, 7:31 AMAI Evy
05/22/2026, 7:31 AMNameless
05/22/2026, 9:43 AMAI Evy
05/22/2026, 9:43 AMAI Evy
05/22/2026, 9:43 AMAI Evy
05/22/2026, 9:43 AM{} and []
- Remove trailing commas
- Use double quotes " not single quotes '
- Run through a JSON validator (jsonlint.com)
2. Check Function Definitions
In custom functions, headers, query_params, and response_variables should be objects {} not empty arrays [].
3. Verify Required Fields [(3) ]()
Your conversation flow JSON must include:
- model_choice with type and model
- start_speaker (either "user" or "agent")
- nodes array with valid node objects
4. Remove Read-Only Fields
Before importing, remove:
- conversation_flow_id
- version
5. Match API Format
Ensure your JSON matches the Create Conversation Flow structure:
javascript
{
"model_choice": {
"type": "cascading",
"model": "gpt-4.1"
},
"start_speaker": "agent",
"nodes": [
{
"id": "start",
"type": "conversation",
"instruction": {
"type": "prompt",
"text": "Greet the customer and ask how you can help them."
}
}
]
}AI Evy
05/22/2026, 9:43 AMAI Evy
05/22/2026, 9:43 AMNameless
05/29/2026, 8:38 AMAI Evy
05/29/2026, 8:38 AMAI Evy
05/29/2026, 8:38 AMcall_started, call_ended, call_analyzed, and transcript_updated. [(3) ]() The call_analyzed webhook fires after post-call analysis completes, allowing you to trigger actions based on sentiment scores. [(2) ]() [(4) ]()
Automation Based on Sentiment: When a text sentiment score falls below a threshold (e.g., < -0.6) , you can automatically trigger Slack alerts to managers with call summaries, enabling proactive intervention. [(2) ]()
What's Not Available:
The sources don't indicate real-time raised voice detection or mid-call flow routing based on vocal intensity or volume changes. Sentiment analysis appears to be a post-call feature rather than a real-time trigger for conversation flow changes. [(1) ](AI Evy
05/29/2026, 8:38 AMAI Evy
05/29/2026, 8:38 AMNameless
06/09/2026, 7:32 AMAI Evy
06/09/2026, 7:32 AMAI Evy
06/09/2026, 7:32 AMotp_code
- **Description**: "The one-time password the customer received via text"
- **Variable Type**: Number
3. Verify OTP with Custom Function [(3) ]()
Use a Code Tool or Custom Function to verify the spoken OTP matches the generated one:
javascript
// Example: verify OTP using Code Tool
const spokenOTP = dv.otp_code;
const generatedOTP = metadata.generated_otp; // stored when OTP was sent
if (spokenOTP === generatedOTP) {
return { verified: true, message: "Authentication successful" };
} else {
return { verified: false, message: "Invalid code" };
}AI Evy
06/09/2026, 7:32 AMAI Evy
06/09/2026, 7:32 AMAI Evy
06/09/2026, 7:32 AM