---
title: "Connect a VoIP provider"
description: "Connect a provider you already use (Twilio or Telnyx by API key; SignalWire, Vonage, Plivo or any SIP trunk over SIP), or open a new Telnyx account with guided setup."
---

Production traffic should run on numbers you own through your own
VoIP provider. The dashboard's **Connections → VoIP** page
(`/dashboard/voip-connections`) connects your provider account; once
connected, you import numbers that support inbound **and outbound**
calling.

Click **New connection** and pick one of two routes: **Connect a
provider you already use**, or **Set up a new Telnyx account**.

## Connect a provider you already use

Pick the provider your numbers are with:

| Provider | How it connects |
|----------|-----------------|
| Twilio | API key: paste your Account SID and Auth Token |
| Telnyx | API key: paste your v2 API key |
| SignalWire | SIP setup |
| Vonage | SIP setup |
| Plivo | SIP setup |
| Other provider | SIP setup with ThunderPhone's setup assistant, for any other carrier or a PBX you run |

For Twilio and Telnyx, paste the credentials and click **Connect &
Test**. ThunderPhone verifies them and configures the provider side
for you.
For Telnyx, ThunderPhone adds one SIP connection of its own to your
Telnyx account (named `thunderphone-production-<org>-outbound-<id>`) and places
outbound calls through it, so Telnyx bills those calls to you. It uses
the outbound voice profile of the Telnyx connection your numbers are
on, so that profile's allowed countries and spend limits apply. If that
connection has no outbound voice profile and your account has exactly
one enabled profile, ThunderPhone uses that one. To use a different one,
assign it to the connection in the Telnyx portal and save the ThunderPhone
connection again. If the account has no enabled profile,
or several, assign one to the connection in the Telnyx portal and save
the ThunderPhone connection again. Until then, outbound calls fail with
`outbound_trunk_unavailable`; inbound calls keep working.

When Telnyx refuses an outbound call (for example SIP 403 Forbidden),
the refusal comes from your own Telnyx account. The usual causes are no
available credit, a caller number that is not on that Telnyx account,
or an outbound voice profile that does not allow the destination
country.

### SIP setup for SignalWire, Vonage and Plivo

