VoIP Connections
Bring your own phone numbers into ThunderPhone from Twilio, Telnyx, SignalWire, or Vonage: create VoIP connections, verify credentials, and manage imported numbers.
VoIP connections let you plug your existing telephony provider into ThunderPhone. Once connected, you can list, import, and verify phone numbers from your provider account; imported numbers become Phone number resources you can assign agents to.
Supported providers
| Provider | provider id | Setup methods |
|---|---|---|
| Twilio | twilio | api_key |
| Telnyx | telnyx | api_key, guided_telnyx |
| SignalWire | signalwire | api_key |
| Vonage | vonage | api_key |
| Manual (any SIP trunk) | manual | manual |
Endpoints
| Method | Path | Description |
|---|---|---|
GET | /v1/voip-connections | List connections |
POST | /v1/voip-connections | Create a connection |
PATCH | /v1/voip-connections/{connection_id} | Update credentials / name |
DELETE | /v1/voip-connections/{connection_id} | Remove a connection |
POST | /v1/voip-connections/test | Test raw credentials (no persisted connection) |
POST | /v1/voip-connections/suggest-name | Infer a display name from credentials |
GET | /v1/voip-connections/{connection_id}/available-numbers | List numbers already owned in the provider account |
POST | /v1/voip-connections/{connection_id}/import-numbers | Import selected numbers |
GET | /v1/voip-connections/{connection_id}/search-numbers | Search purchasable numbers (guided provisioning) |
POST | /v1/voip-connections/{connection_id}/provision-number | Buy a number through the connection (guided provisioning) |
Connection object
{
"id": 5,
"name": "Acme Telnyx Main",
"provider": "telnyx",
"status": "connected",
"setup_method": "api_key",
"inferred_name": false,
"credentials": { "apiKey": "********", "connectionId": "123456" },
"sip_config": { "domain": "acme.sip.telnyx.com" },
"test_result": { "checks": { "credentials_valid": true } },
"last_tested_at": "2026-04-20T18:24:10.113Z",
"numbers_imported": 3,
"provisioning_supported": true,
"provisioning_message": null,
"created_at": "2026-04-20T18:24:10.113Z",
"updated_at": "2026-04-20T18:24:10.113Z"
}| Field | Type | Description |
|---|---|---|
id | integer | Connection id |
name | string | Display name |
provider | string | twilio, telnyx, signalwire, vonage, manual |
status | string | pending, testing, connected, error (read-only) |
setup_method | string | api_key, manual, guided_telnyx |
inferred_name | boolean | true when we auto-generated the name (read-only) |
credentials | object | Returned with sensitive values masked — any key containing key, token, secret, or password reads "********". Writable on create/update |
sip_config | object | Provider-specific SIP config. Writable on create/update; sensitive values masked on read the same way |
test_result | object | Result payload of the most recent credential test (read-only) |
last_tested_at | timestamp | null | Last credential test (read-only) |
numbers_imported | integer | Count of phone numbers imported through this connection (read-only) |
provisioning_supported | boolean | Whether guided number provisioning works for this provider (currently Telnyx and Twilio) |
provisioning_message | string | null | When unsupported: an explanation, e.g. "Provisioning not yet supported for twilio; use Import." |
List connections
curl https://api.thunderphone.com/v1/voip-connections \
-H "Authorization: Bearer sk_live_YOUR_API_KEY"Returns an array of Connection objects.
Create a connection
Creation is a two-phase flow:
- Call
POST /v1/voip-connections/testwith the credentials and get back a verification evidence id if the test passes. - Call this endpoint with the same credentials plus
verification_evidence_id— the evidence proves the credentials were already validated, avoiding double-billing for the test.
curl -X POST https://api.thunderphone.com/v1/voip-connections \
-H "Authorization: Bearer sk_live_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Acme Telnyx Main",
"provider": "telnyx",
"setup_method": "api_key",
"credentials": {
"apiKey": "KEY...",
"connectionId": "123456"
},
"sip_config": { "domain": "acme.sip.telnyx.com" },
"verification_evidence_id": "b9a2..."
}'Request fields
| Field | Type | Required | Description |
|---|---|---|---|
provider | string | yes | Provider id (see table above) |
name | string | no | Defaults to a provider-inferred value |
setup_method | string | no | Defaults to api_key; Telnyx supports guided_telnyx |
credentials | object | yes | Provider-specific key/secret/connection fields |
sip_config | object | no | Extra SIP config — e.g. {"domain": "..."} |
verification_evidence_id | UUID | yes | Id returned by POST /voip-connections/test |
Returns 201 Created with the Connection object.
Errors:
| Status | Condition |
|---|---|
400 | Provider mismatch (evidence generated for a different provider) |
400 | Evidence reused / stale / missing |
422 | Credentials failed re-verification |
Update a connection
curl -X PATCH https://api.thunderphone.com/v1/voip-connections/5 \
-H "Authorization: Bearer sk_live_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "Renamed"}'| Field | Type | Description |
|---|---|---|
name | string | |
credentials | object | Rotate credentials. Requires a fresh verification_evidence_id |
sip_config | object | |
verification_evidence_id | UUID | Required if credentials is present |
Delete a connection
curl -X DELETE https://api.thunderphone.com/v1/voip-connections/5 \
-H "Authorization: Bearer sk_live_YOUR_API_KEY"Returns 204 No Content. Phone numbers imported through the deleted
connection are not removed — they stay in your org with
voip_connection_id cleared.
Test credentials
Probes the provider API with the supplied credentials. On success, the
response includes a verification_evidence_id you can pass to
Create or Update to
create/rotate the connection without a second round trip.
curl -X POST https://api.thunderphone.com/v1/voip-connections/test \
-H "Authorization: Bearer sk_live_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"provider": "telnyx",
"credentials": { "apiKey": "KEY...", "connectionId": "123456" },
"sip_config": { "domain": "acme.sip.telnyx.com" }
}'{
"status": "pass",
"verification_evidence_id": "b9a2...",
"suggested_connection_name": "Telnyx: Acme Main (+15550001234)",
"checks": {
"credentials_valid": true,
"inbound_reachable": true,
"outbound_authorized": true
}
}Suggest a connection name
Asks the provider for an inferred display name. Used by the dashboard onboarding flow.
curl -X POST https://api.thunderphone.com/v1/voip-connections/suggest-name \
-H "Authorization: Bearer sk_live_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"provider": "telnyx",
"credentials": { "apiKey": "KEY..." }
}'{
"suggested_connection_name": "Telnyx: Acme Main",
"suggested_connection_name_inferred": true
}List available numbers
curl https://api.thunderphone.com/v1/voip-connections/5/available-numbers \
-H "Authorization: Bearer sk_live_YOUR_API_KEY"Returns the list of phone numbers visible to this connection's credentials that are not yet imported into any ThunderPhone org:
[
{
"id": "provider-handle-abc",
"number": "+15550001234",
"friendlyName": "Main office",
"type": "local",
"capabilities": ["voice"],
"region": "US-CA",
"monthlyFee": 100
}
]Import numbers
Moves the selected numbers into ThunderPhone as
Phone number resources. Imports start
in status="provisioning" — call
POST /v1/phone-numbers/{id}/verify-voip
to complete verification (inbound routing + outbound authorization
check).
curl -X POST https://api.thunderphone.com/v1/voip-connections/5/import-numbers \
-H "Authorization: Bearer sk_live_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"numbers": ["+15550001234", "+15550009999"]}'{
"imported_count": 2,
"provider_charge_estimate_cents": 0,
"provider_charge_note": "Import does not create ThunderPhone charges. Provider charges are billed by your VoIP provider only.",
"results": [
{ "id": 201, "number": "+15550001234", "status": "provisioning" },
{ "id": 202, "number": "+15550009999", "status": "provisioning" }
]
}numbers accepts 1–50 entries per request.
Search purchasable numbers
For connections where provisioning_supported is true (currently
Telnyx only), search the provider's inventory for numbers you can
buy without leaving ThunderPhone.
curl 'https://api.thunderphone.com/v1/voip-connections/5/search-numbers?country=US&area_code=415&type=local' \
-H "Authorization: Bearer sk_live_YOUR_API_KEY"| Query param | Required | Description |
|---|---|---|
country | yes | Two-letter ISO country code |
type | yes | local or toll-free |
area_code | no | Digits only |
{
"results": [
{
"id": "provider-handle-abc",
"number": "+14155550123",
"friendlyName": "+1 415 555 0123",
"type": "local",
"capabilities": ["voice"],
"region": "US-CA",
"monthlyFee": 100,
"monthlyFeeCurrency": "USD"
}
],
"provider_charge_note": "Purchasing this number and its recurring monthly service are billed directly by Telnyx. ThunderPhone does not add a provisioning fee."
}| Status | Condition |
|---|---|
200 | Search results |
501 | Provider does not support guided provisioning — use Import |
502 | Provider API error / temporarily unavailable |
Provision (buy) a number
Places a number order with the provider using the connection's credentials, then registers the number in ThunderPhone. The provider — not ThunderPhone — bills the purchase and monthly fee.
curl -X POST https://api.thunderphone.com/v1/voip-connections/5/provision-number \
-H "Authorization: Bearer sk_live_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"number": "+14155550123"}'| Field | Type | Required | Description |
|---|---|---|---|
number | string | yes | Explicit E.164 number from a search result |
{
"order_id": "ord_abc123",
"order_status": "pending",
"provider_charge_note": "Purchasing this number and its recurring monthly service are billed directly by Telnyx. ThunderPhone does not add a provisioning fee.",
"result": { "id": 203, "number": "+14155550123", "status": "provisioning" }
}Returns 201 Created when a new order was placed, or 200 OK when
the number was already owned and only registration was needed.
| Status | Condition |
|---|---|
400 | Invalid number / order rejected |
501 | Provider does not support guided provisioning |
502 | Provider API error / temporarily unavailable |