# Connect your own carrier over SIP — credentials are shown once

Source: https://docs.usetone.ai/reference/sip-trunks/create

> Creates a trunk in `pending_verification`: you can configure and test it, and our team approves it before it carries calls.

`POST /v1/sip-trunks`
Creates a trunk in `pending_verification`: you can configure and test it, and our team approves it before it carries calls. Requires completed business verification AND SIP trunking enabled on your organization (`403 sip_trunking_not_enabled` otherwise — contact support). 🔴 The digest password in `connection.password` appears in this response only. Send an `Idempotency-Key` to make a retried create return the same trunk and the same password.

Requires the `admin` scope.

## Request body

| Field | Type | | Meaning |
|---|---|---|---|
| `callerIdPolicy` | string | optional | Which number an outbound INVITE presents as From: the DID the call was placed from, or whatever your trunk defaults to. Open set — handle an unrecognised value rather than throwing. Today: `did`, `trunk_default`. |
| `codecs` | string[] | optional | Codec preference order the gateway offers, best first. G.711 A-law and mu-law today. |
| `customHeaders` | object | optional | Extra headers on every outbound INVITE. Names must start with X-. Some carriers route or bill on these. |
| `inboundAuth` | string | optional | How the gateway authenticates INVITEs from your PBX. `digest` challenges with the trunk credentials; `ip_acl` admits by source address from ipAcl; `both` requires both. Open set — handle an unrecognised value rather than throwing. Today: `ip_acl`, `digest`, `both`. |
| `ipAcl` | string[] | optional | Public IPv4 addresses or CIDR blocks (/24 or narrower) your SBC sends INVITEs from. Required when inboundAuth is ip_acl or both. Private, loopback and reserved ranges are refused. |
| `maxConcurrent` | integer | optional | Concurrent calls this trunk may carry, inbound and outbound combined. Match your carrier's channel count; beyond it Tone refuses rather than letting the carrier fail the call. |
| `name` | string | required | Your name for the trunk. Shown wherever it appears; no effect on routing. |
| `outboundAuthPassword` | string | optional | The password Tone presents when your carrier challenges. Stored encrypted, never returned. |
| `outboundAuthUsername` | string | optional | The username Tone presents when your carrier challenges outbound INVITEs or REGISTERs. |
| `outboundHost` | string | optional | Where Tone sends outbound INVITEs — your carrier or SBC. A hostname or IPv4 address, no scheme; the port is its own field. Must be publicly reachable. |
| `outboundNumberFormat` | string | optional | How the dialled number is formatted toward your carrier: `e164` (+91…), `national` (10 digits), or `national0` (0 + 10 digits). Open set — handle an unrecognised value rather than throwing. Today: `e164`, `national`, `national0`. |
| `outboundPort` | integer | optional | Port on outboundHost. 5061 for TLS, 5060 otherwise. |
| `outboundTransport` | string | optional | Signalling transport toward your carrier. Leave it on tls unless your carrier cannot terminate it. Open set — handle an unrecognised value rather than throwing. Today: `tls`, `tcp`, `udp`. |
| `register` | boolean | optional | Whether Tone should REGISTER to outboundHost. Some carriers require a registration before they route inbound to you. |

### Example request

```json
{
  "callerIdPolicy": "did",
  "codecs": [
    "PCMA",
    "PCMU"
  ],
  "customHeaders": {
    "X-Account-Code": "acme-sales"
  },
  "inboundAuth": "digest",
  "ipAcl": [
    "49.207.12.0/24",
    "103.21.58.4"
  ],
  "maxConcurrent": 10,
  "name": "Airtel SIP — Mumbai office",
  "outboundAuthPassword": "K7x2QpL9vR4nT1cW",
  "outboundAuthUsername": "acme-out",
  "outboundHost": "sbc.acme-telecom.example",
  "outboundNumberFormat": "e164",
  "outboundPort": 5061,
  "outboundTransport": "tls",
  "register": false
}
```

## Response

