# Bring Your Own Carrier (BYOC)

> Connect your own SIP trunk — Telnyx, Twilio, Vonage, Plivo, or a PBX — to the Edesy Voice Platform. Register carriers, gateways, and numbers over the trunk-portal API.

Source: https://edesy.in/docs/sip-trunk-for-ai-voice-agents/connect/byoc

If you already have your own SIP trunk (Telnyx, Twilio, Vonage, Plivo, or any SIP provider), BYOC lets you route your AI voice agent's inbound and outbound calls over **your** carrier. You register the trunk with Edesy, add its SIP endpoints, and register your numbers — then use them with your agent.

All resources are **tenant-isolated**: you can only see and manage your own trunks, gateways, and numbers.

## The flow

1. Register your SIP trunk as a **Carrier**.
2. Add the trunk's **SIP Gateway** endpoint(s).
3. Register your **Phone Numbers** (DIDs) and link them to the carrier.
4. Point your calls through the trunk — inbound and outbound.

## Base path

```
https://voice-api.edesy.in/v1/trunk-portal
```

All endpoints require `Authorization: Bearer <token>` and are automatically scoped to your tenant. See [Authentication](https://edesy.in/docs/sip-trunk-for-ai-voice-agents/getting-started/authentication).

> Prefer a UI? You can also register carriers, gateways, and numbers from the portal at **[voice-app.edesy.in](https://voice-app.edesy.in/)** — and generate the API token used here.

## 1. Carriers (SIP trunks)

A **Carrier** represents a SIP trunk provider (e.g. "My Telnyx Trunk", "Office PBX").

### Create a carrier

```
POST /v1/trunk-portal/carriers
```

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `name` | string | Yes | Display name for the trunk |
| `description` | string | No | Optional description |
| `requires_register` | boolean | No | `true` if the trunk requires SIP REGISTER authentication |
| `register_username` | string | Conditional | SIP auth username (required if `requires_register` is true) |
| `register_password` | string | Conditional | SIP auth password (required if `requires_register` is true) |
| `register_sip_realm` | string | No | SIP realm/domain for registration (e.g. `sip.telnyx.com`) |
| `tech_prefix` | string | No | Digits prepended to outbound numbers for routing |
| `e164_leading_plus` | boolean | No | Whether to add a `+` prefix to numbers |
| `dtmf_type` | string | No | DTMF method: `rfc2833` (default), `tones`, or `info` |
| `outbound_sip_proxy` | string | No | Outbound SIP proxy address if needed |

**Registration-based trunk (Telnyx, Twilio):**

```bash
curl -X POST https://voice-api.edesy.in/v1/trunk-portal/carriers \
  -H "Authorization: Bearer vp_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "My Telnyx Trunk",
    "requires_register": true,
    "register_username": "myuser",
    "register_password": "mypassword",
    "register_sip_realm": "sip.telnyx.com"
  }'
```

**IP-authenticated trunk (static IP):**

```bash
curl -X POST https://voice-api.edesy.in/v1/trunk-portal/carriers \
  -H "Authorization: Bearer vp_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Office PBX",
    "description": "Direct trunk from our office PBX",
    "tech_prefix": "99"
  }'
```

**Response (201 Created):**

```json
{
  "data": {
    "sid": "429ea78b-9af7-4ff7-963f-41e0762b9c7a",
    "name": "My Telnyx Trunk",
    "metadata": {
      "description": "",
      "requires_register": true,
      "tech_prefix": "",
      "dtmf_type": ""
    },
    "created_at": "2026-04-05T12:00:00Z"
  }
}
```

> Save the carrier `sid` — you need it to add gateways and register phone numbers.

**Other carrier operations:**

| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/v1/trunk-portal/carriers` | List carriers |
| `GET` | `/v1/trunk-portal/carriers/:carrier_sid` | Get one carrier |
| `PATCH` | `/v1/trunk-portal/carriers/:carrier_sid` | Update (same body as create) |
| `DELETE` | `/v1/trunk-portal/carriers/:carrier_sid` | Delete — fails if numbers are still assigned; remove them first |

## 2. SIP gateways

A **Gateway** is the actual network endpoint (IP or hostname) of the SIP trunk. Each carrier can have one or more gateways (e.g. primary + failover).

### Create a gateway

```
POST /v1/trunk-portal/carriers/:carrier_sid/gateways
```

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `ipv4` | string | Yes | IP address or hostname of the SIP server |
| `port` | integer | No | SIP port (default `5060`) |
| `inbound` | boolean | No | Accept inbound calls from this gateway |
| `outbound` | boolean | No | Send outbound calls to this gateway |
| `netmask` | integer | No | CIDR netmask for IP matching (default `32`) |
| `protocol` | string | No | Transport: `udp` (default), `tcp`, `tls`, `tls/srtp` |

```bash
curl -X POST https://voice-api.edesy.in/v1/trunk-portal/carriers/429ea78b-.../gateways \
  -H "Authorization: Bearer vp_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "ipv4": "sip.telnyx.com",
    "port": 5060,
    "inbound": true,
    "outbound": true,
    "protocol": "udp"
  }'
```

**Response (201 Created):**

```json
{
  "data": {
    "sid": "abc12345-...",
    "carrier_sid": "429ea78b-...",
    "name": "sip.telnyx.com:5060",
    "metadata": {
      "ipv4": "sip.telnyx.com",
      "port": 5060,
      "inbound": true,
      "outbound": true,
      "protocol": "udp"
    }
  }
}
```

**Other gateway operations:**

| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/v1/trunk-portal/carriers/:carrier_sid/gateways` | List gateways |
| `DELETE` | `/v1/trunk-portal/carriers/:carrier_sid/gateways/:gateway_sid` | Delete a gateway |

## 3. Phone numbers

**Phone Numbers** (DIDs) are the numbers you want to use with the voice agent. Each must be linked to a carrier and, optionally, an application.

### Register a number

```
POST /v1/trunk-portal/numbers
```

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `number` | string | Yes | Phone number in E.164 format (e.g. `+919876543210`) |
| `carrier_sid` | string | Yes | The carrier (trunk) this number belongs to |
| `application_sid` | string | No | Application to handle inbound calls to this number |

```bash
curl -X POST https://voice-api.edesy.in/v1/trunk-portal/numbers \
  -H "Authorization: Bearer vp_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "number": "+919876543210",
    "carrier_sid": "429ea78b-...",
    "application_sid": "207fd2f9-..."
  }'
```

**Response (201 Created):**

```json
{
  "data": {
    "sid": "num12345-...",
    "number": "+919876543210",
    "metadata": {
      "carrier_sid": "429ea78b-...",
      "application_sid": "207fd2f9-..."
    }
  }
}
```

**Other number operations:**

| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/v1/trunk-portal/numbers` | List numbers |
| `PATCH` | `/v1/trunk-portal/numbers/:number_sid` | Change `carrier_sid` and/or `application_sid` |
| `DELETE` | `/v1/trunk-portal/numbers/:number_sid` | Remove a number |

## 4. Applications

**Applications** define the webhook endpoints called when a call arrives or is placed. For most BYOC setups you use the **default Edesy application** that connects calls to the AI voice agent — custom applications are for advanced users who want direct webhook control.

```
POST /v1/trunk-portal/applications
```

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `name` | string | Yes | Application name |
| `call_hook_url` | string | Yes | Webhook URL called when a call needs instructions |
| `call_status_url` | string | No | Webhook URL for call status updates |
| `record_all_calls` | boolean | No | Enable call recording |

```bash
curl -X POST https://voice-api.edesy.in/v1/trunk-portal/applications \
  -H "Authorization: Bearer vp_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "My Voice Agent App",
    "call_hook_url": "https://voice-api.edesy.in/v1/webhooks/voice/call-hook",
    "call_status_url": "https://voice-api.edesy.in/v1/webhooks/voice/call-status",
    "record_all_calls": true
  }'
