Skip to content

SIP gateways ​

5 endpoints. Every path below is relative to the API base URL and already includes the /api/v1 prefix — see conventions for authentication, pagination, errors and idempotency.

Endpoint
GET/telephony/sip-trunks/{trunkId}/gatewaysList a trunk's gateways in failover order, the same ordering the dialler uses, including disabled ones
POST/telephony/sip-trunks/{trunkId}/gatewaysAdd a gateway to a SIP trunk, encrypting its password on write
PATCH/telephony/sip-trunks/{trunkId}/gateways/{id}Update a gateway's host, transport, priority, weight, credentials or limits, returning the refreshed row
DELETE/telephony/sip-trunks/{trunkId}/gateways/{id}Delete a gateway, scoped to its trunk so an id from another trunk cannot be reached
POST/telephony/sip-trunks/{trunkId}/gateways/{id}/testSend a SIP OPTIONS probe to one gateway and return the raw probe result

Endpoints ​

List a trunk's gateways in failover order, the same ordering the dialler uses, including disabled ones ​

GET/telephony/sip-trunks/{trunkId}/gateways
  • No scope is enforced in the handler — any valid key may call it.
  • Unpaginated: returns a bare data array with no meta, unlike the other telephony list endpoints

Path parameters

NameTypeRequiredNotes
trunkIdstringyes
bash
curl -X GET "$CALLAGENT_API_URL/telephony/sip-trunks/01JQ0000000000000000000000/gateways" \
  -H "Authorization: Bearer $CALLAGENT_API_KEY" \
  -H "Accept: application/json"
js
const response = await fetch(`${process.env.CALLAGENT_API_URL}/telephony/sip-trunks/01JQ0000000000000000000000/gateways`, {
  method: 'GET',
  headers: {
    Authorization: `Bearer ${process.env.CALLAGENT_API_KEY}`,
    Accept: 'application/json',
  },
});

if (!response.ok) {
  const { error } = await response.json();
  throw new Error(`${error.type}: ${error.message}`);
}

const { data } = await response.json();
php
<?php

$handle = curl_init(getenv('CALLAGENT_API_URL') . '/telephony/sip-trunks/01JQ0000000000000000000000/gateways');

curl_setopt_array($handle, [
    CURLOPT_CUSTOMREQUEST => 'GET',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('CALLAGENT_API_KEY'),
        'Accept: application/json',
    ],
]);

$body = json_decode(curl_exec($handle), true);
$status = curl_getinfo($handle, CURLINFO_HTTP_CODE);
curl_close($handle);

if ($status >= 400) {
    throw new RuntimeException("{$body['error']['type']}: {$body['error']['message']}");
}

$data = $body['data'];
python
import os, requests

response = requests.get(
    f"{os.environ['CALLAGENT_API_URL']}/telephony/sip-trunks/01JQ0000000000000000000000/gateways",
    headers={"Authorization": f"Bearer {os.environ['CALLAGENT_API_KEY']}"},
    timeout=30,
)
response.raise_for_status()
data = response.json()["data"]

Responses

StatusBody
200Returned in failover order, so a client sees the same ordering the dialler will use rather than insertion order.

Errors

StatusWhen it happens
404No such trunk for this tenant

Example response, generated from the schema:

json
{
  "data": {}
}

Add a gateway to a SIP trunk, encrypting its password on write ​

POST/telephony/sip-trunks/{trunkId}/gateways
  • No scope is enforced in the handler — any valid key may call it.
  • Request body keys are camelCase (e.g. failoverOnly, maxConcurrentCalls, outboundProxy), not the snake_case the rest of the wire format uses

Path parameters

NameTypeRequiredNotes
trunkIdstringyes
bash
curl -X POST "$CALLAGENT_API_URL/telephony/sip-trunks/01JQ0000000000000000000000/gateways" \
  -H "Authorization: Bearer $CALLAGENT_API_KEY" \
  -H "Accept: application/json"
