Skip to content

Local Development

For AI agents: a documentation index is available at the root level at /llms.txt. Append /llms.txt to any URL for a page-level index, or .md for the markdown version of any page.

Server configuration

Custom host and port

LanguageCustom Host and Port
Pythonagent.run(host="0.0.0.0", port=8080)
TypeScriptagent.run({ host: "0.0.0.0", port: 8080 })

Using serve() directly (Python)

For more control, use serve() instead of run():

# Development server
<Badge type="tip" text="Fresh" />
agent.serve(host="127.0.0.1", port=3000)

# Listen on all interfaces
agent.serve(host="0.0.0.0", port=3000)

Development endpoints

EndpointMethodPurpose
/GET/POSTSWML document
/swaigPOSTSWAIG function calls
/post_promptPOSTPost-prompt handling
/debugGET/POSTDebug information
/debug_eventsPOSTReal-time debug events (when enable_debug_events() is active)
/healthGETHealth check ( AgentServer only)

Testing your agent

View SWML output

# Get the SWML document (use credentials from agent startup output or env vars)
curl -u "$SWML_BASIC_AUTH_USER:$SWML_BASIC_AUTH_PASSWORD" http://localhost:3000/

# Pretty print with jq
curl -u "$SWML_BASIC_AUTH_USER:$SWML_BASIC_AUTH_PASSWORD" http://localhost:3000/ | jq .

Using swaig-test CLI

# List available functions
swaig-test my_agent.py --list-tools

# Test a specific function
swaig-test my_agent.py --exec get_weather --city "Seattle"

# Dump SWML output
swaig-test my_agent.py --dump-swml

Exposing local server

SignalWire needs to reach your agent via a public URL. Use ngrok or similar:

Connection Flow: SignalWire Cloud, > ngrok tunnel, > localhost:3000

Steps:

  1. Start your agent (see run command table above)
  2. Start ngrok: ngrok http 3000
  3. Use ngrok URL in SignalWire: https://abc123.ngrok.io

Using ngrok

# Start your agent (example with Python; substitute your language's run command)
python my_agent.py

# In another terminal, start ngrok
ngrok http 3000

ngrok provides a public URL like https://abc123.ngrok.io that forwards to your local server.

Using localtunnel

# Install
npm install -g localtunnel

# Start tunnel
lt --port 3000

Environment variables for development

These environment variables work across all SDK languages:

VariablePurposeDefault
SWML_BASIC_AUTH_PASSWORDAuth passwordAuto-generated
SWML_BASIC_AUTH_USERAuth usernamesignalwire
SWML_PROXY_URL_BASEPublic URL (behind proxy)Auto-detected
SWML_ALLOW_PRIVATE_URLSAllow private IP requestsfalse
# Authentication (username defaults to 'signalwire' when not set)
# Only the password is required; omit both to disable auth for local testing
export SWML_BASIC_AUTH_PASSWORD="test123"

# Or disable authentication entirely for local testing
export SWML_BASIC_AUTH_USER=""
export SWML_BASIC_AUTH_PASSWORD=""

# Override proxy URL if behind ngrok
export SWML_PROXY_URL_BASE="https://abc123.ngrok.io"
# APP_URL is also accepted as a fallback (useful for Dokku deployments)

# Allow DataMap/skill requests to private IPs (e.g., local backend services)
export SWML_ALLOW_PRIVATE_URLS=true

Proxy URL configuration

When behind ngrok or another proxy, the SDK needs to know the public URL:

import os

# Option 1: Environment variable
os.environ['SWML_PROXY_URL_BASE'] = 'https://abc123.ngrok.io'

# Option 2: Auto-detection from X-Forwarded headers
# The SDK automatically detects proxy from request headers

Development workflow

1. Code

Write/modify your agent code.

2. Test Locally

  • swaig-test my_agent.py --dump-swml
  • swaig-test my_agent.py --exec function_name --param value

3. Run Server

python my_agent.py (or your language’s equivalent run command)

4. Expose Publicly

ngrok http 3000

5. Test with SignalWire

Point phone number to ngrok URL and make test call.

Debug mode

Enable debug logging:

import logging
logging.basicConfig(level=logging.DEBUG)

agent = MyAgent()
agent.run()

Or via environment variable:

export SIGNALWIRE_LOG_MODE=default
python my_agent.py

Hot reloading

For automatic reloading during development, use uvicorn directly:

# Install uvicorn with reload support
pip install "uvicorn[standard]"

# Run with auto-reload
uvicorn my_agent:agent._app --reload --host 0.0.0.0 --port 3000

Or create a development script:

# dev.py
from my_agent import MyAgent

agent = MyAgent()
app = agent._app  # Expose the ASGI app for uvicorn

Then run:

uvicorn dev:app --reload --port 3000

Serving static files

Use AgentServer.serve_static_files() to serve static files alongside your agents. This is useful for web dashboards, documentation, or any static content:

from signalwire import AgentServer
from pathlib import Path

# Create your agents
from my_agents import SupportAgent, SalesAgent

HOST = "0.0.0.0"
PORT = 3000

server = AgentServer(host=HOST, port=PORT)
server.register(SupportAgent(), "/support")
server.register(SalesAgent(), "/sales")

# Serve static files from web directory
web_dir = Path(__file__).parent / "web"
if web_dir.exists():
    server.serve_static_files(str(web_dir))

server.run()

Directory Structure:

my_project/
 server.py
 my_agents.py
 web/
     index.html
     styles.css
     app.js

Key Points:

  • Use server.serve_static_files(directory) to serve static files
  • Agent routes always take priority over static files
  • Requests to / serve index.html from the static directory
  • Both /support and /support/ work correctly with agents

Route Priority:

RouteHandler
/supportSupportAgent
/salesSalesAgent
/healthAgentServer health check
/*Static files (fallback)

Common development issues

IssueSolution
Port already in useUse different port: agent.run(port=8080)
401 UnauthorizedCheck SWML_BASIC_AUTH_* env vars
Functions not foundVerify function registration
SWML URL wrongSet SWML_PROXY_URL_BASE for ngrok
Connection refusedEnsure agent is running on correct port
Static files not foundCheck web_dir.exists() and path is correct

SignalWire Developer Documentation