SIP trunks

The partner trunk API creates and manages SIP trunks with your normal API key. Use it to connect a PBX or SIP platform to TelVox, to set up a trunk for each of your own customers, or to create a WebRTC agent seat (a wss trunk) for a browser dialer. You can do the same in the dashboard under SIP Trunks. The dashboard offers TCP or WSS; the API also accepts TLS and UDP.

API stabilityConnect is live. The API is v0.x, so field names may change before v1. Base URL: https://connect.telvox.dev/api. Send Accept: application/json on every request.

Access

SIP trunking is switched on per account by the TelVox team. Until it is, every endpoint on this page returns 403. Requests use the same Bearer API key as the rest of the API (see authentication). You only ever see your own trunks: a trunk ID that is not on your account returns 404, the same as one that does not exist (its body is a JSON message). Your account may have a limit on how many trunks it can hold; past it, creating one returns 422.

error responses
{
  "error": "This account is not enabled for SIP trunking. Contact your account manager to have it switched on."
}

Trunk fields

Every endpoint that returns a trunk uses these fields.

FieldTypeDescription
idintegerThe trunk's ID. Use it in the paths below.
statusstringpending, active, suspended or revoked. See Status values.
labelstring | nullYour name for the trunk.
client_refstring | nullYour own reference, for example the ID of the customer this trunk is for.
endpoint_namestringGenerated by TelVox, for example tk-8f2k3m9q1x7b. It is also the SIP username. It cannot be changed.
voice_webhook_urlstring | nullConnect POSTs each call that arrives over the trunk here and runs the XML you return. When set, it takes precedence over cli_lookup_url.
voice_webhook_methodstringPOST (default) or GET.
cli_lookup_urlstring | nullAsked which caller ID to use for a call from the trunk. Reply with JSON {"callerId": "+91…"} or a full XML <Response>. 5 second timeout; on any failure Connect uses default_caller_id.
cli_lookup_methodstringPOST (default) or GET.
default_caller_idstring | nullCaller ID used when there is no lookup URL or the lookup fails.
status_callback_urlstring | nullWhere status callbacks for calls on this trunk are sent.
status_callback_eventsstringSpace-separated list of events to send. Default completed.
status_callback_formatstring | nullRead-only. The shape of this trunk's status callbacks, flat by default. The create response may show null.
hangup_urlstring | nullStored with the trunk. Connect does not send requests to it today.
dual_channel_recordingbooleanRecord calls on this trunk with each side on its own channel. Default false.
allowed_caller_idsstring[] | nullIf set, a caller ID from cli_lookup_url that is not in this list is refused, and Connect falls back to default_caller_id.
created_atstringWhen the trunk was created (ISO 8601).
connectionobjectThe SIP login. Included on create, fetch and update, not in the list. See below.

The connection object

What your PBX or softphone needs to register. It includes the SIP password, so keep it on your server.

FieldTypeDescription
sip_domainstringThe host to register to: your sip_domain if you set one, otherwise the TelVox SIP host.
sip_portstringThe SIP port, for example 5060.
transportstringtcp, tls, udp or wss.
wss_urlstring | nullThe secure WebSocket URL for wss trunks (WebRTC agent seats). null for the others.
usernamestringThe SIP username (the same as endpoint_name).
passwordstring | nullThe SIP password. null if it cannot be read, in which case rotate it from the dashboard.
secret_availablebooleanfalse when the password cannot be read.

Status values

A new trunk starts as pending. TelVox then sets it up on the SIP server and it becomes active. Calls only flow once it is active, so fetch the trunk to check before you hand the login to your customer.

ValueTypeDescription
pendingstringJust created. The login exists, but calls do not flow yet.
activestringSet up on the SIP server. Calls flow.
suspendedstringSwitched off for now by TelVox. Kept so it can be switched back on.
revokedstringGiven up with DELETE. The row and call history are kept.

List trunks

GET/api/partner/trunks

All of your trunks, newest first, in one response. The list leaves out the connection object, so no passwords are returned.

GET/api/partner/trunks
curl https://connect.telvox.dev/api/partner/trunks \
  -H "Authorization: Bearer $TELVOX_API_KEY" \
  -H "Accept: application/json"
200 ok
200 response
{
  "data": [
    {
      "id": 42,
      "status": "active",
      "label": "Acme",
      "client_ref": "acme-001",
      "endpoint_name": "tk-8f2k3m9q1x7b",
      "voice_webhook_url": "https://your.app/voice/inbound",
      "voice_webhook_method": "POST",
      "cli_lookup_url": null,
      "cli_lookup_method": "POST",
      "default_caller_id": "+918045670100",
      "status_callback_url": "https://your.app/voice/status",
      "status_callback_events": "completed",
      "status_callback_format": "flat",
      "hangup_url": null,
      "dual_channel_recording": false,
      "allowed_caller_ids": null,
      "created_at": "2026-06-22T17:04:11.000000Z"
    }
  ]
}
200 ok

Create a trunk

POST/api/partner/trunks

Create a trunk. Every body field is optional. TelVox generates the endpoint name, SIP username and password, and returns them in connection.

