SIP Trunks API

SIP trunks connect your agents to phone networks. Configure inbound trunks to receive calls from your SIP infrastructure, or outbound trunks to route agent calls through a specific carrier.

Base URL: https://kalem.me/api/v1/trunks

The Trunk Object

{
  "id": 1,
  "name": "Main Inbound Trunk",
  "direction": "inbound",
  "auth_type": "ip",
  "sip_uri": "sip:10.0.0.1@kalem.me",
  "host": "10.0.0.1",
  "port": 5060,
  "username": "kalem_trunk",
  "outbound_server": null,
  "registered": false,
  "created_at": "2025-01-10T08:00:00+00:00",
  "updated_at": "2025-01-10T08:00:00+00:00"
}

Note: The trunk password is never included in API responses for security reasons.

List Trunks

GET /api/v1/trunks

Query Parameters

ParameterTypeDescription
directionstringFilter: inbound or outbound
searchstringSearch by trunk name
per_pageintegerResults per page (default: 15, max: 100)

Example Request

curl -X GET "https://kalem.me/api/v1/trunks?direction=inbound" \
  -H "Authorization: Bearer YOUR_API_TOKEN"
const response = await fetch(
  'https://kalem.me/api/v1/trunks?direction=inbound',
  { headers: { 'Authorization': 'Bearer YOUR_API_TOKEN' } }
);
const data = await response.json();
import requests

response = requests.get(
    'https://kalem.me/api/v1/trunks',
    params={'direction': 'inbound'},
    headers={'Authorization': 'Bearer YOUR_API_TOKEN'}
)
data = response.json()
$response = Http::withToken('YOUR_API_TOKEN')
    ->get('https://kalem.me/api/v1/trunks', [
        'direction' => 'inbound',
    ]);
$data = $response->json();

Create Trunk

POST /api/v1/trunks

Request Body Parameters

ParameterTypeRequiredDescription
namestringRequiredDescriptive name for the trunk
directionstringRequiredinbound or outbound
auth_typestringRequiredip (IP-based) or digest (username/password)
hoststringConditionalRequired for inbound IP auth. IP address or hostname of the source PBX
portintegerOptionalSIP port (default: 5060)
usernamestringConditionalRequired for digest auth
passwordstringConditionalRequired for digest auth (min 8 characters)
outbound_serverstringConditionalRequired for outbound trunks. SIP server address for routing calls

Example: Inbound Trunk with IP Auth

curl -X POST https://kalem.me/api/v1/trunks \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Office PBX",
    "direction": "inbound",
    "auth_type": "ip",
    "host": "203.0.113.50",
    "port": 5060
  }'
const response = await fetch('https://kalem.me/api/v1/trunks', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer YOUR_API_TOKEN',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    name: 'Office PBX',
    direction: 'inbound',
    auth_type: 'ip',
    host: '203.0.113.50',
    port: 5060,
  }),
});
const data = await response.json();
import requests

response = requests.post(
    'https://kalem.me/api/v1/trunks',
    json={
        'name': 'Office PBX',
        'direction': 'inbound',
        'auth_type': 'ip',
        'host': '203.0.113.50',
        'port': 5060,
    },
    headers={'Authorization': 'Bearer YOUR_API_TOKEN'}
)
data = response.json()
$response = Http::withToken('YOUR_API_TOKEN')
    ->post('https://kalem.me/api/v1/trunks', [
        'name' => 'Office PBX',
        'direction' => 'inbound',
        'auth_type' => 'ip',
        'host' => '203.0.113.50',
        'port' => 5060,
    ]);
$data = $response->json();

Example: Outbound Trunk with Digest Auth

curl -X POST https://kalem.me/api/v1/trunks \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Carrier Outbound",
    "direction": "outbound",
    "auth_type": "digest",
    "username": "my_sip_user",
    "password": "secureP@ssw0rd",
    "outbound_server": "sip.carrier.com"
  }'

Get Trunk

GET /api/v1/trunks/{id}
curl -X GET https://kalem.me/api/v1/trunks/1 \
  -H "Authorization: Bearer YOUR_API_TOKEN"

Update Trunk

PUT /api/v1/trunks/{id}
curl -X PUT https://kalem.me/api/v1/trunks/1 \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Renamed Trunk",
    "port": 5080
  }'

Delete Trunk

DELETE /api/v1/trunks/{id}
curl -X DELETE https://kalem.me/api/v1/trunks/1 \
  -H "Authorization: Bearer YOUR_API_TOKEN"

Cannot delete: A trunk that has phone numbers assigned to it. Unassign or delete the numbers first.

Error Response (Numbers Assigned)

// HTTP 409 Conflict
{
  "message": "Cannot delete trunk with assigned numbers. Remove numbers first."
}