VAPI BYO SIP Trunk on SignalWire
SignalWire is the PSTN/SIP backbone. VAPI plugs in as a BYO SIP trunk and runs the AI conversation. SignalWire bridges via the SWML connect verb. Every integration requires a SWML script — assigned to a phone number for inbound or to a SIP Address for outbound.
VAPI Inbound — PSTN → AI
Flow:
- Caller dials a SignalWire number.
- SignalWire executes the assigned SWML.
- SWML
connectbridges to VAPI's SIP endpoint using your VAPI credential ID. - VAPI routes the call to the assigned AI assistant.
SWML
version: 1.0.0
sections:
main:
- connect:
to: sip:%{call.to}@YOUR_CREDENTIAL_ID.sip.vapi.ai
YOUR_CREDENTIAL_ID is the credentialId returned when you create the BYO trunk in VAPI.
Setup steps
1. Create the BYO inbound trunk in VAPI.
POST to the VAPI credentials/trunks API:
{
"provider": "byo-sip-trunk",
"name": "SignalWire",
"sipUri": "sip.signalwire.com",
"inboundEnabled": true
}
Save the returned credentialId. Register all 15 SignalWire SIP IPs in the trunk's IP allowlist (resolve with dig sip.signalwire.com — IPs can change, so re-check periodically).
2. Register the phone number in VAPI.
{
"provider": "byo-phone-number",
"number": "+1XXXXXXXXXX",
"credentialId": "YOUR_CREDENTIAL_ID"
}
3. Assign the AI assistant to the registered number in the VAPI dashboard.
4. Create the SWML script in SignalWire using the YAML above with your credential ID substituted in.
5. Assign the SWML script to the inbound DID in the SignalWire dashboard.
Test
Call the SignalWire DID from any phone. Call routes to VAPI and the assigned AI answers.
VAPI Outbound — AI → PSTN
Flow:
- VAPI initiates an outbound call via the BYO SIP trunk.
- SignalWire receives the SIP call at the assigned SIP Address.
- SWML strips the SIP URI to a clean E.164 destination.
connectbridges to PSTN.- Callee sees the call from your SignalWire DID.
SWML
version: 1.0.0
sections:
main:
- connect:
answer_on_bridge: true
from: "+1XXXXXXXXXX"
to: "%{call.to.replace(/^sip:/i, '').replace(/@.*/, '')}"
The to expression strips both the sip: prefix and the @domain suffix, leaving raw E.164. answer_on_bridge: true is required — otherwise the callee hears dead air.
Setup steps
1. Create the SWML script in SignalWire with your outbound caller ID.
2. Create a SIP Address in SignalWire:
- SIP → SIP Addresses → New.
- Assign the SWML script to it.
- Note the unique SIP domain (e.g.,
your-space.sip.signalwire.com).
3. Get the app password from SignalWire Support. This is not available in the portal UI. You must open a Support ticket and request the app password for your SIP Address. Without it, the outbound trunk fails authentication.
4. Create the BYO outbound trunk in VAPI:
{
"provider": "byo-sip-trunk",
"name": "SignalWire Outbound",
"sipUri": "YOUR_SIGNALWIRE_SIP_DOMAIN",
"username": "+1XXXXXXXXXX",
"password": "YOUR_APP_PASSWORD_FROM_SUPPORT",
"outboundEnabled": true
}
sipUri= SIP domain from step 2.username= SignalWire phone number in E.164 format (not an alphanumeric username).password= app password from Support.- Save the returned
credentialId.
5. Register the outbound phone number in VAPI:
{
"provider": "byo-phone-number",
"number": "+1XXXXXXXXXX",
"credentialId": "YOUR_CREDENTIAL_ID"
}
6. Assign the AI assistant to that number in VAPI.
7. Trigger outbound calls via the VAPI API specifying the assistant and destination.
SignalWire IPs for the inbound allowlist
VAPI's inbound trunk requires the SignalWire SIP IP allowlist to be complete. Resolve:
dig sip.signalwire.com
nslookup sip.signalwire.com
IPs can change. Re-check periodically. Missing IPs = silent dropped inbound calls.
Troubleshooting
Inbound not reaching VAPI
- [ ] All SignalWire IPs registered in the VAPI trunk.
- [ ]
credentialIdin SWML matches the trunk credential. - [ ] SWML script assigned to the right DID.
- [ ] Phone number registered in VAPI in E.164 format.
- [ ]
inboundEnabled: trueon the VAPI trunk. - [ ] SignalWire Dashboard → Logs shows SWML executing.
Outbound auth errors
- [ ] App password obtained from Support (not guessed).
- [ ] Auth
usernameis the E.164 phone number, not an alphanumeric string. - [ ]
sipUrimatches the SignalWire SIP Address domain exactly. - [ ]
outboundEnabled: trueon the VAPI trunk. - [ ] SWML script is assigned to the SIP Address (not a phone number).
Wrong destination dialed
- [ ] SWML
toexpression:%{call.to.replace(/^sip:/i, '').replace(/@.*/, '')}— both replaces required. - [ ] Resulting value is E.164 (e.g.,
+14155551234). - [ ] VAPI outbound logs show clean dial string.
Anti-patterns
- Generating or guessing the app password — it only comes from SignalWire Support.
- Assigning outbound SWML to a phone number — must be on a SIP Address.
- Assigning inbound SWML to a SIP Address — must be on a phone number.
- Only registering some SignalWire IPs in the VAPI inbound trunk — any missing IP drops calls.
- Auth username as an alphanumeric string instead of E.164 number — VAPI outbound auth will fail.
- Missing
answer_on_bridge: trueon outbound SWML — callee hears dead air until pickup.