Skip to navigation

Bring your own carrier

View as MarkdownOpen in Claude

Overview

Bring Your Own Carrier (BYOC) allows SignalWire users to retain their own carriers for connectivity instead of the vendors that we partner with. This allows you to use any carrier you want while still utilizing the powerful programmatic control for SIP from SignalWire!

This might be a good fit for you if:

  • You like your current carrier but want to use our APIs to enhance your call flow
  • You want international origination from a country we don’t yet support
  • The number cannot be ported into SignalWire
  • You want to keep control of the number rather than port it to SignalWire
  • You want to SIP trunk to/from a SIP Server (such as Kamailio)

Inbound BYOC

To send calls from your carrier to SignalWire, give the Resource that should handle them a SIP Address, then point your carrier’s trunk at that address. Every call your carrier sends to the SIP Address goes to that Resource, such as a SWML Script, a Relay Application, a Call Flow, or an AI Agent, and the Resource decides what happens on the call.

To add a SIP Address to a Resource from your SignalWire Dashboard:

  1. Open the Resource and select its Addresses & Phone Numbers tab.
  2. Select + Add, then SIP Address.
  3. On the New SIP Address page, fill out the Domain and Display Name, then select Create.

The Addresses & Phone Numbers tab lists each SIP Address with its URI below its name, in the form sip:<YOUR_SPACE>-<domain>.dapp.signalwire.com. Point your carrier’s trunk at that host.

To create one in code, use Create SIP address. For the Resources that can take a SIP Address, see Resource SIP Addresses. SIP Addresses explains the address format and its options.

Restrict who can send calls to your SIP Address

Limit the SIP Address to your carrier’s IP addresses. Under IP Whitelisting, turn on IP Auth Enabled and list your carrier’s signaling IPs in Authorized IPs, one per line or as CIDR ranges. You can set this on the New SIP Address page or later, when you edit the address. IP Auth Enabled is off by default, and until you turn it on, the SIP Address doesn’t check which IP address a call comes from.

Handle inbound calls

The Resource that holds the SIP Address handles every call your carrier sends to it. Create the Resource first, then add the SIP Address to it as described above.

The samples on this page use these placeholders:

ValueReplace with
<YOUR_PROJECT_ID>Your Project ID
<YOUR_API_TOKEN>An API token with the Voice permission
<YOUR_BASIC_AUTH_PASSWORD>A password you choose to protect your SWML server
<YOUR_PUBLIC_HOST>The public hostname of your SWML server
<YOUR_FROM_SIP_URI>The SIP URI the outbound call comes from
<YOUR_CARRIER_SIP_URI>The SIP URI your carrier gives you for outbound calls, such as sip:+15551234567@carrier.example.com

Handle inbound calls via SWML

A SWML Script runs a SWML document on each call. The Server SDKs build the document and serve it from your server with SWMLService. This one answers the call and says “Hello, welcome to SignalWire!”:

# Install: python -m pip install signalwire-sdk==3.4.1
# Save as byoc_inbound.py and run: python byoc_inbound.py
from signalwire import SWMLBuilder, SWMLService
service = SWMLService(
name="byoc-inbound",
route="/swml",
port=3000,
basic_auth=("signalwire", "<YOUR_BASIC_AUTH_PASSWORD>"),
)
SWMLBuilder(service).say("Hello, welcome to SignalWire!")
# Serves the document at /swml. Put the credentials in the URL you give
# SignalWire: https://signalwire:<YOUR_BASIC_AUTH_PASSWORD>@<YOUR_PUBLIC_HOST>/swml
service.serve()

Run the server, give it a public URL, and create a SWML Script with External URL set to https://signalwire:<YOUR_BASIC_AUTH_PASSWORD>@<YOUR_PUBLIC_HOST>/swml, as Answer a call via SWML shows. To skip the server, create a SWML Script with Hosted Script and paste the YAML or JSON into Primary Script instead. Then add the SIP Address to the script.

Handle inbound calls via WebSocket (Relay)

A Relay client runs on your server and holds a connection to SignalWire, so it needs no public URL. It subscribes to topics you list in contexts, and a Relay Application ties a topic to your SIP Address.

Run either handler below and leave it running. It subscribes to the office topic, and when a call arrives, it answers, plays a message, and hangs up.

# Install: python -m pip install signalwire-sdk==3.4.1
# Save as byoc_inbound_relay.py and run: python byoc_inbound_relay.py
from signalwire.relay import RelayClient
client = RelayClient(
project="<YOUR_PROJECT_ID>",
token="<YOUR_API_TOKEN>",
contexts=["office"],
)
@client.on_call
async def handle_call(call):
await call.answer()
async def hang_up_after_playback(_event):
if call.state != "ended":
await call.hangup()
await call.play([{
"type": "tts",
"params": {"text": "Hello, welcome to SignalWire!"},
}], on_completed=hang_up_after_playback)
await call.wait_for_ended()
client.run()

