Skip to main content

Call Flow Customization

Call Flow Customization

Summary: Control call flow with verb insertion points for pre-answer, post-answer, and post-AI actions.

Understanding Call Flow

By default, AgentBase generates a simple call flow:

answer → ai

The SDK provides three insertion points to customize this flow:

┌─────────────────────────────────────────────────────────────────┐
│ PRE-ANSWER VERBS (call still ringing) │
│ • Ringback tones, call screening, conditional routing │
└─────────────────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────────┐
│ ANSWER VERB (call connected) │
│ • Automatic when auto_answer=True (default) │
└─────────────────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────────┐
│ POST-ANSWER VERBS (before AI) │
│ • Welcome messages, disclaimers, hold music │
└─────────────────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────────┐
│ AI VERB (conversation) │
└─────────────────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────────┐
│ POST-AI VERBS (after conversation) │
│ • Cleanup, transfers, surveys, logging │
└─────────────────────────────────────────────────────────────────┘

Verb Insertion Methods

MethodPurposeCommon Uses
add_pre_answer_verb()Before answeringRingback, screening, routing
add_post_answer_verb()After answer, before AIAnnouncements, disclaimers
add_post_ai_verb()After AI endsCleanup, transfers, surveys

Pre-Answer Verbs

Pre-answer verbs run while the call is still ringing. Use them for:

  • Ringback tones: Play audio before answering
  • Call screening: Check caller ID or time
  • Conditional routing: Route based on variables
#!/usr/bin/env python3
from signalwire_agents import AgentBase


class RingbackAgent(AgentBase):
"""Agent that plays ringback tone before answering."""

def __init__(self):
super().__init__(name="ringback", port=3000)

# Play US ringback tone before answering
# IMPORTANT: auto_answer=False prevents play from answering the call
self.add_pre_answer_verb("play", {
"urls": ["ring:us"],
"auto_answer": False
})

# Configure AI
self.add_language("English", "en-US", "rime.spore")
self.prompt_add_section("Role", "You are a helpful assistant.")


if __name__ == "__main__":
agent = RingbackAgent()
agent.run()

Generated SWML:

{
"sections": {
"main": [
{"play": {"urls": ["ring:us"], "auto_answer": false}},
{"answer": {}},
{"ai": {...}}
]
}
}

Pre-Answer Safe Verbs

Only certain verbs can run before the call is answered:

VerbPre-Answer SafeNotes
playYes*Requires auto_answer: false
connectYes*Requires auto_answer: false
sleepYesWait for duration
setYesSet variables
requestYesHTTP request
switchYesVariable-based branching
condYesConditional branching
ifYesIf/then/else
evalYesEvaluate expressions
gotoYesJump to label
labelYesDefine jump target
hangupYesReject call
transferYesRoute elsewhere

*These verbs auto-answer by default. Set auto_answer: false for pre-answer use.

Available Ringback Tones

ToneDescription
ring:usUS ringback tone
ring:ukUK ringback tone
ring:itItalian ringback tone
ring:atAustrian ringback tone

Post-Answer Verbs

Post-answer verbs run after the call is connected but before the AI speaks:

#!/usr/bin/env python3
from signalwire_agents import AgentBase


class WelcomeAgent(AgentBase):
"""Agent that plays welcome message before AI."""

def __init__(self):
super().__init__(name="welcome", port=3000)

# Play welcome announcement
self.add_post_answer_verb("play", {
"url": "say:Thank you for calling Acme Corporation. "
"Your call may be recorded for quality assurance."
})

# Brief pause before AI speaks
self.add_post_answer_verb("sleep", {"time": 500})

# Configure AI
self.add_language("English", "en-US", "rime.spore")
self.prompt_add_section(
"Role",
"You are a customer service representative. "
"The caller has just heard the welcome message."
)


if __name__ == "__main__":
agent = WelcomeAgent()
agent.run()

Generated SWML:

{
"sections": {
"main": [
{"answer": {}},
{"play": {"url": "say:Thank you for calling..."}},
{"sleep": {"time": 500}},
{"ai": {...}}
]
}
}

Common Post-Answer Uses

Use CaseExample
Welcome message{"url": "say:Thank you for calling..."}
Hold music{"url": "https://example.com/hold.mp3"}
Pause{"time": 500} (milliseconds)
RecordingUse record_call=True in constructor

Post-AI Verbs

Post-AI verbs run after the AI conversation ends:

#!/usr/bin/env python3
from signalwire_agents import AgentBase


class SurveyAgent(AgentBase):
"""Agent that logs call outcome after conversation."""

