> ## Documentation Index
> Fetch the complete documentation index at: https://docs.thunderphone.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Bring your own numbers (VoIP, API)

> Connect Twilio or Telnyx — or any SIP trunk — and import phone numbers from your existing account.

Demo numbers are fine for prototyping, but production traffic should
use numbers you own through your own VoIP provider. This guide walks
you through the three-step flow: **test credentials → create
connection → import numbers → verify**.

<Note>
  The dashboard has a guided version of this whole flow under
  **Connections → VoIP** (`/dashboard/voip-connections`): a Telnyx
  guided setup that walks account creation and API keys, a
  connect-existing-account path, and a manual SIP form with an AI
  setup-guide generator.
</Note>

## Supported providers

| Provider   | `provider` id | Notes                                                                |
| ---------- | ------------- | -------------------------------------------------------------------- |
| Twilio     | `twilio`      | API key + secret                                                     |
| Telnyx     | `telnyx`      | API key; guided onboarding available (`setup_method: guided_telnyx`) |
| SignalWire | `signalwire`  | **Coming soon** — connect via Manual SIP today                       |
| Vonage     | `vonage`      | **Coming soon** — connect via Manual SIP today                       |
| Manual SIP | `manual`      | Any SIP trunk — bring your own config                                |

## 1. Test credentials

Before you create a persisted VoIP connection, probe the provider
credentials to confirm they work. This returns a
`verification_evidence_id` that you pass to the create step so the
credentials aren't double-charged for testing.

```bash theme={null}
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" }
  }'
```

```json Response theme={null}
{
  "status": "pass",
  "verification_evidence_id": "b9a2...",
  "suggested_connection_name": "Telnyx: Acme Main (+15550001234)",
  "checks": {
    "credentials_valid": true,
    "inbound_reachable": true,
    "outbound_authorized": true
  }
}
```

If any check fails, response `status` will be `fail` and `checks` will
show which step blew up. Fix the provider-side config (trunk
assignment, IP allowlist, outbound authorization) and retry.

## 2. Create the connection

Pass the `verification_evidence_id` you just got:

```bash theme={null}
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..."
  }'
```

The response is a [VoipConnection object](/api-reference/voip-connections#connection-object)
in `status="connected"`. Credentials are stored server-side and never
returned in plain text by subsequent GETs — to rotate, run a fresh
`test` and PATCH with the new evidence.

## 3. List and import numbers

Inspect the numbers visible to your credentials that aren't already
in a ThunderPhone org:

```bash theme={null}
curl https://api.thunderphone.com/v1/voip-connections/5/available-numbers \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY"
```

Then import the ones you want:

```bash theme={null}
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"]}'
```

Each import becomes a [phone number resource](/api-reference/phone-numbers)
in your org with `source="voip"` and `status="provisioning"`.

## 4. Verify each imported number

Importing registers the number as *available*; actually routing calls
through it needs a round-trip verification (inbound dial-in check +
outbound authorization probe).

```bash theme={null}
curl -X POST https://api.thunderphone.com/v1/phone-numbers/{id}/verify-voip \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY"
```

On success, `voip_verification_status` flips to `verified` and the
number enters `status="active"`. On failure, the response spells out
what failed — fix it (often a missing trunk assignment in the
provider dashboard) and call again.

## 5. Assign agents and take a call

With the number verified, you assign inbound / outbound agents the
same way as a demo number. See
[Handle inbound calls](/guides/handle-inbound-calls) and
[Place outbound calls](/guides/place-outbound-calls).

## Rotating credentials

When a provider key rotates, re-run the test-then-update flow:

```bash theme={null}
# 1. Test the new credentials
curl -X POST https://api.thunderphone.com/v1/voip-connections/test ...

# 2. PATCH the connection with the new evidence
curl -X PATCH https://api.thunderphone.com/v1/voip-connections/{id} \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "credentials": { "apiKey": "NEW_KEY..." },
    "verification_evidence_id": "fresh-evidence-id"
  }'
```

The connection stays in place — no need to re-import numbers.

***

## Next steps

<CardGroup cols={2}>
  <Card title="VoIP connections reference" icon="phone-volume" href="/api-reference/voip-connections">
    Every field on the connection, evidence, and import responses.
  </Card>

  <Card title="Phone numbers reference" icon="phone" href="/api-reference/phone-numbers">
    Assign agents, transfer between orgs, release numbers.
  </Card>

  <Card title="Place outbound calls" icon="arrow-up-right" href="/guides/place-outbound-calls">
    Now that you own the number, start placing outbound.
  </Card>
</CardGroup>