ParameterTypeDescription
labelstringYour name for the trunk. Up to 255 characters, no line breaks.
client_refstringYour own reference. Up to 255 characters, no line breaks.
sip_domainstringThe host shown in the connection details, if you want your own (for example a name you point at TelVox).
transportstringtcp (default), tls, udp or wss. Use wss for a WebRTC agent seat. It cannot be changed later.
voice_webhook_urlstringURL that receives calls arriving over the trunk and returns XML.
voice_webhook_methodstringPOST (default) or GET.
cli_lookup_urlstringURL that picks the caller ID for each call from the trunk.
cli_lookup_methodstringPOST (default) or GET.
default_caller_idstringFallback caller ID. Up to 32 characters.
status_callback_urlstringWhere to send status callbacks.
status_callback_eventsstringSpace-separated events, for example "ringing in-progress completed". Default completed.
hangup_urlstringStored with the trunk. Not called today.
dual_channel_recordingbooleanRecord each side on its own channel. Default false.
allowed_caller_idsstring[]Caller IDs the lookup may return. Each up to 32 characters.
POST/api/partner/trunks
curl -X POST https://connect.telvox.dev/api/partner/trunks \
  -H "Authorization: Bearer $TELVOX_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "label": "Acme",
    "client_ref": "acme-001",
    "transport": "tcp",
    "voice_webhook_url": "https://your.app/voice/inbound",
    "status_callback_url": "https://your.app/voice/status",
    "default_caller_id": "+918045670100"
  }'
201 createdpending
201 response
{
  "id": 42,
  "status": "pending",
  "label": "Acme",
  "client_ref": "acme-001",
  "endpoint_name": "tk-8f2k3m9q1x7b",
  "voice_webhook_url": "https://your.app/voice/inbound",
  "voice_webhook_method": "POST",
  "cli_lookup_url": null,
  "cli_lookup_method": "POST",
  "default_caller_id": "+918045670100",
  "status_callback_url": "https://your.app/voice/status",
  "status_callback_events": "completed",
  "status_callback_format": null,
  "hangup_url": null,
  "dual_channel_recording": false,
  "allowed_caller_ids": null,
  "created_at": "2026-06-22T17:04:11.000000Z",
  "connection": {
    "sip_domain": "<TelVox SIP host>",
    "sip_port": "5060",
    "transport": "tcp",
    "wss_url": null,
    "username": "tk-8f2k3m9q1x7b",
    "password": "<SIP password>",
    "secret_available": true
  }
}
201 created

A bad field returns 422 with message and errors.

Fetch a trunk

GET/api/partner/trunks/{id}

One trunk with its connection object, including the SIP password, so you can show the login again in your own portal.

GET/api/partner/trunks/{id}
curl https://connect.telvox.dev/api/partner/trunks/42 \
  -H "Authorization: Bearer $TELVOX_API_KEY" \
  -H "Accept: application/json"
200 ok

Update a trunk

PUT/api/partner/trunks/{id}

Change a trunk's settings. PATCH works the same way. Send only the fields you want to change.

You can change label, client_ref, sip_domain, voice_webhook_url and its method, cli_lookup_url and its method, default_caller_id, status_callback_url, status_callback_events, hangup_url, dual_channel_recording and allowed_caller_ids. The SIP username, password, endpoint name, transport and status cannot be changed here; other fields are ignored. The response is the updated trunk with its connection object.

PUT/api/partner/trunks/{id}
curl -X PUT https://connect.telvox.dev/api/partner/trunks/42 \
  -H "Authorization: Bearer $TELVOX_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "status_callback_events": "ringing in-progress completed",
    "allowed_caller_ids": ["+918045670100", "+918045670101"]
  }'
200 ok

Revoke a trunk

DELETE/api/partner/trunks/{id}

Revoke a trunk. This is a soft delete: the status becomes revoked and the trunk and its call history are kept. There is no API call to undo it.

DELETE/api/partner/trunks/{id}
curl -X DELETE https://connect.telvox.dev/api/partner/trunks/42 \
  -H "Authorization: Bearer $TELVOX_API_KEY" \
  -H "Accept: application/json"
200 okrevoked
200 response
{ "status": "revoked" }
200 ok

WebRTC agent seat login

A WebRTC agent seat is a trunk created with "transport": "wss". A browser dialer signs in to it with a standard SIP-over-WebSocket library such as JsSIP or SIP.js. This endpoint returns everything the dialer needs: the WebSocket URL, the SIP login and fresh TURN servers for strict networks. Call it from your server when an agent signs in and pass the JSON to that agent's browser. It returns 404 for trunks that are not wss.

GET/api/partner/trunks/{id}/connection

The browser-dialer login for a wss trunk: wss_url, sip_domain, username, password, secret_available and ice_servers.

GET/api/partner/trunks/{id}/connection
curl https://connect.telvox.dev/api/partner/trunks/43/connection \
  -H "Authorization: Bearer $TELVOX_API_KEY" \
  -H "Accept: application/json"
200 ok
200 response
{
  "wss_url": "wss://connect.telvox.dev/ws",
  "sip_domain": "<TelVox SIP host>",
  "username": "tk-3n7v1c8w2p5d",
  "password": "<SIP password>",
  "secret_available": true,
  "ice_servers": [
    {
      "urls": [
        "stun:<STUN host>:3478",
        "turn:<TURN host>:3478?transport=udp",
        "turn:<TURN host>:3478?transport=tcp",
        "turns:<TURN host>:5349?transport=tcp"
      ],
      "username": "<TURN username>",
      "credential": "<TURN credential>"
    }
  ]
}
200 ok

ice_servers is ready to pass to the browser as iceServers. The TURN credentials are new on every request and expire, so fetch them each time an agent signs in. If TURN is not available, ice_servers holds only a STUN server.

The seat password reaches the browserThe browser needs the seat's SIP password to register, so it leaves your server. It is a long-lived password, not a short-lived token. Treat it like a secret: only send it to a signed-in agent, keep it out of page source and logs, and give each agent their own seat. If it leaks, rotate it from the dashboard (SIP Trunks), then fetch the new login.