Skip to main content

Swaig

SWAIG (SignalWire AI Gateway)

Summary: SWAIG is the system that lets the AI call your functions during a conversation. You define functions, SignalWire calls them via webhooks, and your responses guide the AI.

What is SWAIG?

SWAIG (SignalWire AI Gateway) connects the AI conversation to your backend logic. When the AI decides it needs to perform an action (like looking up an order or checking a balance), it calls a SWAIG function that you've defined.

┌─────────────────────────────────────────────────────────────────────────────┐
│ SWAIG Function Flow │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ User: "What's my account balance?" │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ SignalWire AI Engine │ │
│ │ 1. Transcribes speech │ │
│ │ 2. Understands intent: "user wants account balance" │ │
│ │ 3. Decides to call: get_balance(account_id="12345") │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ │ POST /swaig │
│ │ {"function": "get_balance", "argument": {"account_id": "12345"}} │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ Your Agent │ │
│ │ def get_balance(self, args, raw_data): │ │
│ │ account_id = args.get("account_id") │ │
│ │ balance = lookup_balance(account_id) │ │
│ │ return SwaigFunctionResult(f"Balance is ${balance}") │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ │ Response: {"response": "Balance is $150.00"} │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ SignalWire AI Engine │ │
│ │ Speaks: "Your account balance is one hundred fifty dollars." │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘

SWAIG in SWML

When your agent generates SWML, it includes SWAIG function definitions in the ai verb:

{
"version": "1.0.0",
"sections": {
"main": [
{
"ai": {
"SWAIG": {
"defaults": {
"web_hook_url": "https://your-agent.com/swaig"
},
"functions": [
{
"function": "get_balance",
"description": "Get the customer's current account balance",
"parameters": {
"type": "object",
"properties": {
"account_id": {
"type": "string",
"description": "The customer's account ID"
}
},
"required": ["account_id"]
}
}
]
}
}
}
]
}
}

Defining SWAIG Functions

There are three ways to define SWAIG functions in your agent:

Method 1: define_tool()

The most explicit way to register a function:

from signalwire_agents import AgentBase, SwaigFunctionResult


class MyAgent(AgentBase):
def __init__(self):
super().__init__(name="my-agent")

self.define_tool(
name="get_balance",
description="Get account balance for a customer",
parameters={
"type": "object",
"properties": {
"account_id": {
"type": "string",
"description": "The account ID to look up"
}
},
"required": ["account_id"]
},
handler=self.get_balance
)

def get_balance(self, args, raw_data):
account_id = args.get("account_id")
# Your business logic here
return SwaigFunctionResult(f"Account {account_id} has a balance of $150.00")

Method 2: @AgentBase.tool Decorator

A cleaner approach using decorators:

from signalwire_agents import AgentBase, SwaigFunctionResult


class MyAgent(AgentBase):
def __init__(self):
super().__init__(name="my-agent")

@AgentBase.tool(
name="get_balance",
description="Get account balance for a customer",
parameters={
"type": "object",
"properties": {
"account_id": {
"type": "string",
"description": "The account ID to look up"
}
},
"required": ["account_id"]
}
)
def get_balance(self, args, raw_data):
account_id = args.get("account_id")
return SwaigFunctionResult(f"Account {account_id} has a balance of $150.00")

Method 3: DataMap (Serverless)

For direct API integration without code:

from signalwire_agents import AgentBase


class MyAgent(AgentBase):
def __init__(self):
super().__init__(name="my-agent")

self.data_map.add_tool(
name="get_balance",
description="Get account balance",
parameters={
"account_id": {
"type": "string",
"description": "The account ID"
}
},
data_map={
"webhooks": [
{
"url": "https://api.example.com/accounts/${enc:args.account_id}/balance",
"method": "GET",
"headers": {
"Authorization": "Bearer ${env.API_KEY}"
},
"output": {
"response": "Account balance is $${balance}",
"action": [{"set_global_data": {"balance": "${balance}"}}]
}
}
]
}
)

Function Handler Signature

Every SWAIG function handler receives two arguments:

def my_function(self, args, raw_data):
"""
args: dict - The parsed arguments from the AI
Example: {"account_id": "12345", "include_history": True}

raw_data: dict - The complete request payload from SignalWire
Contains metadata, call info, and conversation context
"""
pass

The raw_data Payload

The raw_data contains rich context about the call:

def my_function(self, args, raw_data):
# Call metadata
# Call information (nested under 'call' key)
call_data = raw_data.get("call", {})
call_id = call_data.get("call_id") or raw_data.get("call_id") # Fallback for compatibility
call_sid = raw_data.get("call_sid")

# Caller information (from nested call object)
from_number = call_data.get("from") or call_data.get("from_number")
to_number = call_data.get("to") or call_data.get("to_number")

# Global data (shared state)
global_data = raw_data.get("global_data", {})
customer_name = global_data.get("customer_name")

# Conversation context
meta_data = raw_data.get("meta_data", {})

return SwaigFunctionResult("Processed")

SwaigFunctionResult

Always return a SwaigFunctionResult from your handlers:

from signalwire_agents import SwaigFunctionResult


def simple_response(self, args, raw_data):
# Simple text response - AI will speak this
return SwaigFunctionResult("Your order has been placed successfully.")


def response_with_actions(self, args, raw_data):
result = SwaigFunctionResult("Transferring you now.")

# Add actions to control call behavior
result.add_action("transfer", True)
result.add_action("swml", {
"version": "1.0.0",
"sections": {
"main": [
{"connect": {"to": "+15551234567", "from": "+15559876543"}}
]
}
})

