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}/gateways | List a trunk's gateways in failover order, the same ordering the dialler uses, including disabled ones |
| POST | /telephony/sip-trunks/{trunkId}/gateways | Add 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}/test | Send 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
dataarray with nometa, unlike the other telephony list endpoints
Path parameters
| Name | Type | Required | Notes |
|---|---|---|---|
trunkId | string | yes |
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
| Status | Body |
|---|---|
200 | Returned in failover order, so a client sees the same ordering the dialler will use rather than insertion order. |
Errors
| Status | When it happens |
|---|---|
404 | No 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
| Name | Type | Required | Notes |
|---|---|---|---|
trunkId | string | yes |
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
| Status | Body |
|---|---|
201 | data-wrapped SipTrunkGateway |
Errors
| Status | When it happens |
|---|---|
404 | No such trunk for this tenant |
422 | Validation 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
| Name | Type | Required | Notes |
|---|---|---|---|
trunkId | string | yes | |
id | string | yes |
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
| Status | Body |
|---|---|
200 | data-wrapped object |
Errors
| Status | When it happens |
|---|---|
404 | No such gateway on this trunk |
422 | Validation 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
| Name | Type | Required | Notes |
|---|---|---|---|
trunkId | string | yes | |
id | string | yes |
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
| Status | Body |
|---|---|
204 | No content |
Errors
| Status | When it happens |
|---|---|
404 | No 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
| Name | Type | Required | Notes |
|---|---|---|---|
trunkId | string | yes | |
id | string | yes |
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
| Status | Body |
|---|---|
200 | data-wrapped object |
Errors
| Status | When it happens |
|---|---|
404 | No 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": {}
}
}