js
const response = await fetch(`${process.env.CALLAGENT_API_URL}/telephony/sip-trunks/01JQ0000000000000000000000/gateways`, {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.CALLAGENT_API_KEY}`,
    Accept: 'application/json',
  },
});

if (!response.ok) {
  const { error } = await response.json();
  throw new Error(`${error.type}: ${error.message}`);
}

const { data } = await response.json();
php
<?php

$handle = curl_init(getenv('CALLAGENT_API_URL') . '/telephony/sip-trunks/01JQ0000000000000000000000/gateways');

curl_setopt_array($handle, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('CALLAGENT_API_KEY'),
        'Accept: application/json',
    ],
]);

$body = json_decode(curl_exec($handle), true);
$status = curl_getinfo($handle, CURLINFO_HTTP_CODE);
curl_close($handle);

if ($status >= 400) {
    throw new RuntimeException("{$body['error']['type']}: {$body['error']['message']}");
}

$data = $body['data'];
python
import os, requests

response = requests.post(
    f"{os.environ['CALLAGENT_API_URL']}/telephony/sip-trunks/01JQ0000000000000000000000/gateways",
    headers={"Authorization": f"Bearer {os.environ['CALLAGENT_API_KEY']}"},
    timeout=30,
)
response.raise_for_status()
data = response.json()["data"]

Responses

StatusBody
201data-wrapped SipTrunkGateway

Errors

StatusWhen it happens
404No such trunk for this tenant
422Validation failed

Example response, generated from the schema:

json
{
  "data": {
    "id": "01JQ0000000000000000000000",
    "organization_id": "01JQ0000000000000000000000",
    "sip_trunk_id": "01JQ0000000000000000000000",
    "name": "Support Line",
    "host": "string",
    "port": 1,
    "transport": "udp",
    "priority": 1,
    "weight": 1,
    "enabled": true,
    "failover_only": true,
    "max_concurrent_calls": 1,
    "username": "Support Line",
    "realm": "string",
    "outbound_proxy": "string",
    "register": true,
    "status": "unknown",
    "status_checked_at": "2026-01-01T12:00:00+00:00",
    "last_response_code": 1,
    "last_latency_ms": 1,
    "last_error": "string",
    "consecutive_failures": 1,
    "metadata": {},
    "created_at": "2026-01-01T12:00:00+00:00",
    "updated_at": "2026-01-01T12:00:00+00:00"
  }
}

Update a gateway's host, transport, priority, weight, credentials or limits, returning the refreshed row ​

PATCH/telephony/sip-trunks/{trunkId}/gateways/{id}
  • No scope is enforced in the handler — any valid key may call it.
  • Request body keys are camelCase (e.g. failoverOnly, maxConcurrentCalls, outboundProxy), not the snake_case the rest of the wire format uses

Path parameters

NameTypeRequiredNotes
trunkIdstringyes
idstringyes
bash
curl -X PATCH "$CALLAGENT_API_URL/telephony/sip-trunks/01JQ0000000000000000000000/gateways/01JQ0000000000000000000000" \
  -H "Authorization: Bearer $CALLAGENT_API_KEY" \
  -H "Accept: application/json"
js
const response = await fetch(`${process.env.CALLAGENT_API_URL}/telephony/sip-trunks/01JQ0000000000000000000000/gateways/01JQ0000000000000000000000`, {
  method: 'PATCH',
  headers: {
    Authorization: `Bearer ${process.env.CALLAGENT_API_KEY}`,
    Accept: 'application/json',
  },
});

if (!response.ok) {
  const { error } = await response.json();
  throw new Error(`${error.type}: ${error.message}`);
}

const { data } = await response.json();
php
<?php

$handle = curl_init(getenv('CALLAGENT_API_URL') . '/telephony/sip-trunks/01JQ0000000000000000000000/gateways/01JQ0000000000000000000000');

curl_setopt_array($handle, [
    CURLOPT_CUSTOMREQUEST => 'PATCH',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('CALLAGENT_API_KEY'),
        'Accept: application/json',
    ],
]);

$body = json_decode(curl_exec($handle), true);
$status = curl_getinfo($handle, CURLINFO_HTTP_CODE);
curl_close($handle);

if ($status >= 400) {
    throw new RuntimeException("{$body['error']['type']}: {$body['error']['message']}");
}

$data = $body['data'];
python
import os, requests

response = requests.patch(
    f"{os.environ['CALLAGENT_API_URL']}/telephony/sip-trunks/01JQ0000000000000000000000/gateways/01JQ0000000000000000000000",
    headers={"Authorization": f"Bearer {os.environ['CALLAGENT_API_KEY']}"},
    timeout=30,
)
response.raise_for_status()
data = response.json()["data"]

Responses

StatusBody
200data-wrapped object

Errors

StatusWhen it happens
404No such gateway on this trunk
422Validation failed

Example response, generated from the schema:

json
{
  "data": {
    "id": "01JQ0000000000000000000000",
    "organization_id": "01JQ0000000000000000000000",
    "sip_trunk_id": "01JQ0000000000000000000000",
    "name": "Support Line",
    "host": "string",
    "port": 1,
    "transport": "udp",
    "priority": 1,
    "weight": 1,
    "enabled": true,
    "failover_only": true,
    "max_concurrent_calls": 1,
    "username": "Support Line",
    "realm": "string",
    "outbound_proxy": "string",
    "register": true,
    "status": "unknown",
    "status_checked_at": "2026-01-01T12:00:00+00:00",
    "last_response_code": 1,
    "last_latency_ms": 1,
    "last_error": "string",
    "consecutive_failures": 1,
    "metadata": {},
    "created_at": "2026-01-01T12:00:00+00:00",
    "updated_at": "2026-01-01T12:00:00+00:00"
  }
}

Delete a gateway, scoped to its trunk so an id from another trunk cannot be reached ​

DELETE/telephony/sip-trunks/{trunkId}/gateways/{id}
  • No scope is enforced in the handler — any valid key may call it.
  • This response is not data-wrapped.

Path parameters

NameTypeRequiredNotes
trunkIdstringyes
idstringyes
bash
curl -X DELETE "$CALLAGENT_API_URL/telephony/sip-trunks/01JQ0000000000000000000000/gateways/01JQ0000000000000000000000" \
  -H "Authorization: Bearer $CALLAGENT_API_KEY" \
  -H "Accept: application/json"
js
const response = await fetch(`${process.env.CALLAGENT_API_URL}/telephony/sip-trunks/01JQ0000000000000000000000/gateways/01JQ0000000000000000000000`, {
  method: 'DELETE',
  headers: {
    Authorization: `Bearer ${process.env.CALLAGENT_API_KEY}`,
    Accept: 'application/json',
  },
});

if (!response.ok) {
  const { error } = await response.json();
  throw new Error(`${error.type}: ${error.message}`);
}

const { data } = await response.json();
php
<?php

$handle = curl_init(getenv('CALLAGENT_API_URL') . '/telephony/sip-trunks/01JQ0000000000000000000000/gateways/01JQ0000000000000000000000');

curl_setopt_array($handle, [
    CURLOPT_CUSTOMREQUEST => 'DELETE',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('CALLAGENT_API_KEY'),
        'Accept: application/json',
    ],
]);

$body = json_decode(curl_exec($handle), true);
$status = curl_getinfo($handle, CURLINFO_HTTP_CODE);
curl_close($handle);

if ($status >= 400) {
    throw new RuntimeException("{$body['error']['type']}: {$body['error']['message']}");
}

$data = $body['data'];
python
import os, requests

response = requests.delete(
    f"{os.environ['CALLAGENT_API_URL']}/telephony/sip-trunks/01JQ0000000000000000000000/gateways/01JQ0000000000000000000000",
    headers={"Authorization": f"Bearer {os.environ['CALLAGENT_API_KEY']}"},
    timeout=30,
)
response.raise_for_status()
data = response.json()["data"]

Responses

StatusBody
204No content

Errors

StatusWhen it happens
404No such gateway on this trunk

Send a SIP OPTIONS probe to one gateway and return the raw probe result ​

POST/telephony/sip-trunks/{trunkId}/gateways/{id}/test
  • No scope is enforced in the handler — any valid key may call it.
  • Unlike the trunk-level OPTIONS test, this does not persist a diagnostic row or update gateway status

Path parameters

NameTypeRequiredNotes
trunkIdstringyes
idstringyes
bash
curl -X POST "$CALLAGENT_API_URL/telephony/sip-trunks/01JQ0000000000000000000000/gateways/01JQ0000000000000000000000/test" \
  -H "Authorization: Bearer $CALLAGENT_API_KEY" \
  -H "Accept: application/json"
js
const response = await fetch(`${process.env.CALLAGENT_API_URL}/telephony/sip-trunks/01JQ0000000000000000000000/gateways/01JQ0000000000000000000000/test`, {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.CALLAGENT_API_KEY}`,
    Accept: 'application/json',
  },
});