These three open the [manual SIP form](#manual-sip-configuration) with
the steps for that provider at the top, a link to the provider's own
setup guide, and an example SIP domain:

- **SignalWire:** create SIP credentials for outbound calls, enter the
  SIP domain, username and password, and give each number a SWML script
  that connects incoming calls to the ThunderPhone SIP endpoint.
- **Vonage:** in the SIP Trunking dashboard, create a trunk with
  outbound credentials, enter its domain (for example
  `your-trunk.sip-us.vonage.com`) and the credentials, add the
  ThunderPhone SIP endpoint as the trunk's inbound SIP URI, and link
  your numbers to the trunk.
- **Plivo:** create an outbound trunk with a credential and enter its
  Termination SIP Domain (`<trunk-id>.zt.plivo.com`), username and
  password; create an inbound trunk whose Primary URI is the
  ThunderPhone SIP endpoint followed by `;transport=tcp`; then set each
  number's Application Type to that trunk.

The result is a manual SIP connection (`provider: "manual"` in the
API), tested and verified exactly like one entered by hand. The
connection card shows the provider's name, for example
**SignalWire · SIP**.

## Set up a new Telnyx account

No provider yet? The guided flow walks you through opening a Telnyx
account and connecting it:

<Steps>
  <Step title="Create the account">
    Click **Open Telnyx Signup**, create your account, and check
    "I've created my account."
  </Step>
  <Step title="Get an API key">
    Click **Open API Keys Page**, generate a key, and paste it into
    **API Key (v2)**. Optionally name the connection.
  </Step>
  <Step title="Connect & Test">
    Click **Connect & Test** — ThunderPhone verifies the credentials
    and provisions what it needs on the Telnyx side before saving.
  </Step>
</Steps>

### What ThunderPhone sets up in Telnyx

Unless you enter your own Connection ID, both Telnyx routes (guided
setup and pasting an API key) leave the same thing in your Telnyx
account: one SIP connection named
`thunderphone-<your organization id>`. It is an **FQDN connection**
whose FQDN record points at ThunderPhone's SIP endpoint, and every
number you import is assigned to it. Telnyx sends calls for those
numbers straight to ThunderPhone over SIP.

- Its webhook fields are empty on purpose. No webhook, Call Control
  application, TeXML application or registered SIP phone is involved.
- Don't move imported numbers to another connection or application,
  and don't register a device against it.
- If you entered your own Connection ID when connecting, ThunderPhone
  uses that connection instead and never changes it; it must be an FQDN
  connection pointing at the ThunderPhone SIP endpoint.

Before August 30, 2026, the `thunderphone-…` connection ThunderPhone
created was a **Credential connection**, which cannot receive calls.
If yours still shows as a Credential connection, contact support and
we will convert it.

## Manual SIP configuration

The SignalWire, Vonage and Plivo tiles open this form, and so does
**I'll enter every setting myself** under **Other provider**. Any SIP trunk works, including DIDWW or a PBX you run
yourself. Enter the connection name, **SIP domain / URI**,
optional username and password, port, and transport; **Advanced
Settings** adds media encryption (SRTP). The form also shows the SIP
address your trunk must send inbound calls to: `sip.thunderphone.com`,
on port 5060 for UDP or TCP, or 5061 for TLS. Over TLS, the SIP
signaling stays encrypted all the way to LiveKit Cloud, where
ThunderPhone handles the call; audio never passes through
`sip.thunderphone.com`, so SRTP works as before. Address each call to
the dialed number in E.164 form, with the same number, including the
`+`, in both the request line and the `To` header: for example
`sip:+14155550142@sip.thunderphone.com`. The `+` is what lets
ThunderPhone handle the call in the right region (see the note under
[IP allowlisting](#ip-allowlisting-and-trunk-authentication)).
Trunks set up earlier with a `<your-project>.sip.livekit.cloud`
address keep working while **Allowed Source IPs** is empty; you don't
need to change them. ThunderPhone
turns those settings into a dedicated pair of SIP trunks on LiveKit
Cloud: outbound calls
from the connection's numbers are placed to your SIP domain with your
credentials, and inbound calls to those numbers are accepted from any
address unless you fill in the optional **Allowed Source IPs** field.
Only fill it in if your carrier publishes fixed signaling IPs: a wrong
or incomplete list makes ThunderPhone reject your carrier's incoming
calls. Two buttons do the heavy lifting:

- **Generate AI Setup Guide** — writes provider-specific instructions
  for the trunk-side configuration you need.
- **Test & Save Connection** — checks that the SIP endpoint resolves
  to a public address and, for TCP/TLS, accepts a connection on the
  chosen port (UDP hosts get the DNS check only), then saves. It cannot
  place a call yet because the connection has no numbers; the real
  proof comes at import time (below).

Editing a connection later re-runs the same check on your changed
settings before anything is saved, then rebuilds the trunk and
re-verifies every number on the connection with a short test call in
the background — the numbers show as *Provisioning* until their test
call succeeds. Changing only **Allowed Source IPs** on a *Connected*
connection is different: it never affects outgoing calls, so
ThunderPhone updates the list on the inbound trunk right away and the
numbers stay *Active*. Their incoming call test resets to *not tested*;
run it again, or place a real call through your carrier, to confirm the
new list lets calls in. The test fails when its call would be refused
under the list. Stored passwords are never shown back; leave the
password blank to keep it, or tick "Remove the stored password" for an
IP-authenticated trunk.

### IP allowlisting and trunk authentication

Some carriers (DIDWW, Telnyx IP-auth connections, most PBXs behind a
firewall) only accept SIP traffic from addresses you allowlist, or
authenticate outbound calls by source IP. ThunderPhone uses two sets of
addresses.

**SIP edge.** Inbound calls you send to `sip.thunderphone.com` go
through ThunderPhone's SIP edge, which carries SIP signaling only. The
first INVITE goes to `sip.thunderphone.com`; the rest of the call's
signaling (ACK, hang-up, transfers) runs between your carrier and the
edge server that took the call, `sip-1` or `sip-2`. If your firewall
filters SIP signaling, allow all three, on UDP and TCP port 5060 and
TLS port 5061:

| Host | IP address |
|------|------------|
| `sip.thunderphone.com` | `172.170.39.202` |
| `sip-1.thunderphone.com` | `13.86.104.121` |
| `sip-2.thunderphone.com` | `20.80.98.90` |

**LiveKit Cloud.** The audio (RTP) of every call, and the SIP signaling
of outbound calls, run between your carrier and LiveKit Cloud, never
through the SIP edge. Allow **every** range below, for both SIP
signaling and media:

{/* generated:livekit-static-ips:start */}
{/*
  GENERATED BLOCK — do not edit by hand.
  Source: scripts/docs-freshness/data/livekit-static-ips.json
  Regenerate: python3 scripts/docs-freshness/generate_livekit_static_ips.py
  CI regenerates this block and fails on drift
  (scripts/docs-freshness/generators.d/30-livekit-static-ips.sh).
*/}

| IP range |
|----------|
| `143.223.88.0/21` |
| `161.115.160.0/19` |
| `153.57.128.0/18` |

LiveKit applies these ranges to Realtime, SIP signaling and media and Webhooks, and they cover calls handled in its Canada, EU, India, Japan and US regions. Region names for a pinned endpoint: `canada`, `eu`, `india`, `japan`, `us`.
{/* generated:livekit-static-ips:end */}

<Note>
  Calls sent to `sip.thunderphone.com` are handled in a covered region.
  A number with a leading `+` in the `To` header and a Japanese, Indian,
  EU/EEA, UK or Swiss country code goes to that region. Every other call
  is handled in the LiveKit region nearest ThunderPhone's SIP edge,
  currently the US. A trunk that still points at a `<your-project>.sip.livekit.cloud`
  address is routed through the LiveKit region nearest the caller, which
  can be outside the covered regions if your carrier is. Move it to
  `sip.thunderphone.com`, or pin it by inserting a region name just
  before `.sip.livekit.cloud`: for example
  `<your-project>.japan.sip.livekit.cloud`. Inbound number matching is
  unaffected.
</Note>

A few rules of thumb for the trunk itself:

- **ThunderPhone does not register.** It never sends SIP `REGISTER` to
  your carrier, so configure the trunk for IP-based and/or credential
  (username + password) authentication, not registration.
- **Outbound calls should use credentials.** Enable username/password
  authentication on the carrier side and enter the same values in the
  manual SIP form. Treat the IP allowlist as a second layer, not the
  only one — outbound calls to destinations outside the covered regions
  may originate from other addresses.
- **Inbound is the reverse direction.** The optional **Allowed Source
  IPs** field in the form is where you enter *your carrier's* signaling
  addresses so ThunderPhone only accepts calls from them. Enter IP
  addresses or CIDR ranges; hostnames never match. Leave it blank
  unless your carrier publishes a fixed list. ThunderPhone checks the
  list on every inbound call against the address its SIP edge sees, so
  once the list is set, send your calls to `sip.thunderphone.com`. A
  call from an address not on the list, or one sent straight to a
  `<your-project>.sip.livekit.cloud` address, is refused: it fails or
  connects and ends straight away.

The ranges are published by LiveKit at
[Regions, regional endpoints, and static IPs](https://docs.livekit.io/deploy/admin/regions/endpoints/);
ThunderPhone checks that page weekly and updates this guide when it
changes.

## After connecting

Import numbers from **Phone Numbers → Add number → Connect VoIP
provider** (or straight from the connection). Each imported number is
verified before it goes active. For a manual SIP connection that
verification places a short (a few seconds) test call **from your
number, through your SIP trunk, to a ThunderPhone number** — so the
username, password, transport and your carrier's outbound routing are
all exercised for real. Your carrier bills that call like any other.
If the carrier rejects it, the number stays in *Provisioning* and the
phone-numbers page shows the carrier's answer (for example "rejected
ThunderPhone's credentials (SIP 401)"); fix the connection and verify
again. If your carrier accepts the trunk but its routing plan does not
cover ThunderPhone's test number (DIDWW answers "Destination number
blacklisted by routing plan"), the number shows as *Verified · carrier
accepted*: it is usable, but calls to other destinations were not
tested. That call proves your trunk can *place* calls as the number; it
is not proof that you own the number, and it does not test calls *to*
the number; the optional incoming test below does. Verified numbers
support inbound and outbound — which unlocks
[outbound campaigns](/guides/outbound-campaigns) and programmatic
[outbound calls](/guides/place-outbound-calls).

### Incoming call test

**Verify number** opens a dialog that can also test incoming calls. The
test places a call to your number, checks that the call reaches
ThunderPhone, then hangs up without starting your agent:

- **Numbers with their own outbound SIP trunk** (manual SIP numbers, and
  Twilio numbers once ThunderPhone has set up outbound calling in your
  Twilio account) first call themselves: ThunderPhone dials the number
  from the number, through that trunk. The dialog tells you before it
  places the call. The call goes out through your carrier and, if your
  inbound routing is right, comes straight back to ThunderPhone. When it
  comes back under the number's own caller ID, that one call proves both
  that your carrier accepts ThunderPhone's calls as the number and that
  calls to the number reach ThunderPhone, in any country. Some carriers
  refuse or don't connect a call from a number to itself; that says
  nothing about your routing, so ThunderPhone then calls from one of its
  own numbers instead. If your number forwards somewhere else, that phone
  may ring twice: first showing the number's own caller ID, then
  ThunderPhone's test number.
- **Other numbers** (and the fallback above) are called from one of
  ThunderPhone's own numbers. ThunderPhone can only place these calls to
  US and Canadian numbers for now; for other numbers the test reports
  that it couldn't run, with your carrier's answer if the self-call was
  refused.

If your carrier changes the caller ID on the way, ThunderPhone can't
recognise the call, so your agent may answer it for a moment. The
incoming test is optional. Skip it and the number still works; the
phone-numbers page then shows *Incoming not tested*, and you can run it
later with **Test** or the number's **Test incoming calls** menu item.
Right after an import, the phone-numbers page also asks you to test
incoming calls on the new numbers. Nothing is dialed until you confirm in
the dialog.

If your provider sends the number somewhere else, whatever it points at
may ring for up to 8 seconds per call before ThunderPhone hangs up. If
something answers first (a Twilio app that answers and then forwards, a
phone menu), the test waits for the forwarded call to reach ThunderPhone:
up to 8 seconds on the self-call and 15 seconds on a call from a
ThunderPhone number. The result stays on the number (*Incoming* passed or
failed) until you change the connection or re-import the number, which
resets it to *not tested*. Changing a connection re-verifies outgoing
calls in the background but never calls your number unasked.

## Troubleshooting inbound calls

The connection's **Test** button checks credentials, and verification
checks outbound dialing. To check inbound, run the incoming call test
above, or call the number from a phone and look for the call in
**Call History**.
Browser **Talk** tests and bot-to-bot simulations don't go through
your carrier, so they don't show whether inbound works. A simulation
that targets the phone number (SIP loopback) does dial it through your
carrier, so an answered one does exercise inbound routing.

- **You hear your own mobile carrier's message** (for example "Welcome
  to Verizon" or "the number you have dialed is not in service"): the
  call failed before it reached ThunderPhone. Check that the number is
  active in your provider account and assigned to the ThunderPhone
  connection or trunk.
- **Busy signal or the call rings out:** the provider accepted the call
  but could not deliver it. With Telnyx, check the number's
  **Connection** in the Telnyx portal: it should be the
  `thunderphone-…` FQDN connection described above, or your own FQDN
  connection pointing at the ThunderPhone SIP endpoint. With Twilio, the
  number should be attached to the ThunderPhone SIP trunk rather than a
  Voice webhook or TwiML app.
- **Your carrier's logs show `404 No trunk found`** (manual SIP): the
  call reached ThunderPhone but matched no trunk. Check that the number
  is imported on this connection and that the trunk points at
  `sip.thunderphone.com`. If it points at a
  `<your-project>.sip.livekit.cloud` address, **Allowed Source IPs**
  must be empty: with a list set, only calls sent to
  `sip.thunderphone.com` are accepted.
- **`404 Not Found` from `sip.thunderphone.com`** (manual SIP): the
  call was not addressed to a phone number, or the request line and the
  `To` header name different numbers. Send the dialed number in both,
  as above.
- **`403 Forbidden` from `sip.thunderphone.com`:** the request line
  names another host. Address the call to `sip.thunderphone.com`, for
  example `sip:+14155550142@sip.thunderphone.com`.
- **The call reaches ThunderPhone but the wrong agent answers, or
  nobody does:** check the number's inbound agent on **Phone Numbers**.

The in-app copilot can check a number for you: ask it why calls to
the number fail. For Telnyx connections it also looks up, read-only and for
organization admins, which connection Telnyx assigns the number to.

---

## Next steps

<CardGroup cols={2}>
  <Card title="Get a phone number" icon="phone" href="/guides/get-a-phone-number">
    The full add-a-number flow, ThunderPhone numbers and VoIP.
  </Card>
  <Card title="Bring your own numbers (API)" icon="code" href="/guides/bring-your-own-numbers">
    The same connect-import-verify flow over REST.
  </Card>
  <Card title="Run outbound campaigns" icon="megaphone" href="/guides/outbound-campaigns">
    CSV lists, calling windows, and retry policy.
  </Card>
  <Card title="VoIP connections reference" icon="phone-volume" href="/api-reference/voip-connections">
    Every field on connections, evidence, and imports.
  </Card>
</CardGroup>
