---
title: "Connect over SIP (no phone number)"
description: "Send calls to a ThunderPhone agent straight from your PBX, carrier or SIP application, with a one-time SIP URI per call or a permanent SIP address."
---

If your own system already handles phone numbers (a PBX, a contact-center
platform, or a carrier account where you rotate numbers), you can send calls to
a ThunderPhone agent over plain SIP. You don't import or buy a number on
ThunderPhone. Your system dials a SIP address and the agent answers.

There are two kinds of SIP address:

| | **Per-call SIP URI** | **SIP endpoint** |
|---|---|---|
| Looks like | `sip:c-<token>@sip.thunderphone.com` | `sip:agent-<id>-<letters>@sip.thunderphone.com` |
| Created with | `POST /v1/calls/register-sip`, once per call | `POST /v1/sip-endpoints`, once |
| Authentication | The one-time token in the address | SIP digest username + password (and optionally a source-IP allowlist) |
| Good for | Apps that already call an API before transferring a call; per-call variables | PBXs and carriers that should just "point a trunk" at ThunderPhone |
| Valid for | 5 minutes, one call | Until you delete it |

Both kinds route to one agent in your organization, and both show up in call
history like any other inbound call.

## Per-call SIP URI

### 1. Register the call

```bash
curl -X POST https://api.thunderphone.com/v1/calls/register-sip \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "agent_id": 12,
    "to_number": "+14155550142",
    "variables": {"customer_name": "Ada"}
  }'
```

```json
{
  "call_id": 862197007562,
  "sip_uri": "sip:c-1790372992.Qm3xT8vLk2PzR7nYw4HbJa.uF6sD1gK9eW2cX5vB8nM0q@sip.thunderphone.com",
  "expires_at": "2026-09-25T21:49:52Z",
  "agent_id": 12,
  "to_number": "+14155550142"
}
```

- `to_number` (optional) is the number the caller dialed on your side. It is
  stored as the call's destination in call history, and the agent sees it as
  its own number.
- `variables` (optional) fill `{{placeholders}}` in the agent's prompt, the
  same way as for [outbound calls](/guides/call-variables).
- `call_id` is the id the call will have in call history
  (`GET /v1/calls/{call_id}`) once it connects.

### 2. Dial the URI

Send the call to `sip_uri` within 5 minutes. The URI works for one call only:
once a call has used it, it is spent, even if that call ends quickly.

With Twilio, for example, return this TwiML for the call you want to hand over:

```xml
<Response>
  <Dial>
    <Sip>sip:c-1790372992.Qm3xT8vLk2PzR7nYw4HbJa.uF6sD1gK9eW2cX5vB8nM0q@sip.thunderphone.com;transport=tcp</Sip>
  </Dial>
</Response>
```

From a PBX, dial the URI as a SIP destination. No username or password is
needed; the token is the credential, so treat the URI like a password until it
has been used.

## SIP endpoint (permanent address)

### 1. Create the endpoint

```bash
curl -X POST https://api.thunderphone.com/v1/sip-endpoints \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"agent_id": 12, "name": "Main PBX"}'
```

```json
{
  "id": 7,
  "name": "Main PBX",
  "agent_id": 12,
  "sip_uri": "sip:agent-12-jhxmginwjx@sip.thunderphone.com",
  "username": "tp409667538f83",
  "password": "8mJ0…",
  "allowed_source_cidrs": [],
  "status": "active",
  "created_at": "2026-09-25T21:46:18Z"
}
```

<Warning>
  The `password` is returned only once, when the endpoint is created. Store it
  in your PBX or carrier configuration straight away. If you lose it, rotate it
  with `POST /v1/sip-endpoints/{id}/rotate-password`.
</Warning>

### 2. Point your system at it

Configure a SIP trunk or destination with:

| Setting | Value |
|---|---|
| SIP URI | the endpoint's `sip_uri` |
| Username / password | `username` / `password` (digest authentication; ThunderPhone challenges each call) |
| Transport | TCP on port 5060 or TLS on port 5061 (recommended), or UDP on port 5060 |
| Codecs | G.711 (PCMU/PCMA) or G.722 |

<Note>
  Prefer TCP or TLS. UDP works from public addresses and from most NATs, but
  behind a "symmetric" NAT (one that uses a new port for every destination) the
  agent's hang-ups and transfers may not reach your system. A source-IP
  allowlist (below) also needs TCP or TLS.
</Note>

These transport and codec settings apply to per-call URIs too. Two network
details matter for both kinds of address:

