ThunderPhone 2.0 is live.Self-serve, from 2¢/min.Read the announcement

Developer

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

Providerprovider idSetup methods
Twiliotwilioapi_key
Telnyxtelnyxapi_key, guided_telnyx
SignalWiresignalwireapi_key
Vonagevonageapi_key
Manual (any SIP trunk)manualmanual

Endpoints

MethodPathDescription
GET/v1/voip-connectionsList connections
POST/v1/voip-connectionsCreate a connection
PATCH/v1/voip-connections/{connection_id}Update credentials / name
DELETE/v1/voip-connections/{connection_id}Remove a connection
POST/v1/voip-connections/testTest raw credentials (no persisted connection)
POST/v1/voip-connections/suggest-nameInfer a display name from credentials
GET/v1/voip-connections/{connection_id}/available-numbersList numbers already owned in the provider account
POST/v1/voip-connections/{connection_id}/import-numbersImport selected numbers
GET/v1/voip-connections/{connection_id}/search-numbersSearch purchasable numbers (guided provisioning)
POST/v1/voip-connections/{connection_id}/provision-numberBuy 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"
}
FieldTypeDescription
idintegerConnection id
namestringDisplay name
providerstringtwilio, telnyx, signalwire, vonage, manual
statusstringpending, testing, connected, error (read-only)
setup_methodstringapi_key, manual, guided_telnyx
inferred_namebooleantrue when we auto-generated the name (read-only)
credentialsobjectReturned with sensitive values masked — any key containing key, token, secret, or password reads "********". Writable on create/update
sip_configobjectProvider-specific SIP config. Writable on create/update; sensitive values masked on read the same way
test_resultobjectResult payload of the most recent credential test (read-only)
last_tested_attimestamp | nullLast credential test (read-only)
numbers_importedintegerCount of phone numbers imported through this connection (read-only)
provisioning_supportedbooleanWhether guided number provisioning works for this provider (currently Telnyx and Twilio)
provisioning_messagestring | nullWhen unsupported: an explanation, e.g. "Provisioning not yet supported for twilio; use Import."

List connections

cURL
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:

  1. Call POST /v1/voip-connections/test with the credentials and get back a verification evidence id if the test passes.
  2. 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
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

FieldTypeRequiredDescription
providerstringyesProvider id (see table above)
namestringnoDefaults to a provider-inferred value
setup_methodstringnoDefaults to api_key; Telnyx supports guided_telnyx
credentialsobjectyesProvider-specific key/secret/connection fields
sip_configobjectnoExtra SIP config — e.g. {"domain": "..."}
verification_evidence_idUUIDyesId returned by POST /voip-connections/test

Returns 201 Created with the Connection object.

Errors:

StatusCondition
400Provider mismatch (evidence generated for a different provider)
400Evidence reused / stale / missing
422Credentials failed re-verification

Update a connection

cURL
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"}'
FieldTypeDescription
namestring
credentialsobjectRotate credentials. Requires a fresh verification_evidence_id
sip_configobject
verification_evidence_idUUIDRequired if credentials is present

Delete a connection

cURL
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
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" }
  }'
Response (success)
{
  "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
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..." }
  }'
Response
{
  "suggested_connection_name": "Telnyx: Acme Main",
  "suggested_connection_name_inferred": true
}

List available numbers

cURL
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
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"]}'
Response
{
  "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
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 paramRequiredDescription
countryyesTwo-letter ISO country code
typeyeslocal or toll-free
area_codenoDigits only
Response
{
  "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."
}
StatusCondition
200Search results
501Provider does not support guided provisioning — use Import
502Provider 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
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"}'
FieldTypeRequiredDescription
numberstringyesExplicit E.164 number from a search result
Response (201 Created)
{
  "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.

StatusCondition
400Invalid number / order rejected
501Provider does not support guided provisioning
502Provider API error / temporarily unavailable