Request Lifecycle

View as MarkdownOpen in Claude

The Complete Call Flow

Understanding the request lifecycle helps you debug issues and optimize your agents. Here’s the complete flow:

Complete call lifecycle.
Complete Call Lifecycle

Phase 1: Call Setup

When a call arrives at SignalWire:

Call setup phase.
Call Setup

Key points:

  • SignalWire knows which agent to contact based on phone number configuration
  • The request includes Basic Auth credentials
  • POST is the default; GET requests are also supported for SWML retrieval

Phase 2: SWML Generation

Your agent builds and returns the SWML document:

## Inside AgentBase._render_swml()
def _render_swml(self, request_body=None):
"""Generate SWML document for this agent."""
# 1. Build the prompt (POM or text)
prompt = self._build_prompt()
# 2. Collect all SWAIG functions
functions = self._tool_registry.get_functions()
# 3. Generate webhook URLs with security tokens
webhook_url = self._build_webhook_url("/swaig")
# 4. Assemble AI configuration
ai_config = {
"prompt": prompt,
"post_prompt": self._post_prompt,
"post_prompt_url": self._build_webhook_url("/post_prompt"),
"SWAIG": {
"defaults": {"web_hook_url": webhook_url},
"functions": functions
},
"hints": self._hints,
"languages": self._languages,
"params": self._params
}
# 5. Build complete SWML document
swml = {
"version": "1.0.0",
"sections": {
"main": [
{"answer": {}},
{"ai": ai_config}
]
}
}
return swml

Phase 3: AI Conversation

Once SignalWire has the SWML, it executes the instructions:

AI conversation loop.
AI Conversation Loop

AI Parameters that control this loop:

ParameterDefaultPurpose
end_of_speech_timeout500msWait time after user stops speaking
attention_timeout15000msMax silence before AI prompts
inactivity_timeout30000msMax silence before ending call
barge_match_string-Words that immediately interrupt AI

Phase 4: Function Calls

When the AI needs to call a function:

SWAIG function call phase.
SWAIG Function Call

Phase 5: Call End

When the call ends, the post-prompt summary is sent:

Call ending phase.
Call Ending

Handling Post-Prompt

Configure post-prompt handling in your agent:

LanguageSet Post-Prompt
Pythonagent.set_post_prompt("Summarize this call...")
TypeScriptagent.setPostPrompt('Summarize this call...')
from signalwire import AgentBase
class MyAgent(AgentBase):
def __init__(self):
super().__init__(name="my-agent")
self.set_post_prompt(
"Summarize this call including: "
"1) The caller's main question or issue "
"2) How it was resolved "
"3) Any follow-up actions needed"
)
def on_post_prompt(self, data):
"""Handle the call summary."""
summary = data.get("post_prompt_data", {})
call_id = data.get("call_id")
self.log_call_summary(call_id, summary)

Request/Response Headers

SWML Request (GET or POST /)

GET / HTTP/1.1
Host: your-agent.com
Authorization: Basic c2lnbmFsd2lyZTpwYXNzd29yZA==
Accept: application/json
X-Forwarded-For: signalwire-ip
X-Forwarded-Proto: https

SWML Response

HTTP/1.1 200 OK
Content-Type: application/json
{"version": "1.0.0", "sections": {...}}

SWAIG Request (POST /swaig)

POST /swaig HTTP/1.1
Host: your-agent.com
Authorization: Basic c2lnbmFsd2lyZTpwYXNzd29yZA==
Content-Type: application/json
{"action": "swaig_action", "function": "...", ...}

SWAIG Response

HTTP/1.1 200 OK
Content-Type: application/json
{"response": "...", "action": [...]}

Debugging the Lifecycle

View SWML Output

## See what your agent returns
curl -u signalwire:password http://localhost:3000/ | jq '.'
## Using swaig-test
swaig-test my_agent.py --dump-swml

Test Function Calls

## Call a function directly
swaig-test my_agent.py --exec get_balance --account_id 12345
## With verbose output
swaig-test my_agent.py --exec get_balance --account_id 12345 --verbose

Monitor Live Traffic

from signalwire import AgentBase
class DebugAgent(AgentBase):
def __init__(self):
super().__init__(name="debug-agent")
def on_swml_request(self, request_data=None, callback_path=None, request=None):
"""Called when SWML is requested."""
if request:
print(f"SWML requested from: {request.client.host}")
print(f"Headers: {dict(request.headers)}")
def on_swaig_request(self, function_name, args, raw_data):
"""Called before each SWAIG function."""
print(f"Function called: {function_name}")
print(f"Arguments: {args}")
print(f"Call ID: {raw_data.get('call_id')}")

Error Handling

SWML Errors

If your agent can’t generate SWML:

def _render_swml(self):
try:
return self._build_swml()
except Exception as e:
# Return minimal valid SWML
return {
"version": "1.0.0",
"sections": {
"main": [
{"answer": {}},
{"play": {"url": "https://example.com/error.mp3"}},
{"hangup": {}}
]
}
}

SWAIG Errors

If a function fails:

def get_balance(self, args, raw_data):
try:
balance = self.lookup_balance(args.get("account_id"))
return FunctionResult(f"Your balance is ${balance}")
except DatabaseError:
return FunctionResult(
"I'm having trouble accessing account information right now. "
"Please try again in a moment."
)
except Exception as e:
# Log the error but return user-friendly message
self.logger.error(f"Function error: {e}")
return FunctionResult(
"I encountered an unexpected error. "
"Let me transfer you to a representative."
)

Next Steps

Now that you understand the complete lifecycle, let’s look at how security works throughout this flow.