- **Follow Record-Route.** After the first INVITE, the rest of the call's
  signalling (ACK, hang-up, transfers) goes to the specific ThunderPhone server
  that answered, which is named in the `Record-Route` header
  (`sip-1.thunderphone.com` or `sip-2.thunderphone.com`, or its IP address).
  If you restrict outbound SIP, allow those hosts as well as
  `sip.thunderphone.com`.
- **Audio goes elsewhere.** `sip.thunderphone.com` carries signalling only.
  Allow media (RTP) to the addresses and ports in the SDP answer.

Every call to the address reaches the endpoint's agent, whatever number your
caller dialed on your side. You never import numbers, so rotating them on your
side needs no change on ThunderPhone.

### Pass the dialed number (optional)

To record which of your numbers the caller dialed, send it on the INVITE in an
`X-Original-To` header (for example `X-Original-To: +14155550142`) or in a
standard `Diversion` header. ThunderPhone stores it as the call's destination.

The destination is chosen in this order: the `to_number` given when a per-call
URI was registered, then a number in `X-Original-To`, then one in `Diversion`.
If none of these gives a number, it is shown as `sip:agent-12-jhxmginwjx` (the
endpoint's address) for an endpoint, or `sip:call` for a per-call URI.

With Twilio you can add the header to the SIP URI:
`sip:agent-12-jhxmginwjx@sip.thunderphone.com;transport=tcp?X-Original-To=%2B14155550142`.

### Restrict source IPs (optional)

Set `allowed_source_cidrs` to only accept calls from your own signalling
addresses, on top of the username and password:

```bash
curl -X PATCH https://api.thunderphone.com/v1/sip-endpoints/7 \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"allowed_source_cidrs": ["203.0.113.0/28"]}'
```

Calls from any other address are refused before they reach the agent. Use the
addresses your system sends SIP signalling from (for a carrier such as Twilio,
its published signalling IP ranges), not your media addresses.

<Warning>
  The allowlist is checked for calls over TCP or TLS only. A UDP source address
  can be forged, so an endpoint with an allowlist refuses every call that
  arrives over UDP. Send calls to it with `;transport=tcp` (port 5060) or over
  TLS (port 5061).
</Warning>

## What callers and your system see

- **Caller ID:** the user part of the `From` header is stored as the caller.
  A phone number (digits, optionally with `+`, spaces, dashes, dots or
  brackets) is normalized to E.164; anything else, such as a SIP username, is
  kept as sent. At most 32 characters are kept, and a missing caller is stored
  as `anonymous`.
- **Refused calls:** an unknown or expired per-call URI gets SIP
  `404 Not Found`. An already-used per-call URI, a disabled endpoint, or a
  source outside the allowlist (including any UDP call to an endpoint with an
  allowlist) is disconnected before the agent starts: your system may see the
  call fail, or connect and then end straight away. These refusals do not
  create a call-history entry. If your organization is out of credit or has
  reached its spending limit, the call is refused and shows in call history as
  a failed call with no charge. To retry a per-call attempt, register a new URI.
- **Transfers:** the agent's cold transfers work (ThunderPhone sends a SIP
  `REFER`, which your system must accept). Warm transfers are not available on
  SIP-address calls, because there is no ThunderPhone number to show as caller
  ID for the second leg. With Twilio, the TwiML above connects the agent but
  does not handle transfers: add `referUrl="https://YOUR_SERVER/sip-transfer"`
  to the `<Dial>`, and have that webhook read `ReferTransferTarget` and return
  TwiML that dials the transfer destination (see Twilio's
  [SIP REFER guide](https://www.twilio.com/docs/voice/api/refer-to-twilio)).
- **Text messages:** the SIP address and the dialed number can't send texts.
  The agent can text during a SIP-address call only if it has a separate
  SMS-capable number set up for texting; otherwise its SMS tool is
  unavailable.
- **Registration:** ThunderPhone does not accept SIP `REGISTER`. Send calls to
  the address; don't register to it.

## Pricing

SIP-address calls are billed at the agent's normal per-minute rate. There is
no phone-number charge, because no ThunderPhone number carries the call. Your
own carrier bills its side of the call as usual. See
[Pricing](/guides/pricing).

## Test it

1. Create a per-call URI or an endpoint as above.
2. Dial it from your system, or from any SIP softphone (for an endpoint, enter
   the username and password).
3. After hanging up, open the call in **Calls** in the dashboard, or fetch it
   with `GET /v1/calls/{call_id}`. It shows the caller, the destination
   (see the order under "Pass the dialed number") and the transcript.

API details: [SIP addresses API reference](/api-reference/sip-addresses).