| Field | Type | | Meaning |
|---|---|---|---|
| `connection` | object | required | Everything your PBX needs to register or send INVITEs. The password appears in this response only. |
| `connection.host` | string | required | What to point your PBX at. |
| `connection.ips` | string[] | required | The gateway's public addresses, for your firewall. |
| `connection.password` | string | required | Shown exactly once — now. Store it. Rotating issues a new one and invalidates this. |
| `connection.port` | integer | required | Signalling port. |
| `connection.realm` | string | required | The digest realm. |
| `connection.transport` | string | required | Signalling transport. |
| `connection.username` | string | required | The digest username your PBX authenticates with. |
| `trunk` | object | required | The trunk, as it now stands. |
| `trunk.allowOrigination` | boolean | required | Whether this trunk may ORIGINATE: send Tone an INVITE for a number Tone does not host and have Tone dial it out. Granted by Tone support, not settable here. Every originated call runs the full pre-dial compliance gate before anything is dialled. |
| `trunk.approvedAt` | string | null | required | When our team approved the trunk. Null while pending. |
| `trunk.callerIdPolicy` | string | required | Which number outbound INVITEs present as From. Open set — handle an unrecognised value rather than throwing. Today: `did`, `trunk_default`. |
| `trunk.codecs` | string[] | required | Codec preference order, best first. |
| `trunk.connection` | object | required | Where your PBX sends INVITEs. The same for every trunk on this deployment. |
| `trunk.connection.host` | string | required | What to point your PBX or SBC at. |
| `trunk.connection.ips` | string[] | required | The gateway's public addresses, for your firewall allow-list. Signalling and media both originate here. |
| `trunk.connection.port` | integer | required | Signalling port. TLS. |
| `trunk.connection.transport` | string | required | Signalling transport Tone accepts. |
| `trunk.createdAt` | string | required | RFC 3339, UTC. |
| `trunk.customHeaders` | object | required | Extra X-* headers on every outbound INVITE. |
| `trunk.digestUsername` | string | null | required | The username your PBX authenticates with. 🔴 Its password is returned exactly once, at creation and at rotation, and never again. |
| `trunk.disabledReason` | string | null | required | Why the trunk is disabled, when it is. |
| `trunk.hasOutboundPassword` | boolean | required | Whether an outbound password is stored. The value itself is never returned. |
| `trunk.id` | string | required | The trunk's id. |
| `trunk.inboundAuth` | string | required | How the gateway authenticates INVITEs from your PBX. Open set — handle an unrecognised value rather than throwing. Today: `ip_acl`, `digest`, `both`. |
| `trunk.ipAcl` | string[] | required | Public CIDRs your SBC sends from, canonicalised. |
| `trunk.lastInboundAt` | string | null | required | When a call last arrived over this trunk. RFC 3339, UTC. |
| `trunk.lastOutboundAt` | string | null | required | When Tone last placed a call over this trunk. RFC 3339, UTC. |
| `trunk.lastProbe` | object | null | required | The most recent POST /v1/sip-trunks/{id}/test result, or null if never tested. |
| `trunk.maxConcurrent` | integer | required | Concurrent calls this trunk may carry, both directions combined. |
| `trunk.name` | string | required | Your own name for it. |
| `trunk.outboundAuthUsername` | string | null | required | Username Tone presents to your carrier. |
| `trunk.outboundHost` | string | null | required | Where Tone sends outbound INVITEs. |
| `trunk.outboundNumberFormat` | string | required | Dialled-number format toward your carrier. Open set — handle an unrecognised value rather than throwing. Today: `e164`, `national`, `national0`. |
| `trunk.outboundPort` | integer | required | Port on outboundHost. |
| `trunk.outboundTransport` | string | required | Transport toward your carrier. Open set — handle an unrecognised value rather than throwing. Today: `tls`, `tcp`, `udp`. |
| `trunk.realm` | string | required | The digest realm the gateway challenges with. |
| `trunk.register` | boolean | required | Whether Tone REGISTERs to your carrier. |
| `trunk.status` | string | required | Lifecycle. `pending_verification` until our team approves the carrier relationship; only `active` carries calls; `disabled` is the kill switch and `disabledReason` says why. Open set — handle an unrecognised value rather than throwing. Today: `pending_verification`, `active`, `disabled`. |
| `trunk.updatedAt` | string | required | RFC 3339, UTC. |

### Example response

```json
{
  "data": {
    "connection": {
      "host": "sip.usetone.ai",
      "ips": [
        "13.233.10.20"
      ],
      "password": "Qm9vZ2llV29vZ2llMTIzNDU2",
      "port": 5061,
      "realm": "sip.usetone.ai",
      "transport": "tls",
      "username": "t_9f2c4a7b1d3e5f60"
    },
    "trunk": {
      "allowOrigination": false,
      "approvedAt": "2026-09-09T11:00:00Z",
      "callerIdPolicy": "did",
      "codecs": [
        "PCMA",
        "PCMU"
      ],
      "connection": {
        "host": "sip.usetone.ai",
        "ips": [
          "13.233.10.20"
        ],
        "port": 5061,
        "transport": "tls"
      },
      "createdAt": "2026-09-09T10:14:22Z",
      "customHeaders": {},
      "digestUsername": "t_9f2c4a7b1d3e5f60",
      "disabledReason": null,
      "hasOutboundPassword": true,
      "id": "4b7c1a02-9de5-4f31-bb07-2c1a5e9f0d43",
      "inboundAuth": "digest",
      "ipAcl": [
        "49.207.12.0/24"
      ],
      "lastInboundAt": "2026-09-10T08:12:44Z",
      "lastOutboundAt": null,
      "lastProbe": {
        "at": "2026-09-10T08:12:44Z",
        "error": null,
        "latencyMs": 41,
        "method": "options",
        "ok": true,
        "sipStatus": 200
      },
      "maxConcurrent": 10,
      "name": "Airtel SIP — Mumbai office",
      "outboundAuthUsername": "acme-out",
      "outboundHost": "sbc.acme-telecom.example",
      "outboundNumberFormat": "e164",
      "outboundPort": 5061,
      "outboundTransport": "tls",
      "realm": "sip.usetone.ai",
      "register": false,
      "status": "active",
      "updatedAt": "2026-09-10T08:12:44Z"
    }
  }
}
```
