***
id: be97d959-5a14-4d49-946d-e6a6d7b69199
title: Exposing Agents
sidebar-title: Exposing Agents
slug: /python/guides/exposing-agents
max-toc-depth: 3
----------------
## Exposing Your Agent to the Internet
Use ngrok to create a public URL for your local agent so SignalWire can send webhook requests to it.
### Why You Need a Public URL
SignalWire's cloud needs to reach your agent via HTTP:
### Installing ngrok
#### macOS (Homebrew)
```bash
brew install ngrok
```
#### Linux
```bash
## Download
curl -s https://ngrok-agent.s3.amazonaws.com/ngrok.asc | \
sudo tee /etc/apt/trusted.gpg.d/ngrok.asc >/dev/null && \
echo "deb https://ngrok-agent.s3.amazonaws.com buster main" | \
sudo tee /etc/apt/sources.list.d/ngrok.list
## Install
sudo apt update && sudo apt install ngrok
```
#### Windows
```powershell
## Using Chocolatey
choco install ngrok
## Or download from https://ngrok.com/download
```
#### Direct Download
Visit [ngrok.com/download](https://ngrok.com/download) and download for your platform.
### Create an ngrok Account (Free)
1. Go to [ngrok.com](https://ngrok.com) and sign up
2. Get your auth token from the dashboard
3. Configure ngrok with your token:
```bash
ngrok config add-authtoken YOUR_AUTH_TOKEN_HERE
```
This enables:
* Longer session times
* Custom subdomains (paid)
* Multiple tunnels
### Basic Usage
Start your agent in one terminal:
```bash
## Terminal 1
python my_agent.py
```
Start ngrok in another terminal:
```bash
## Terminal 2
ngrok http 3000
```
You'll see output like:
```
ngrok (Ctrl+C to quit)
Session Status online
Account your-email@example.com (Plan: Free)
Version 3.x.x
Region United States (us)
Latency 45ms
Web Interface http://127.0.0.1:4040
Forwarding https://abc123def456.ngrok-free.app -> http://localhost:3000
Connections ttl opn rt1 rt5 p50 p90
0 0 0.00 0.00 0.00 0.00
```
Your public URL is: `https://abc123def456.ngrok-free.app`
### Test the Tunnel
```bash
## Test locally (use credentials from agent startup output or env vars)
curl -u "$SWML_BASIC_AUTH_USER:$SWML_BASIC_AUTH_PASSWORD" http://localhost:3000/
## Test through ngrok (use YOUR URL from ngrok output)
curl -u "$SWML_BASIC_AUTH_USER:$SWML_BASIC_AUTH_PASSWORD" https://abc123def456.ngrok-free.app/
```
Both should return the same SWML document.
### ngrok Web Interface
ngrok provides a web interface at `http://127.0.0.1:4040` showing:
* All requests coming through the tunnel
* Request/response headers and bodies
* Timing information
* Ability to replay requests
This is invaluable for debugging SignalWire webhook calls!
### Static Domains (Recommended)
Free ngrok gives you random URLs that change each restart. For easier development, use a static domain:
#### Free Static Domain (ngrok account required)
1. Go to ngrok Dashboard → Domains
2. Create a free static domain (e.g., `your-name.ngrok-free.app`)
3. Use it:
```bash
ngrok http --url=https://your-name.ngrok-free.app 3000
```
Now your URL stays the same across restarts!
### Understanding Basic Authentication
**Important:** The SDK automatically secures your agent with HTTP Basic Authentication. Every time you start your agent, you'll see:
```
Agent 'my-agent' is available at:
URL: http://localhost:3000
Basic Auth: signalwire:7vVZ8iMTOWL0Y7-BG6xaN3qhjmcm4Sf59nORNdlF9bs (source: provided)
```
**The password changes on every restart** unless you set environment variables.
#### Setting Persistent Credentials
For development, set these environment variables to use the same credentials across restarts:
```bash
## In your .env file or shell
export SWML_BASIC_AUTH_USER=signalwire
export SWML_BASIC_AUTH_PASSWORD=your-secure-password-here
```
Then start your agent:
```bash
python my_agent.py
```
Now it will show:
```
Basic Auth: signalwire:your-secure-password-here (source: environment)
```
**Why this matters:**
* SignalWire needs these credentials to call your agent
* Random passwords mean reconfiguring SignalWire on every restart
* Set environment variables once for consistent development
### Configure Your Agent for ngrok
Set the `SWML_PROXY_URL_BASE` environment variable so your agent generates correct webhook URLs:
```bash
## In your .env file
SWML_PROXY_URL_BASE=https://your-name.ngrok-free.app
SWML_BASIC_AUTH_USER=signalwire
SWML_BASIC_AUTH_PASSWORD=your-secure-password-here
```
Or set them when running:
```bash
SWML_PROXY_URL_BASE=https://your-name.ngrok-free.app \
SWML_BASIC_AUTH_USER=signalwire \
SWML_BASIC_AUTH_PASSWORD=your-secure-password-here \
python my_agent.py
```
This ensures:
* SWAIG function webhook URLs point to your public ngrok URL, not localhost
* Authentication credentials remain consistent across restarts
### Complete Development Setup
Here's the full workflow:
```bash
## Terminal 1: Start ngrok with static domain
ngrok http --url=https://your-name.ngrok-free.app 3000
## Terminal 2: Start agent with environment variables
export SWML_PROXY_URL_BASE=https://your-name.ngrok-free.app
export SWML_BASIC_AUTH_USER=signalwire
export SWML_BASIC_AUTH_PASSWORD=your-secure-password-here
python my_agent.py
## Terminal 3: Test (use the credentials from Terminal 2)
curl -u signalwire:your-secure-password-here https://your-name.ngrok-free.app/
curl -u signalwire:your-secure-password-here https://your-name.ngrok-free.app/debug
```
### Using a Script
Create `start-dev.sh`:
```bash
#!/bin/bash
## start-dev.sh - Start development environment
NGROK_DOMAIN="your-name.ngrok-free.app"
AUTH_USER="signalwire"
AUTH_PASS="your-secure-password-here"
echo "Starting development environment..."
echo "Public URL: https://${NGROK_DOMAIN}"
echo "Basic Auth: ${AUTH_USER}:${AUTH_PASS}"
echo ""
## Start ngrok in background
ngrok http --url=https://${NGROK_DOMAIN} 3000 &
NGROK_PID=$!
## Wait for ngrok to start
sleep 2
## Start agent with environment variables
export SWML_PROXY_URL_BASE="https://${NGROK_DOMAIN}"
export SWML_BASIC_AUTH_USER="${AUTH_USER}"
export SWML_BASIC_AUTH_PASSWORD="${AUTH_PASS}"
python my_agent.py
## Cleanup on exit
trap "kill $NGROK_PID 2>/dev/null" EXIT
```
Make it executable:
```bash
chmod +x start-dev.sh
./start-dev.sh
```
### Alternative Tunneling Solutions
#### Cloudflare Tunnel (Free)
```bash
## Install cloudflared
brew install cloudflared # macOS
## Quick tunnel (no account needed)
cloudflared tunnel --url http://localhost:3000
```
#### localtunnel (Free, no signup)
```bash
## Install
npm install -g localtunnel
## Run
lt --port 3000
```
#### tailscale Funnel (Requires Tailscale)
```bash
## If you use Tailscale
tailscale funnel 3000
```
### Production Alternatives
For production, don't use ngrok. Instead:
| Option | Description |
| -------------- | --------------------------------------------------- |
| **Cloud VM** | Deploy to AWS, GCP, Azure, DigitalOcean |
| **Serverless** | AWS Lambda, Google Cloud Functions, Azure Functions |
| **Container** | Docker on Kubernetes, ECS, Cloud Run |
| **VPS** | Any server with a public IP |
See the [Deployment](/docs/agents-sdk/python/guides/local-development) chapter for production deployment guides.
### Troubleshooting
#### ngrok shows "ERR\_NGROK\_108"
Your auth token is invalid or expired. Get a new one from the ngrok dashboard:
```bash
ngrok config add-authtoken YOUR_NEW_TOKEN
```
#### Connection refused
Your agent isn't running or is on a different port:
```bash
## Check agent is running
curl http://localhost:3000/
## If using different port
ngrok http 8080
```
#### Webhook URLs still show localhost
Set `SWML_PROXY_URL_BASE`:
```bash
export SWML_PROXY_URL_BASE=https://your-domain.ngrok-free.app
python my_agent.py
```
#### ngrok tunnel expires
Free ngrok tunnels expire after a few hours. Solutions:
* Restart ngrok
* Use a static domain (stays same after restart)
* Upgrade to paid ngrok plan
* Use an alternative like Cloudflare Tunnel
### What's Next?
Your agent is now accessible at a public URL. You're ready to connect it to SignalWire!
### You've Completed Phase 1!
* Installed the SDK
* Created your first agent
* Set up development environment
* Exposed agent via ngrok
Your agent is ready at: `https://your-domain.ngrok-free.app`
**Next Chapter: [Core Concepts](/docs/agents-sdk/python/guides/architecture)** - Deep dive into SWML, SWAIG, and agent architecture
**Or jump to: [SignalWire Integration](/docs/agents-sdk/python/guides/account-setup)** - Connect your agent to phone numbers