return result


def response_with_data(self, args, raw_data):
result = SwaigFunctionResult("I've saved your preferences.")

# Store data for later functions
result.add_action("set_global_data", {
"user_preference": "email",
"confirmed": True
})

return result

Common Actions

ActionPurposeExample
set_global_dataStore data for later use{"key": "value"}
transferEnd AI, prepare for transferTrue
swmlExecute SWML after AI ends{"version": "1.0.0", ...}
stopEnd the AI conversationTrue
toggle_functionsEnable/disable functions[{"active": false, "function": "fn_name"}]
saySpeak text immediately"Please hold..."
play_filePlay audio file"https://example.com/hold_music.mp3"

SWAIG Request Flow

┌─────────────────────────────────────────────────────────────────────────────┐
│ SWAIG Request Processing │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ 1. SignalWire sends POST to /swaig │
│ │ │
│ ▼ │
│ 2. Agent validates authentication (Basic Auth) │
│ │ │
│ ▼ │
│ 3. Agent validates function-specific token (if configured) │
│ │ │
│ ▼ │
│ 4. Agent looks up function in ToolRegistry │
│ │ │
│ ├───► Function found ───► Execute handler │
│ │ │ │
│ │ ▼ │
│ │ Return SwaigFunctionResult │
│ │ │ │
│ │ ▼ │
│ │ Format JSON response │
│ │ │
│ └───► Function not found ───► Return error response │
│ │
│ 5. Response sent to SignalWire │
│ │ │
│ ▼ │
│ 6. AI incorporates response into conversation │
│ │
└─────────────────────────────────────────────────────────────────────────────┘

SWAIG Request Format

SignalWire sends a POST request with this structure:

{
"action": "swaig_action",
"function": "get_balance",
"argument": {
"parsed": [
{
"account_id": "12345"
}
],
"raw": "{\"account_id\": \"12345\"}"
},
"call": {
"call_id": "uuid-here",
"from": "+15551234567",
"from_number": "+15551234567",
"to": "+15559876543",
"to_number": "+15559876543",
"direction": "inbound"
},
"call_id": "uuid-here",
"call_sid": "call-sid-here",
"global_data": {
"customer_name": "John Doe"
},
"meta_data": {},
"ai_session_id": "session-uuid"
}

Important Note on Request Structure:

  • Call information (caller/callee numbers, call_id, direction) is nested under the call key
  • Always use defensive access: call_data = raw_data.get("call", {})
  • Some fields may also appear at the top level for backwards compatibility
  • Use the pattern shown in "Accessing Call Information" above for robust code

SWAIG Response Format

Your agent responds with:

{
"response": "The account balance is $150.00",
"action": [
{
"set_global_data": {
"last_balance_check": "2024-01-15T10:30:00Z"
}
}
]
}

Or for a transfer:

{
"response": "Transferring you to a specialist now.",
"action": [
{"transfer": true},
{
"swml": {
"version": "1.0.0",
"sections": {
"main": [
{"connect": {"to": "+15551234567", "from": "+15559876543"}}
]
}
}
}
]
}

Function Parameters (JSON Schema)

SWAIG functions use JSON Schema for parameter definitions:

self.define_tool(
name="search_orders",
description="Search customer orders",
parameters={
"type": "object",
"properties": {
"customer_id": {
"type": "string",
"description": "Customer ID to search for"
},
"status": {
"type": "string",
"enum": ["pending", "shipped", "delivered", "cancelled"],
"description": "Filter by order status"
},
"limit": {
"type": "integer",
"description": "Maximum number of results",
"default": 10
},
"include_details": {
"type": "boolean",
"description": "Include full order details",
"default": False
}
},
"required": ["customer_id"]
},
handler=self.search_orders
)

Webhook Security

SWAIG endpoints support multiple security layers:

  1. Basic Authentication: HTTP Basic Auth on all requests
  2. Function Tokens: Per-function security tokens
  3. HTTPS: TLS encryption in transit
## Function-specific token security
self.define_tool(
name="sensitive_action",
description="Perform a sensitive action",
parameters={...},
handler=self.sensitive_action,
secure=True # Enables per-function token validation
)

Testing SWAIG Functions

Use swaig-test to test functions locally:

## List all registered functions
swaig-test my_agent.py --list-tools

## Execute a function with arguments
swaig-test my_agent.py --exec get_balance --account_id 12345

## View the SWAIG configuration in SWML
swaig-test my_agent.py --dump-swml | grep -A 50 '"SWAIG"'

Best Practices

  1. Keep functions focused: One function, one purpose
  2. Write clear descriptions: Help the AI understand when to use each function
  3. Validate inputs: Check for required arguments
  4. Handle errors gracefully: Return helpful error messages
  5. Use global_data: Share state between function calls
  6. Log for debugging: Track function calls and responses
def get_balance(self, args, raw_data):
account_id = args.get("account_id")

if not account_id:
return SwaigFunctionResult(
"I need an account ID to look up the balance. "
"Could you provide your account number?"
)

try:
balance = self.lookup_balance(account_id)
return SwaigFunctionResult(f"Your current balance is ${balance:.2f}")
except AccountNotFoundError:
return SwaigFunctionResult(
"I couldn't find an account with that ID. "
"Could you verify the account number?"
)

Next Steps

Now that you understand how SWAIG connects AI to your code, let's trace the complete lifecycle of a request through the system.