Then create the Relay Application and give it the SIP Address:

  1. Open My Resources, select + Add, then Relay Application.
  2. Enter office as the Topic. It must match the contexts value in your code.
  3. Select Create, then add the SIP Address to the Relay Application.

The Server SDKs document every Relay client option.

Handle inbound calls with Call Flow Builder

Build a Call Flow in Call Flow Builder, then add the SIP Address to it. In the example below, the Call Flow answers the call and plays a TTS message.

A Call Flow that answers the call and plays a TTS message

A Call Flow that answers the call and plays a TTS message

Test inbound calls

Place a call through your carrier to the SIP Address. You hear “Hello, welcome to SignalWire!”, then the call ends. If the call never reaches your Resource, check that your carrier sends it to the SIP Address exactly. If IP Auth Enabled is on, also check that Authorized IPs includes your carrier’s signaling IP addresses.

Outbound BYOC

To place outbound calls through your carrier, you need a SIP URI from your carrier that routes calls to your SIP trunk. Dial that URI from SWML, from Relay with the Server SDKs, with the Calling API, or from Call Flow Builder. If your carrier requires SIP authentication, SWML connect takes a username and password.

Outbound call examples

Dial your carrier via SWML

This document dials your carrier’s SIP URI with connect and sends a custom SIP header with the call. The Server SDKs build it and serve it with SWMLService, as in Handle inbound calls via SWML.

# Install: python -m pip install signalwire-sdk==3.4.1
# Save as byoc_outbound.py and run: python byoc_outbound.py
from signalwire import SWMLBuilder, SWMLService
service = SWMLService(
name="byoc-outbound",
route="/swml",
port=3000,
basic_auth=("signalwire", "<YOUR_BASIC_AUTH_PASSWORD>"),
)
SWMLBuilder(service).connect(
to="<YOUR_CARRIER_SIP_URI>",
headers=[{"name": "x-FROM_NUMBER", "value": "%2B123456789"}],
)
# Serves the document at /swml. Put the credentials in the URL you give
# SignalWire: https://signalwire:<YOUR_BASIC_AUTH_PASSWORD>@<YOUR_PUBLIC_HOST>/swml
service.serve()

Dial your carrier via WebSocket (Relay)

A Relay client dials a SIP device by its URI. Run either program below. It dials your carrier’s SIP URI, plays a message once the call is answered, and hangs up. If the carrier rejects the call or nobody answers, the dial fails with an error.

# Install: python -m pip install signalwire-sdk==3.4.1
# Save as byoc_outbound_relay.py and run: python byoc_outbound_relay.py
import asyncio
from signalwire.relay import RelayClient
client = RelayClient(
project="<YOUR_PROJECT_ID>",
token="<YOUR_API_TOKEN>",
contexts=["default"],
)
async def main():
async with client:
call = await client.dial(
devices=[[{
"type": "sip",
"params": {
"from": "<YOUR_FROM_SIP_URI>",
"to": "<YOUR_CARRIER_SIP_URI>",
"timeout": 30,
},
}]],
)
async def hang_up_after_playback(_event):
if call.state != "ended":
await call.hangup()
await call.play([{
"type": "tts",
"params": {"text": "Hello, welcome to SignalWire!"},
}], on_completed=hang_up_after_playback)
await call.wait_for_ended()
asyncio.run(main())

Dial your carrier via REST

To create an outbound call using the Calling API with curl, you can use the Create a Call endpoint with the following format:

curl -L 'https://<YOURSPACENAME>.signalwire.com/api/calling/calls' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Authorization: Basic Base64-token-here' \
--data-raw '{
"command": "dial",
"params": {
"url": "https://example.com/swml",
"from": "sip:from-sip@example-112233445566.sip.signalwire.com",
"to": "sip:from-sip@example-112233445567.sip.signalwire.com",
"caller_id": "+1234567890",
"fallback_url": "https://example.com/fallback"
}
}'

In the params you can specify a url that points to an SWML script, or you can pass a swml key with the SWML script directly.

Dial your carrier with Call Flow Builder

To create an outbound call using a Call Flow, you can use the Forward to Phone node to dial out to a SIP endpoint:

A Call Flow that forwards a call to a SIP endpoint

A Call Flow that forwards a call to a SIP endpoint

How to Get Started

If you are interested in BYOC and need additional assistance getting started, reach out to support@signalwire.com! One of our support technicians can make sure everything gets squared away so you can get up and running in no time.

Sign Up Here

If you would like to test this example out, create a SignalWire account and Space.

Please feel free to reach out to us on our Community Discord or create a Support ticket if you need guidance!