Overview
SignalWire provides three layers for building voice AI: the Call Flow Builder (visual, no-code), SWML (YAML/JSON script), and the Python Agents SDK (code-first). All three converge on the same runtime — SWML is the intermediate representation.
SWML Structure
version: 1.0.0
sections:
main:
- answer
- ai:
prompt:
text: |
You are a helpful receptionist for Acme Corp.
Be friendly, concise, and professional.
SWAIG:
functions:
- function: get_hours
description: "Get business hours. Call when user asks when we're open."
parameters:
type: object
properties: {}
web_hook_url: https://api.example.com/swaig
SWAIG Function Schema
SWAIG (SignalWire AI Gateway) is the tool-calling mechanism. Functions are defined inside ai.SWAIG.functions[].
{
"function": "get_balance",
"description": "Get customer account balance. Call when user asks about balance.",
"active": true,
"parameters": {
"type": "object",
"properties": {
"account_id": {
"type": "string",
"description": "Customer account ID"
}
},
"required": ["account_id"]
},
"web_hook_url": "https://api.example.com/swaig",
"fillers": {
"en-US": ["Let me check on that...", "One moment please..."]
}
}
When the AI invokes a function, it POSTs to web_hook_url. Response must include:
{
"response": "Your balance is $127.50.",
"action": []
}
data_map: Serverless Function Handler
For simple API passthrough without a webhook server, use data_map:
- function: get_weather
description: "Get current weather. Call when user asks about weather."
parameters:
type: object
properties:
city:
type: string
data_map:
webhooks:
- url: "https://api.weather.com/v1/current?city=${args.city}&key=API_KEY"
method: GET
output:
response: "The weather in ${args.city} is ${response.condition}, ${response.temp_f}°F."
data_map evaluates expressions inline — no server required.
Python Agents SDK (AgentBase)
from signalwire import AgentBase
class MyAgent(AgentBase):
def __init__(self):
super().__init__(
name="receptionist",
route="/",
host="0.0.0.0",
port=3000,
)
self.set_prompt_text(
"You are a helpful receptionist for Acme Corp. Be concise."
)
@self.tool(
name="get_hours",
description="Get business hours. Call when user asks when we're open.",
)
def get_hours():
return FunctionResult("We're open Monday–Friday, 9am to 5pm PST.")
agent = MyAgent()
agent.run()
Key AgentBase Methods
| Method | Purpose |
|--------|---------|
| set_prompt_text(text) | Set main system prompt |
| set_temperature(n) | AI temperature (0.0–2.0) |
| set_max_tokens(n) | Response length limit |
| set_language("en-US") | Voice language |
| set_voice("en-US-Neural2-F") | TTS voice |
| set_record_call(True) | Enable call recording |
| add_language("es-US", ...) | Multi-language support |
SWAIG Defaults
self.set_swaig_defaults(
web_hook_url="https://api.example.com/swaig",
web_hook_auth_user="user",
web_hook_auth_password="secret",
)
Applies authentication to all functions that don't override it.
Call Flow Builder Nodes
| Node | Description |
|------|-------------|
| Handle Call | Entry point (mandatory, one per flow) |
| AI Agent | Connects to an AI Agent resource |
| Gather Input | Collects DTMF or speech |
| Forward to Phone | Transfer to number or SIP endpoint |
| Play Audio or TTS | Play file, silence, or synthesized speech |
| Request | HTTP GET/POST to external API |
| Conditions | JavaScript if/else logic |
| Set Variables | Create %{vars.key} variables |
| Execute SWML | Fetch and run remote SWML document |
| Voicemail Recording | Async voicemail capture |
Variables in CFB
%{call.from} # Caller's phone number
%{call.to} # Dialed number
%{vars.account_id} # Custom variable set via Set Variables node
%{request_response.balance} # Field from a Request node response
%{record_call_url} # URL of recorded call
IVR to AI Migration Pattern
Before (IVR tree):
Press 1 for Hours → Play hours message
Press 2 for Location → Play address
Press 3 for Agent → Transfer to queue
After (AI agent):
ai:
prompt:
text: |
You handle calls for Acme Corp.
For hours, say: Monday–Friday 9am–5pm PST.
For location, say: 123 Main St, Portland OR.
For agent requests, use transfer_to_agent function.
SWAIG:
functions:
- function: transfer_to_agent
description: "Transfer caller to live agent when requested."
parameters:
type: object
properties: {}
data_map:
output:
action:
- transfer: "+15551234567"
Webhook Auth
SignalWire signs SWAIG webhook requests with HMAC-SHA256. Verify in your handler:
import hmac, hashlib
def verify_swaig_signature(body: bytes, signature: str, secret: str) -> bool:
expected = hmac.new(secret.encode(), body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, signature)
Multi-Agent Server
from signalwire import AgentServer
server = AgentServer(host="0.0.0.0", port=3000)
server.add_agent(ReceptionistAgent(), route="/reception")
server.add_agent(SupportAgent(), route="/support")
server.run()