Open in
Connect over SIP (no phone number)
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
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"}
}'{
"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.call_idis 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:
<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
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"}'{
"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"
}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 |
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-Routeheader (sip-1.thunderphone.comorsip-2.thunderphone.com, or its IP address). If you restrict outbound SIP, allow those hosts as well assip.thunderphone.com. - Audio goes elsewhere.
sip.thunderphone.comcarries 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:
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.
What callers and your system see
- Caller ID: the user part of the
Fromheader 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 asanonymous. - 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: addreferUrl="https://YOUR_SERVER/sip-transfer"to the<Dial>, and have that webhook readReferTransferTargetand return TwiML that dials the transfer destination (see Twilio's SIP REFER guide). - 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.
Test it
- Create a per-call URI or an endpoint as above.
- Dial it from your system, or from any SIP softphone (for an endpoint, enter the username and password).
- 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.