def __init__(self):
super().__init__(name="survey", port=3000)

# Configure AI
self.add_language("English", "en-US", "rime.spore")
self.prompt_add_section("Role", "You are a support agent.")

# After AI ends, log the call and hang up
self.add_post_ai_verb("request", {
"url": "https://api.example.com/call-complete",
"method": "POST"
})
self.add_post_ai_verb("hangup", {})


if __name__ == "__main__":
agent = SurveyAgent()
agent.run()

Common Post-AI Uses

Use CaseVerbExample
Clean disconnecthangup{}
Transfer to humantransfer{"dest": "tel:+15551234567"}
Post-call surveypromptDTMF collection
Log outcomerequestHTTP POST to API
Connect to queueenter_queue{"name": "support"}

Complete Example

Here's an agent with all three insertion points:

#!/usr/bin/env python3
from signalwire_agents import AgentBase


class CallFlowAgent(AgentBase):
"""Agent demonstrating complete call flow customization."""

def __init__(self):
super().__init__(name="call-flow", port=3000)

# PRE-ANSWER: Ringback tone
self.add_pre_answer_verb("play", {
"urls": ["ring:us"],
"auto_answer": False
})

# POST-ANSWER: Welcome and disclaimer
self.add_post_answer_verb("play", {
"url": "say:Welcome to Acme Corporation."
})
self.add_post_answer_verb("play", {
"url": "say:This call may be recorded for quality assurance."
})
self.add_post_answer_verb("sleep", {"time": 500})

# Configure AI
self.add_language("English", "en-US", "rime.spore")
self.prompt_add_section(
"Role",
"You are a friendly customer service representative. "
"The caller has just heard the welcome message."
)
self.set_params({
"end_of_speech_timeout": 1000,
"attention_timeout": 10000
})

# POST-AI: Clean disconnect
self.add_post_ai_verb("hangup", {})


if __name__ == "__main__":
agent = CallFlowAgent()
agent.run()

Generated SWML:

{
"sections": {
"main": [
{"play": {"urls": ["ring:us"], "auto_answer": false}},
{"answer": {}},
{"play": {"url": "say:Welcome to Acme Corporation."}},
{"play": {"url": "say:This call may be recorded..."}},
{"sleep": {"time": 500}},
{"ai": {...}},
{"hangup": {}}
]
}
}

Controlling Answer Behavior

Disable Auto-Answer

Set auto_answer=False to prevent automatic answering:

class ManualAnswerAgent(AgentBase):
def __init__(self):
# Disable auto-answer
super().__init__(name="manual", port=3000, auto_answer=False)

# Pre-answer: Play ringback
self.add_pre_answer_verb("play", {
"urls": ["ring:us"],
"auto_answer": False
})

# Note: Without auto_answer, the AI will start without
# explicitly answering. Use add_answer_verb() if you need
# to answer at a specific point.

Customize Answer Verb

Use add_answer_verb() to configure the answer verb:

# Set max call duration to 1 hour
agent.add_answer_verb({"max_duration": 3600})

Dynamic Call Flow

Modify call flow based on caller information using on_swml_request():

class DynamicFlowAgent(AgentBase):
def __init__(self):
super().__init__(name="dynamic", port=3000)
self.add_language("English", "en-US", "rime.spore")
self.prompt_add_section("Role", "You are a receptionist.")

# VIP numbers get special treatment
self.vip_numbers = ["+15551234567", "+15559876543"]

def on_swml_request(self, request_data=None, callback_path=None, request=None):
call_data = (request_data or {}).get("call", {})
caller = call_data.get("from", "")

if caller in self.vip_numbers:
# VIP: No ringback, immediate welcome
self.clear_pre_answer_verbs()
self.add_post_answer_verb("play", {
"url": "say:Welcome back, valued customer!"
})
else:
# Regular caller: Ringback tone
self.add_pre_answer_verb("play", {
"urls": ["ring:us"],
"auto_answer": False
})

Clear Methods

Remove verbs from insertion points:

agent.clear_pre_answer_verbs()   # Remove all pre-answer verbs
agent.clear_post_answer_verbs() # Remove all post-answer verbs
agent.clear_post_ai_verbs() # Remove all post-AI verbs

Method Chaining

All verb insertion methods return self for chaining:

agent = AgentBase(name="chained", port=3000)
agent.add_pre_answer_verb("play", {"urls": ["ring:us"], "auto_answer": False}) \
.add_post_answer_verb("play", {"url": "say:Welcome"}) \
.add_post_answer_verb("sleep", {"time": 500}) \
.add_post_ai_verb("hangup", {})