```

Also available: `GET /v1/trunk-portal/applications`, `GET /v1/trunk-portal/applications/:app_sid`, `PATCH …/:app_sid`, `DELETE …/:app_sid`.

## 5. Call logs

Retrieve call detail records (CDRs) for monitoring and debugging.

```
GET /v1/trunk-portal/call-logs?page=1&count=25
```

| Param | Type | Default | Description |
|-------|------|---------|-------------|
| `page` | integer | `1` | Page number |
| `count` | integer | `25` | Records per page (max 100) |

## Complete setup flow

Adding your own Telnyx SIP trunk end to end:

```bash
# 1. Create the carrier
CARRIER=$(curl -s -X POST https://voice-api.edesy.in/v1/trunk-portal/carriers \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{
    "name": "My Telnyx Trunk",
    "requires_register": true,
    "register_username": "telnyx_user_123",
    "register_password": "telnyx_pass_456",
    "register_sip_realm": "sip.telnyx.com"
  }')
CARRIER_SID=$(echo $CARRIER | jq -r '.data.sid')

# 2. Add the SIP gateway
curl -s -X POST "https://voice-api.edesy.in/v1/trunk-portal/carriers/$CARRIER_SID/gateways" \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"ipv4": "sip.telnyx.com", "port": 5060, "inbound": true, "outbound": true, "protocol": "udp"}'

# 3. Register a phone number (using the default voice-agent application)
curl -s -X POST https://voice-api.edesy.in/v1/trunk-portal/numbers \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d "{\"number\": \"+919876543210\", \"carrier_sid\": \"$CARRIER_SID\"}"
```

Then, on your SIP provider's side (e.g. the Telnyx dashboard), point the trunk to Edesy and use either IP-based auth (allowlist Edesy's address) or SIP registration, as configured with your account.

## Common provider configurations

| Provider | Carrier config | Gateway |
|----------|----------------|---------|
| **Telnyx** | `requires_register: true`, `register_sip_realm: "sip.telnyx.com"` | `sip.telnyx.com:5060`, `udp` |
| **Twilio Elastic SIP** | `e164_leading_plus: true` (IP-authenticated) | `<your-trunk>.pstn.twilio.com:5060`, `udp` |
| **Vonage (Nexmo)** | `requires_register: false` (IP-authenticated) | `sip.nexmo.com:5060`, `udp` |
| **Plivo** | `requires_register: true`, `register_sip_realm: "phone.plivo.com"` | `phone.plivo.com:5060`, `udp` |
| **Generic PBX** | `tech_prefix: "99"` | `203.0.113.50:5060`, `udp` |

## Next

- [Phone Numbers](https://edesy.in/docs/sip-trunk-for-ai-voice-agents/api-reference/phone-numbers) — wire an Edesy-provisioned Indian DID instead of your own
- [Error Codes](https://edesy.in/docs/sip-trunk-for-ai-voice-agents/api-reference/errors)
