Results Actions
Results & Actions
Summary: SwaigFunctionResult is the return type for all SWAIG functions. It contains response text for the AI to speak and optional actions like transfers, SMS, or context changes.
Basic Results
Return a simple response:
from signalwire_agents import SwaigFunctionResult
def check_order(self, args, raw_data):
order_number = args.get("order_number")
return SwaigFunctionResult(f"Order {order_number} shipped yesterday")
SwaigFunctionResult Components
| Component | Description |
|---|---|
response | Text the AI will speak to the caller |
action | List of actions to execute (transfers, SMS, context changes, etc.) |
post_process | If True, AI speaks once more before actions execute (useful for confirmations) |
Method Chaining
SwaigFunctionResult methods return self for chaining:
def transfer_to_support(self, args, raw_data):
department = args.get("department", "support")
return (
SwaigFunctionResult("I'll transfer you now")
.connect("+15551234567", final=True)
)
Call Transfer
Transfer to another number:
def transfer_call(self, args, raw_data):
department = args.get("department")
numbers = {
"sales": "+15551111111",
"support": "+15552222222",
"billing": "+15553333333"
}
dest = numbers.get(department, "+15550000000")
return (
SwaigFunctionResult(f"Transferring you to {department}")
.connect(dest, final=True)
)
Transfer options:
## Permanent transfer - call leaves agent completely
.connect("+15551234567", final=True)
## Temporary transfer - returns to agent if far end hangs up
.connect("+15551234567", final=False)
## With custom caller ID
.connect("+15551234567", final=True, from_addr="+15559876543")
## Transfer to SIP address
.connect("support@company.com", final=True)
SIP REFER transfer:
Use SIP REFER for attended transfers:
def transfer_to_extension(self, args, raw_data):
extension = args.get("extension")
return (
SwaigFunctionResult(f"Transferring to extension {extension}")
.sip_refer(f"sip:{extension}@pbx.example.com")
)
SWML-specific transfer:
Transfer with AI response for context handoff:
def transfer_with_context(self, args, raw_data):
department = args.get("department")
return (
SwaigFunctionResult("Let me connect you")
.swml_transfer(
dest="+15551234567",
ai_response=f"Customer needs help with {department}",
final=True
)
)
Send SMS
Send a text message during the call:
def send_confirmation(self, args, raw_data):
phone = args.get("phone_number")
order_id = args.get("order_id")
return (
SwaigFunctionResult("I've sent you a confirmation text")
.send_sms(
to_number=phone,
from_number="+15559876543",
body=f"Your order {order_id} has been confirmed!"
)
)
SMS with media:
def send_receipt(self, args, raw_data):
phone = args.get("phone_number")
receipt_url = args.get("receipt_url")
return (
SwaigFunctionResult("I've sent your receipt")
.send_sms(
to_number=phone,
from_number="+15559876543",
body="Here's your receipt:",
media=[receipt_url]
)
)
Payment Processing
Process credit card payments during the call:
def collect_payment(self, args, raw_data):
amount = args.get("amount")
description = args.get("description", "Purchase")
return (
SwaigFunctionResult("I'll collect your payment information now")
.pay(
payment_connector_url="https://api.example.com/payment",
charge_amount=amount,
description=description,
input_method="dtmf",
security_code=True,
postal_code=True
)
)
Payment with custom prompts:
def subscription_payment(self, args, raw_data):
return (
SwaigFunctionResult("Let's set up your monthly subscription")
.pay(
payment_connector_url="https://api.example.com/subscribe",
charge_amount="29.99",
description="Monthly Subscription",
token_type="reusable",
prompts=[
{
"say": "Please enter your credit card number",
"type": "card_number"
},
{
"say": "Enter the expiration month and year",
"type": "expiration"
}
]
)
)
Call Recording
Start and stop call recording:
def start_recording(self, args, raw_data):
return (
SwaigFunctionResult("Starting call recording")
.record_call(
control_id="my_recording",
stereo=True,
format="mp3",
direction="both"
)
)
def stop_recording(self, args, raw_data):
return (
SwaigFunctionResult("Recording stopped")
.stop_record_call(control_id="my_recording")
)
Record with auto-stop:
def record_with_timeout(self, args, raw_data):
return (
SwaigFunctionResult("Recording your message")
.record_call(
control_id="voicemail",
max_length=120.0, # Stop after 2 minutes
end_silence_timeout=3.0, # Stop after 3s silence
beep=True
)
)
Audio Tapping
Tap audio to external endpoint for monitoring or transcription. Supports WebSocket (wss://) or RTP (rtp://) URIs:
WebSocket tap:
def start_websocket_monitoring(self, args, raw_data):
return (
SwaigFunctionResult("Call monitoring started")
.tap(
uri="wss://monitor.example.com/audio",
control_id="supervisor_tap",
direction="both",
codec="PCMU"
)
)
RTP tap:
def start_rtp_tap(self, args, raw_data):
return (
SwaigFunctionResult("Recording to RTP endpoint")
.tap(
uri="rtp://192.168.1.100:5004",
control_id="rtp_tap",
direction="both",
codec="PCMU",
rtp_ptime=20
)
)
def stop_monitoring(self, args, raw_data):
return (
SwaigFunctionResult("Monitoring stopped")
.stop_tap(control_id="supervisor_tap")
)
Call Control
Hold:
Put caller on hold:
def hold_for_agent(self, args, raw_data):
return (
SwaigFunctionResult("Please hold while I find an available agent")
.hold(timeout=60) # Hold for up to 60 seconds
)
Hang Up
End the call:
def end_call(self, args, raw_data):
return (
SwaigFunctionResult("Thank you for calling. Goodbye!")
.hangup()
)
Speech Control
Direct speech with .say():
Make the AI speak specific text immediately:
def announce_status(self, args, raw_data):
order_status = args.get("status")
return (
SwaigFunctionResult()
.say(f"Your order status is: {order_status}")
)
Stop AI from speaking:
def interrupt_speech(self, args, raw_data):
return (
SwaigFunctionResult()
.stop() # Immediately stop AI speech
.say("Let me start over")
)
Wait for user input:
Pause and wait for the user to speak:
def wait_for_confirmation(self, args, raw_data):
return (
SwaigFunctionResult("I'll wait for your response")
.wait_for_user(enabled=True, timeout=10)
)
Simulate user input:
Inject text as if the user spoke it:
def auto_confirm(self, args, raw_data):
return (
SwaigFunctionResult()
.simulate_user_input("yes, I confirm")
)
Background Audio
Play audio files in the background during conversation:
def play_hold_music(self, args, raw_data):
return (
SwaigFunctionResult("Please hold")
.play_background_file(
filename="https://example.com/hold-music.mp3",
wait=False
)
)
def stop_hold_music(self, args, raw_data):
return (
SwaigFunctionResult("I'm back")
.stop_background_file()
)
Update Global Data
Store data accessible throughout the call:
def save_customer_info(self, args, raw_data):
customer_id = args.get("customer_id")
customer_name = args.get("name")
return (
SwaigFunctionResult(f"I've noted your information, {customer_name}")
.update_global_data({
"customer_id": customer_id,
"customer_name": customer_name,
"verified": True
})
)
Remove global data:
def clear_session_data(self, args, raw_data):
return (
SwaigFunctionResult("Session data cleared")
.remove_global_data(["customer_id", "verified"])
)
Metadata Management
Store function-specific metadata (separate from global data):
def track_function_usage(self, args, raw_data):
return (
SwaigFunctionResult("Usage tracked")
.set_metadata({
"function_called": "check_order",
"timestamp": "2024-01-15T10:30:00Z",
"user_id": args.get("user_id")
})
)
Remove metadata:
def clear_function_metadata(self, args, raw_data):
return (
SwaigFunctionResult("Metadata cleared")
.remove_metadata(["timestamp", "user_id"])
)
Context Switching
Advanced context switch:
Change the agent's prompt/context with new system and user prompts:
def switch_to_technical(self, args, raw_data):
return (
SwaigFunctionResult("Switching to technical support mode")
.switch_context(
system_prompt="You are now a technical support specialist. "
"Help the customer with their technical issue.",
user_prompt="The customer needs help with their account"
)
)
SWML context switch:
Switch to a named SWML context:
def switch_to_billing(self, args, raw_data):
return (
SwaigFunctionResult("Let me connect you with billing")
.swml_change_context("billing_context")
)
SWML step change:
Change to a specific workflow step:
def move_to_checkout(self, args, raw_data):
return (
SwaigFunctionResult("Moving to checkout")
.swml_change_step("checkout_step")
)
Function Control
Dynamically enable or disable functions during the call:
def enable_payment_functions(self, args, raw_data):
return (
SwaigFunctionResult("Payment functions are now available")
.toggle_functions([
{"function": "collect_payment", "active": True},
{"function": "refund_payment", "active": True},
{"function": "check_balance", "active": False}
])
)
Enable functions on timeout:
def enable_escalation_on_timeout(self, args, raw_data):
return (
SwaigFunctionResult("I'll help you with that")
.enable_functions_on_timeout(enabled=True)
)
Update AI settings:
def adjust_speech_timing(self, args, raw_data):
return (
SwaigFunctionResult("Adjusting response timing")
.update_settings({
"end_of_speech_timeout": 1000,
"attention_timeout": 30000
})
)
Set speech timeouts:
def configure_timeouts(self, args, raw_data):
return (
SwaigFunctionResult()
.set_end_of_speech_timeout(800) # 800ms
.set_speech_event_timeout(5000) # 5s
)
Conference & Rooms
Join a conference:
def join_team_conference(self, args, raw_data):
conf_name = args.get("conference_name")
return (
SwaigFunctionResult(f"Joining {conf_name}")
.join_conference(
name=conf_name,
muted=False,
beep="true",
start_conference_on_enter=True
)
)
Join a SignalWire room:
def join_support_room(self, args, raw_data):
return (
SwaigFunctionResult("Connecting to support room")
.join_room(name="support-room-1")
)
Post-Processing
Let AI speak once more before executing actions:
def transfer_with_confirmation(self, args, raw_data):
return (
SwaigFunctionResult(
"I'll transfer you to billing. Is there anything else first?",
post_process=True # AI can respond to follow-up before transfer
)
.connect("+15551234567", final=True)
)
Multiple Actions
Chain multiple actions together:
def complete_interaction(self, args, raw_data):
customer_phone = args.get("phone")
return (
SwaigFunctionResult("I've completed your request")
.update_global_data({"interaction_complete": True})
.send_sms(
to_number=customer_phone,
from_number="+15559876543",
body="Thank you for calling!"
)
)
Action Execution Order and Interactions
When chaining multiple actions, understanding how they interact is important.
Execution Order
Actions execute in the order they're added to the SwaigFunctionResult. The response text is processed first, then actions execute sequentially.
# These execute in order: 1, 2, 3
return (
SwaigFunctionResult("Starting process")
.update_global_data({"step": 1}) # 1st
.send_sms(to_number=phone, ...) # 2nd
.update_global_data({"step": 2}) # 3rd
)
Terminal Actions
Some actions end the call or AI session. Once a terminal action executes, subsequent actions may not run:
Terminal actions:
.connect(final=True)- Transfers call away permanently.hangup()- Ends the call.swml_transfer(final=True)- Transfers to another SWML endpoint
Non-terminal actions:
.update_global_data()- Continues normally.send_sms()- Continues normally.say()- Continues normally.connect(final=False)- Returns to agent if far end hangs up
Best practice: Put terminal actions last in the chain.
# Good - data saved before transfer
return (
SwaigFunctionResult("Transferring you now")
.update_global_data({"transferred": True}) # Executes
.send_sms(to_number=phone, body="...") # Executes
.connect("+15551234567", final=True) # Terminal
)
# Risky - SMS might not send
return (
SwaigFunctionResult("Transferring you now")
.connect("+15551234567", final=True) # Terminal - call leaves
.send_sms(to_number=phone, body="...") # May not execute
)
Conflicting Actions
Some action combinations don't make sense together:
| Combination | Result |
|---|---|
Multiple .connect() | Last one wins |
.hangup() then .connect() | Hangup executes, connect ignored |
.connect(final=True) then .say() | Say won't execute (call transferred) |
Multiple .update_global_data() | Merged (later keys overwrite earlier) |
Multiple .send_sms() | All execute (multiple SMS sent) |
Using post_process with Actions
When post_process=True, the AI speaks the response and can respond to follow-up before actions execute:
return (
SwaigFunctionResult(
"I'll transfer you. Anything else first?",
post_process=True # AI waits for response
)
.connect("+15551234567", final=True) # Executes after AI finishes
)
This is useful for:
- Confirming before transfers
- Last-chance questions before hangup
- Warning before destructive actions
Action Timing Considerations
Immediate actions execute as soon as the function returns:
.update_global_data().toggle_functions()
Speech actions execute during AI's turn:
.say().stop()
Call control actions affect the call flow:
.connect()- Immediate transfer.hangup()- Immediate disconnect.hold()- Immediate hold
External actions may have latency:
.send_sms()- Network delay possible.record_call()- Recording starts immediately but storage is async
Advanced: Execute Raw SWML
For advanced use cases, execute raw SWML documents directly:
def execute_custom_swml(self, args, raw_data):
swml_doc = {
"version": "1.0.0",
"sections": {
"main": [
{"play": {"url": "https://example.com/announcement.mp3"}},
{"hangup": {}}
]
}
}
return (
SwaigFunctionResult()
.execute_swml(swml_doc, transfer=False)
)
Note: Most use cases are covered by the convenience methods above. Use execute_swml() only when you need SWML features not available through other action methods.
Action Reference
Call Control Actions
| Method | Description |
|---|---|
.connect(dest, final, from_addr) | Transfer call to another number or SIP URI |
.swml_transfer(dest, ai_response, final) | SWML-specific transfer with AI response |
.sip_refer(to_uri) | SIP REFER transfer |
.hangup() | End the call |
.hold(timeout) | Put caller on hold (default 300s, max 900s) |
.send_sms(to, from, body, media) | Send SMS message |
.record_call(control_id, stereo, ...) | Start call recording |
.stop_record_call(control_id) | Stop call recording |
.tap(uri, control_id, direction, ...) | Tap call audio to external URI |
.stop_tap(control_id) | Stop call tapping |
.pay(payment_connector_url, ...) | Process payment |
.execute_swml(doc, transfer) | Execute raw SWML document |
.join_room(name) | Join a SignalWire room |
.join_conference(name, muted, ...) | Join a conference |
Speech & Audio Actions
| Method | Description |
|---|---|
.say(text) | Have AI speak specific text |
.stop() | Stop AI from speaking |
.play_background_file(url, wait) | Play background audio |
.stop_background_file() | Stop background audio |
.simulate_user_input(text) | Inject text as user speech |
.wait_for_user(enabled, timeout, answer_first) | Wait for user to speak |
Context & Workflow Actions
| Method | Description |
|---|---|
.switch_context(system_prompt, user_prompt) | Advanced context switch with new prompts |
.swml_change_context(ctx) | Switch to named context |
.swml_change_step(step) | Change to specific workflow step |
Data Management Actions
| Method | Description |
|---|---|
.update_global_data(data) | Set global session data |
.remove_global_data(keys) | Remove keys from global data |
.set_metadata(data) | Set function-specific metadata |
.remove_metadata(keys) | Remove function metadata keys |
AI Behavior Actions
| Method | Description |
|---|---|
.toggle_functions(funcs) | Enable/disable specific functions |
.enable_functions_on_timeout(enabled) | Enable functions when timeout occurs |
.update_settings(config) | Modify AI settings dynamically |
.set_end_of_speech_timeout(ms) | Adjust speech timeout |
.set_speech_event_timeout(ms) | Adjust speech event timeout |
.enable_extensive_data(enabled) | Enable extended data in webhooks |
Events
| Method | Description |
|---|---|
.swml_user_event(data) | Fire custom user event |