if (!response.ok) {
  const { error } = await response.json();
  throw new Error(`${error.type}: ${error.message}`);
}

const { data } = await response.json();
php
<?php

$handle = curl_init(getenv('CALLAGENT_API_URL') . '/telephony/sip-trunks/01JQ0000000000000000000000/gateways/01JQ0000000000000000000000/test');

curl_setopt_array($handle, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('CALLAGENT_API_KEY'),
        'Accept: application/json',
    ],
]);

$body = json_decode(curl_exec($handle), true);
$status = curl_getinfo($handle, CURLINFO_HTTP_CODE);
curl_close($handle);

if ($status >= 400) {
    throw new RuntimeException("{$body['error']['type']}: {$body['error']['message']}");
}

$data = $body['data'];
python
import os, requests

response = requests.post(
    f"{os.environ['CALLAGENT_API_URL']}/telephony/sip-trunks/01JQ0000000000000000000000/gateways/01JQ0000000000000000000000/test",
    headers={"Authorization": f"Bearer {os.environ['CALLAGENT_API_KEY']}"},
    timeout=30,
)
response.raise_for_status()
data = response.json()["data"]

Responses

StatusBody
200data-wrapped object

Errors

StatusWhen it happens
404No such gateway on this trunk

Example response, generated from the schema:

json
{
  "data": {
    "kind": "string",
    "outcome": "string",
    "response_code": 1,
    "response_reason": "string",
    "latency_ms": 1,
    "target": "string",
    "request_trace": "string",
    "response_trace": "string",
    "message": "string",
    "context": {}
  }
}

Documented against the code. Anything not yet implemented is labelled, never described as working.