A SIP server that bridges SIP phone calls to the StreamCoreAI server. It handles incoming calls (e.g., from a phone network) and outgoing calls (initiated via REST API), converting audio between G.711 μ-law (PCMU) used by SIP and Opus used by StreamCoreAI's WebRTC transport.
For splitting this tree into its own Git repository and publishing github.com/streamcoreai/go-sdk, see Repository layout.
Phone Network ←→ SIP (PCMU/RTP) ←→ SIP Server ←→ WHIP (Opus/WebRTC) ←→ StreamCoreAI Server
Each call creates:
- A local RTP listener for SIP-side audio (PCMU at 8kHz)
- A StreamCoreAI SDK client connection via WHIP (Opus at 48kHz)
- A bidirectional audio bridge that transcodes and resamples between the two
- Go 1.23+
- A running StreamCoreAI server with a WHIP endpoint
Copy and edit the config file:
cp config.toml.example config.tomlKey settings in config.toml:
[sip]
listen_addr = "0.0.0.0:5060" # SIP UDP listen address
external_ip = "" # Public IP for SDP (auto-detected if empty)
rtp_port_min = 10000
rtp_port_max = 20000
[streamcoreai]
whip_endpoint = "http://localhost:8080/whip"
[api]
listen_addr = "0.0.0.0:8090"
[providers.twilio]
enabled = false
trunk_domain = "your-trunk.pstn.twilio.com"
[providers.ringcentral]
enabled = false
outbound_proxy = "sip.ringcentral.com:5060"go build -o sip-server .
./sip-server # uses config.toml in current directory
./sip-server /path/to/config.tomlGET /health
GET /providers
GET /calls
GET /calls/{id}
POST /calls
Content-Type: application/json
{
"to": "+15551234567",
"provider": "twilio"
}
DELETE /calls/{id}
A local Asterisk PBX is included for end-to-end testing — see Local Testing with Asterisk below.
Configure your Twilio Elastic SIP Trunk domain. Point the trunk's origination URI to your SIP server's address.
Configure the RingCentral outbound proxy and credentials.
A Dockerized Asterisk PBX is included so you can test the full call flow locally without any external SIP providers.
- Docker & Docker Compose
- A running StreamCoreAI server on
localhost:8080(or via the rootdocker-compose.yml) - A SIP softphone app (e.g. Oher, Zoiper, Oher Mobile, Linphone)
Softphone (6001) StreamCoreAI Server
│ │
│ SIP/RTP (PCMU) │ WHIP (Opus/WebRTC)
▼ ▼
┌──────────┐ SIP/RTP (PCMU) ┌──────────────────┐
│ Asterisk │◄────────────────────►│ SIP Server │
│ (PBX) │ │ (this project) │
└──────────┘ └──────────────────┘
172.20.0.10 172.20.0.20
1. Start Asterisk + SIP Server:
cd sip-server
docker compose up --buildThis starts:
- Asterisk on
172.20.0.10:5060(mapped to hostlocalhost:5060/udp) - SIP Server on
172.20.0.20:5060(mapped to hostlocalhost:5061/udp) with REST API onlocalhost:8090
Make sure your StreamCoreAI server is running on
localhost:8080before starting.
2. Configure your softphone:
Register with the Asterisk server using these credentials:
| Setting | Value |
|---|---|
| SIP Server | localhost (or your machine IP) |
| SIP Port | 5060 |
| Username | 6001 |
| Password | test123 |
| Transport | UDP |
A second extension 6002 / test123 is also available.
3. Test an incoming call (phone → StreamCoreAI):
From your softphone, dial 7000. Asterisk routes the call to the SIP server, which connects to StreamCoreAI. You should hear the AI agent respond.
4. Test an outgoing call (StreamCoreAI → phone):
Use the REST API to initiate a call from StreamCoreAI to your softphone:
curl -X POST http://localhost:8090/calls \
-H "Content-Type: application/json" \
-d '{"to": "6001", "provider": "asterisk"}'Your softphone should ring. Answer it to talk to the AI agent.
5. Manage calls:
# List active calls
curl http://localhost:8090/calls
# Get call details
curl http://localhost:8090/calls/<call-id>
# Hang up a call
curl -X DELETE http://localhost:8090/calls/<call-id>
# Check health
curl http://localhost:8090/health
# List providers
curl http://localhost:8090/providersIf you want to run just Asterisk in Docker and the SIP server natively:
# Start only Asterisk
docker compose up asterisk --build
# Run SIP server locally (uses config.toml which points to localhost)
go build -o sip-server .
./sip-serverThe default config.toml has the asterisk provider pointing to localhost:5060.
| Extension | Description |
|---|---|
7000 |
Routes to the StreamCoreAI SIP server |
6001 |
Test softphone extension 1 |
6002 |
Test softphone extension 2 |
9999 |
Echo test (plays back your audio for debugging) |
- No audio: Make sure the RTP port ranges don't conflict. Asterisk uses
10000-10100, SIP server uses10200-10300in Docker mode. - Registration failed: Check that Asterisk is running (
docker compose logs asterisk) and the credentials match. - "provider not configured": Make sure
providers.asterisk.enabled = truein your config. - StreamCoreAI connection fails: Verify StreamCoreAI is running at the configured WHIP endpoint. In Docker, it uses
host.docker.internal:8080.
- SIP INVITE arrives → server sends 100 Trying
- Parses remote SDP for RTP address
- Creates a StreamCoreAI SDK connection via WHIP
- Allocates a local RTP port and responds with 200 OK + SDP
- Starts bidirectional audio bridge (PCMU ↔ Opus)
- REST API request triggers
POST /calls - Creates a StreamCoreAI SDK connection
- Sends SIP INVITE to the provider's trunk with local SDP
- On 200 OK, sends ACK and starts audio bridge
- SIP → StreamCoreAI: Decode G.711 μ-law → upsample 8kHz to 48kHz → encode Opus → send via WebRTC
- StreamCoreAI → SIP: Decode Opus → downsample 48kHz to 8kHz → encode G.711 μ-law → send via RTP