Results & Actions

View as MarkdownOpen in Claude

Basic Results

Return a simple response:

from signalwire import FunctionResult
def check_order(self, args, raw_data):
order_number = args.get("order_number")
return FunctionResult(f"Order {order_number} shipped yesterday")

FunctionResult Constructor Across Languages

LanguageConstructor
PythonFunctionResult("response text")
TypeScriptnew FunctionResult("response text")

FunctionResult Components

ComponentDescription
responseA prompt injected into the model’s context. Write it as an instruction (“Tell the caller…”); the model decides what to say
actionList of actions to execute (transfers, SMS, context changes, etc.)
post_processIf True, AI speaks once more before actions execute (useful for confirmations)

Method Chaining

FunctionResult methods return self for chaining:

def transfer_to_support(self, args, raw_data):
department = args.get("department", "support")
return (
FunctionResult("Tell the caller you're transferring them now.")
.connect("+15551234567", final=True)
)

Common Action Methods Across Languages

The key action methods are available in all SDK languages with consistent naming:

ActionPython
Transfer.connect(dest, final=True)
Hang up.hangup()
Send SMS.send_sms(to, from, body)
Global data.update_global_data({})
Toggle funcs.toggle_functions([])

The Python examples below demonstrate all action methods. The same methods are available in every SDK language using the naming conventions shown above.

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 (
FunctionResult(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 (
FunctionResult(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 (
FunctionResult("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 (
FunctionResult("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 (
FunctionResult("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 (
FunctionResult("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 (
FunctionResult("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 (
FunctionResult("Starting call recording")
.record_call(
control_id="my_recording",
stereo=True,
format="mp3",
direction="both"
)
)
def stop_recording(self, args, raw_data):
return (
FunctionResult("Recording stopped")
.stop_record_call(control_id="my_recording")
)

Record with auto-stop:

def record_with_timeout(self, args, raw_data):
return (
FunctionResult("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 (
FunctionResult("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 (
FunctionResult("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 (
FunctionResult("Monitoring stopped")
.stop_tap(control_id="supervisor_tap")
)

Call Control

Hold:

Put caller on hold:

def hold_for_agent(self, args, raw_data):
return (
FunctionResult("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 (
FunctionResult("Thank you for calling. Goodbye!")
.hangup()
)

RPC Actions (Cross-Call Communication)

RPC actions enable communication between calls, essential for call screening and multi-party coordination scenarios.

Basic RPC execution:

def execute_custom_rpc(self, args, raw_data):
return (
FunctionResult("Executing RPC")
.execute_rpc(
method="ai_message",
call_id="target-call-id",
params={"role": "system", "message_text": "Hello"}
)
)

Call screening pattern - dial out while holding caller:

def screen_call_to_human(self, args, raw_data):
"""Place caller on hold and dial out to a human."""
human_number = args.get("phone_number")
caller_info = args.get("caller_name", "Unknown")
return (
FunctionResult("Please hold while I connect you.")
.hold(timeout=120)
.rpc_dial(
to_number=human_number,
from_number="+15559876543",
dest_swml=f"https://example.com/screening-agent?caller={caller_info}"
)
)

Inject message into held caller’s AI:

def notify_caller(self, args, raw_data):
"""Tell the held caller's AI to relay a message."""
caller_call_id = args.get("original_call_id")
return (
FunctionResult("I'll let them know.")
.rpc_ai_message(
call_id=caller_call_id,
message_text="The person you're trying to reach is unavailable. Please leave a message."
)
)

Release caller from hold:

def release_caller(self, args, raw_data):
"""Unhold the caller and have their AI continue."""
caller_call_id = args.get("original_call_id")
return (
FunctionResult("Returning you to the caller.")
.rpc_ai_message(caller_call_id, "You can take their message now.")
.rpc_ai_unhold(caller_call_id)
)

Dynamic Hints

Dynamically update speech recognition hints during a call. This is useful when the conversation context changes and different vocabulary becomes relevant.

Add dynamic hints:

def set_product_context(self, args, raw_data):
category = args.get("category", "general")
hints = {
"electronics": ["HDMI", "USB-C", "Bluetooth", "WiFi"],
"medical": ["prescription", "dosage", "milligrams", "refill"]
}
return (
FunctionResult(f"Switching to {category} mode")
.add_dynamic_hints(hints.get(category, []))
)

Pronunciation pattern hints:

Hints can also include pronunciation patterns for words that are commonly misrecognized:

def set_name_hints(self, args, raw_data):
name = args.get("customer_name", "")
return (
FunctionResult(f"I'll listen for {name}")
.add_dynamic_hints([
name,
{"pattern": "cab bee", "replace": "Cabby", "ignore_case": True}
])
)

Each hint can be a simple string or a dict with pattern (regex), replace (replacement text), and optional ignore_case (bool).

Clear dynamic hints:

def reset_context(self, args, raw_data):
return (
FunctionResult("Context reset")
.clear_dynamic_hints()
)

History Management

Control the conversation history visible to the AI. The replace_in_history() method lets you remove or replace a conversation turn to keep the history clean and focused.

Remove a conversation turn:

def process_sensitive_data(self, args, raw_data):
# Process the data, then remove this exchange from history
ssn = args.get("ssn")
# ... process ssn ...
return (
FunctionResult("I've verified your identity.")
.replace_in_history(True) # Removes this Q&A pair from history
)

Replace with summary text:

def collect_payment_info(self, args, raw_data):
card_number = args.get("card_number")
# ... process payment ...
return (
FunctionResult("Payment processed successfully.")
.replace_in_history("Payment information was collected and processed.")
)

When True is passed, the entire question/answer pair is removed from the AI’s conversation history. When a string is passed, the pair is replaced with that text. This is useful for keeping sensitive information out of the conversation context.

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 (
FunctionResult()
.say(f"Your order status is: {order_status}")
)

Stop AI from speaking:

def interrupt_speech(self, args, raw_data):
return (
FunctionResult()
.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 (
FunctionResult("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 (
FunctionResult()
.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 (
FunctionResult("Please hold")
.play_background_file(
filename="https://example.com/hold-music.mp3",
wait=False
)
)
def stop_hold_music(self, args, raw_data):
return (
FunctionResult("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 (
FunctionResult(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 (
FunctionResult("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 (
FunctionResult("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 (
FunctionResult("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 (
FunctionResult("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 (
FunctionResult("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 (
FunctionResult("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 (
FunctionResult("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 (
FunctionResult("I'll help you with that")
.enable_functions_on_timeout(enabled=True)
)

Update AI settings:

def adjust_speech_timing(self, args, raw_data):
return (
FunctionResult("Adjusting response timing")
.update_settings({
"end_of_speech_timeout": 1000,
"attention_timeout": 30000
})
)

Set speech timeouts:

def configure_timeouts(self, args, raw_data):
return (
FunctionResult()
.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 (
FunctionResult(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 (
FunctionResult("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 (
FunctionResult(
"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 (
FunctionResult("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 FunctionResult. The response text is processed first, then actions execute sequentially.

# These execute in order: 1, 2, 3
return (
FunctionResult("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 (
FunctionResult("Tell the caller you're transferring them 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 (
FunctionResult("Tell the caller you're transferring them 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:

CombinationResult
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 (
FunctionResult(
"Tell the caller you'll transfer them, and ask if there's 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 (
FunctionResult()
.execute_swml(swml_doc, transfer=False)
)

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

MethodDescription
.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
.execute_rpc(method, params, call_id)Execute RPC method on a call
.rpc_dial(to, from, dest_swml)Dial out with destination SWML
.rpc_ai_message(call_id, message)Inject message into another call’s AI
.rpc_ai_unhold(call_id)Release another call from hold
.join_room(name)Join a SignalWire room
.join_conference(name, muted, ...)Join a conference

Dynamic Hints & History Actions

MethodDescription
.add_dynamic_hints(hints)Add speech recognition hints dynamically during a call
.clear_dynamic_hints()Remove all dynamically added hints
.replace_in_history(text)Remove (True) or replace (string) this Q&A pair in history

Speech & Audio Actions

MethodDescription
.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

MethodDescription
.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

MethodDescription
.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

MethodDescription
.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

MethodDescription
.swml_user_event(data)Fire custom user event